diff --git a/.msggen.json b/.msggen.json index 36a927773fa2..59b706c80658 100644 --- a/.msggen.json +++ b/.msggen.json @@ -548,6 +548,8 @@ "pending": 0 }, "WaitSubsystem": { + "chainmoves": 4, + "channelmoves": 5, "forwards": 1, "htlcs": 3, "invoices": 0, @@ -3715,6 +3717,16 @@ "UtxoPsbt.psbt": 1, "UtxoPsbt.reservations[]": 6 }, + "WaitChainmoves": { + "Wait.chainmoves.account": 1, + "Wait.chainmoves.credit_msat": 2, + "Wait.chainmoves.debit_msat": 3 + }, + "WaitChannelmoves": { + "Wait.channelmoves.account": 1, + "Wait.channelmoves.credit_msat": 2, + "Wait.channelmoves.debit_msat": 3 + }, "WaitDetails": { "Wait.details.bolt11": 4, "Wait.details.bolt12": 5, @@ -3758,6 +3770,8 @@ "Wait.subsystem": 1 }, "WaitResponse": { + "Wait.chainmoves": 10, + "Wait.channelmoves": 11, "Wait.created": 2, "Wait.deleted": 4, "Wait.details": 5, @@ -12892,6 +12906,38 @@ "added": "v23.08", "deprecated": null }, + "Wait.chainmoves": { + "added": "v25.09", + "deprecated": null + }, + "Wait.chainmoves.account": { + "added": "v25.09", + "deprecated": null + }, + "Wait.chainmoves.credit_msat": { + "added": "v25.09", + "deprecated": null + }, + "Wait.chainmoves.debit_msat": { + "added": "v25.09", + "deprecated": null + }, + "Wait.channelmoves": { + "added": "v25.09", + "deprecated": null + }, + "Wait.channelmoves.account": { + "added": "v25.09", + "deprecated": null + }, + "Wait.channelmoves.credit_msat": { + "added": "v25.09", + "deprecated": null + }, + "Wait.channelmoves.debit_msat": { + "added": "v25.09", + "deprecated": null + }, "Wait.created": { "added": "pre-v0.10.1", "deprecated": null diff --git a/cln-grpc/proto/node.proto b/cln-grpc/proto/node.proto index 978ab3a19b8e..ecb275805df4 100644 --- a/cln-grpc/proto/node.proto +++ b/cln-grpc/proto/node.proto @@ -2849,6 +2849,8 @@ message WaitRequest { FORWARDS = 1; SENDPAYS = 2; HTLCS = 3; + CHAINMOVES = 4; + CHANNELMOVES = 5; } // Wait.indexname enum WaitIndexname { @@ -2868,6 +2870,8 @@ message WaitResponse { FORWARDS = 1; SENDPAYS = 2; HTLCS = 3; + CHAINMOVES = 4; + CHANNELMOVES = 5; } WaitSubsystem subsystem = 1; optional uint64 created = 2; @@ -2878,6 +2882,8 @@ message WaitResponse { optional WaitInvoices invoices = 7; optional WaitSendpays sendpays = 8; optional WaitHtlcs htlcs = 9; + optional WaitChainmoves chainmoves = 10; + optional WaitChannelmoves channelmoves = 11; } message WaitForwards { @@ -2960,6 +2966,18 @@ message WaitHtlcs { optional bytes payment_hash = 7; } +message WaitChainmoves { + string account = 1; + Amount credit_msat = 2; + Amount debit_msat = 3; +} + +message WaitChannelmoves { + string account = 1; + Amount credit_msat = 2; + Amount debit_msat = 3; +} + message WaitDetails { // Wait.details.status enum WaitDetailsStatus { diff --git a/cln-grpc/src/convert.rs b/cln-grpc/src/convert.rs index 9a817a5a0fa2..89a9719878a6 100644 --- a/cln-grpc/src/convert.rs +++ b/cln-grpc/src/convert.rs @@ -2686,6 +2686,28 @@ impl From for pb::WaitblockheightResponse { } } +#[allow(unused_variables)] +impl From for pb::WaitChainmoves { + fn from(c: responses::WaitChainmoves) -> Self { + Self { + account: c.account, // Rule #2 for type string + credit_msat: Some(c.credit_msat.into()), // Rule #2 for type msat + debit_msat: Some(c.debit_msat.into()), // Rule #2 for type msat + } + } +} + +#[allow(unused_variables)] +impl From for pb::WaitChannelmoves { + fn from(c: responses::WaitChannelmoves) -> Self { + Self { + account: c.account, // Rule #2 for type string + credit_msat: Some(c.credit_msat.into()), // Rule #2 for type msat + debit_msat: Some(c.debit_msat.into()), // Rule #2 for type msat + } + } +} + #[allow(unused_variables)] impl From for pb::WaitDetails { fn from(c: responses::WaitDetails) -> Self { @@ -2763,6 +2785,8 @@ impl From for pb::WaitSendpays { impl From for pb::WaitResponse { fn from(c: responses::WaitResponse) -> Self { Self { + chainmoves: c.chainmoves.map(|v| v.into()), + channelmoves: c.channelmoves.map(|v| v.into()), created: c.created, // Rule #2 for type u64? deleted: c.deleted, // Rule #2 for type u64? details: c.details.map(|v| v.into()), diff --git a/cln-rpc/src/model.rs b/cln-rpc/src/model.rs index a39fb90f7c83..d2b26876afe7 100644 --- a/cln-rpc/src/model.rs +++ b/cln-rpc/src/model.rs @@ -3802,7 +3802,7 @@ pub mod requests { } } - /// ['The subsystem to get the next index value from.', ' `invoices`: corresponding to `listinvoices` (added in *v23.08*).', ' `sendpays`: corresponding to `listsendpays` (added in *v23.11*).', ' `forwards`: corresponding to `listforwards` (added in *v23.11*).', ' `htlcs`: corresponding to `listhtlcs` (added in *v25.05*).'] + /// ['The subsystem to get the next index value from.', ' `invoices`: corresponding to `listinvoices` (added in *v23.08*).', ' `sendpays`: corresponding to `listsendpays` (added in *v23.11*).', ' `forwards`: corresponding to `listforwards` (added in *v23.11*).', ' `htlcs`: corresponding to `listhtlcs` (added in *v25.05*).', ' `chainmoves`: corresponding to `listchainmoves` (added in *v25.09*).', ' `channelmoves`: corresponding to `listchannelmoves` (added in *v25.09*).'] #[derive(Copy, Clone, Debug, Deserialize, Serialize, PartialEq, Eq)] #[allow(non_camel_case_types)] pub enum WaitSubsystem { @@ -3814,6 +3814,10 @@ pub mod requests { SENDPAYS = 2, #[serde(rename = "htlcs")] HTLCS = 3, + #[serde(rename = "chainmoves")] + CHAINMOVES = 4, + #[serde(rename = "channelmoves")] + CHANNELMOVES = 5, } impl TryFrom for WaitSubsystem { @@ -3824,6 +3828,8 @@ pub mod requests { 1 => Ok(WaitSubsystem::FORWARDS), 2 => Ok(WaitSubsystem::SENDPAYS), 3 => Ok(WaitSubsystem::HTLCS), + 4 => Ok(WaitSubsystem::CHAINMOVES), + 5 => Ok(WaitSubsystem::CHANNELMOVES), o => Err(anyhow::anyhow!("Unknown variant {} for enum WaitSubsystem", o)), } } @@ -3836,6 +3842,8 @@ pub mod requests { WaitSubsystem::FORWARDS => "FORWARDS", WaitSubsystem::SENDPAYS => "SENDPAYS", WaitSubsystem::HTLCS => "HTLCS", + WaitSubsystem::CHAINMOVES => "CHAINMOVES", + WaitSubsystem::CHANNELMOVES => "CHANNELMOVES", }.to_string() } } @@ -9711,6 +9719,20 @@ pub mod responses { pub status: Option, } + #[derive(Clone, Debug, Deserialize, Serialize)] + pub struct WaitChainmoves { + pub account: String, + pub credit_msat: Amount, + pub debit_msat: Amount, + } + + #[derive(Clone, Debug, Deserialize, Serialize)] + pub struct WaitChannelmoves { + pub account: String, + pub credit_msat: Amount, + pub debit_msat: Amount, + } + /// ['Still ongoing, completed, failed locally, or failed after forwarding.'] #[derive(Copy, Clone, Debug, Deserialize, Serialize, PartialEq, Eq)] #[allow(non_camel_case_types)] @@ -10018,6 +10040,10 @@ pub mod responses { SENDPAYS = 2, #[serde(rename = "htlcs")] HTLCS = 3, + #[serde(rename = "chainmoves")] + CHAINMOVES = 4, + #[serde(rename = "channelmoves")] + CHANNELMOVES = 5, } impl TryFrom for WaitSubsystem { @@ -10028,6 +10054,8 @@ pub mod responses { 1 => Ok(WaitSubsystem::FORWARDS), 2 => Ok(WaitSubsystem::SENDPAYS), 3 => Ok(WaitSubsystem::HTLCS), + 4 => Ok(WaitSubsystem::CHAINMOVES), + 5 => Ok(WaitSubsystem::CHANNELMOVES), o => Err(anyhow::anyhow!("Unknown variant {} for enum WaitSubsystem", o)), } } @@ -10040,6 +10068,8 @@ pub mod responses { WaitSubsystem::FORWARDS => "FORWARDS", WaitSubsystem::SENDPAYS => "SENDPAYS", WaitSubsystem::HTLCS => "HTLCS", + WaitSubsystem::CHAINMOVES => "CHAINMOVES", + WaitSubsystem::CHANNELMOVES => "CHANNELMOVES", }.to_string() } } @@ -10050,6 +10080,10 @@ pub mod responses { #[serde(skip_serializing_if = "Option::is_none")] pub details: Option, #[serde(skip_serializing_if = "Option::is_none")] + pub chainmoves: Option, + #[serde(skip_serializing_if = "Option::is_none")] + pub channelmoves: Option, + #[serde(skip_serializing_if = "Option::is_none")] pub created: Option, #[serde(skip_serializing_if = "Option::is_none")] pub deleted: Option, diff --git a/common/coin_mvt.c b/common/coin_mvt.c index cadd7b45ac6d..201683174cb7 100644 --- a/common/coin_mvt.c +++ b/common/coin_mvt.c @@ -35,6 +35,7 @@ static const char *mvt_tags[] = { "splice", "penalty_adj", "journal", + "foreign", }; #define PRIMARY_TAG_BITS ((1ULL << MVT_DEPOSIT) | \ @@ -60,21 +61,110 @@ static const char *mvt_tags[] = { (1ULL << MVT_JOURNAL) | \ (1ULL << MVT_CHANNEL_PROPOSED)) +static enum mvt_tag mvt_tag_in_db(enum mvt_tag mvt_tag) +{ + switch (mvt_tag) { + case MVT_DEPOSIT: + BUILD_ASSERT(MVT_DEPOSIT == 0); + return mvt_tag; + case MVT_WITHDRAWAL: + BUILD_ASSERT(MVT_WITHDRAWAL == 1); + return mvt_tag; + case MVT_PENALTY: + BUILD_ASSERT(MVT_PENALTY == 2); + return mvt_tag; + case MVT_INVOICE: + BUILD_ASSERT(MVT_INVOICE == 3); + return mvt_tag; + case MVT_ROUTED: + BUILD_ASSERT(MVT_ROUTED == 4); + return mvt_tag; + case MVT_PUSHED: + BUILD_ASSERT(MVT_PUSHED == 5); + return mvt_tag; + case MVT_CHANNEL_OPEN: + BUILD_ASSERT(MVT_CHANNEL_OPEN == 6); + return mvt_tag; + case MVT_CHANNEL_CLOSE: + BUILD_ASSERT(MVT_CHANNEL_CLOSE == 7); + return mvt_tag; + case MVT_CHANNEL_TO_US: + BUILD_ASSERT(MVT_CHANNEL_TO_US == 8); + return mvt_tag; + case MVT_HTLC_TIMEOUT: + BUILD_ASSERT(MVT_HTLC_TIMEOUT == 9); + return mvt_tag; + case MVT_HTLC_FULFILL: + BUILD_ASSERT(MVT_HTLC_FULFILL == 10); + return mvt_tag; + case MVT_HTLC_TX: + BUILD_ASSERT(MVT_HTLC_TX == 11); + return mvt_tag; + case MVT_TO_WALLET: + BUILD_ASSERT(MVT_TO_WALLET == 12); + return mvt_tag; + case MVT_ANCHOR: + BUILD_ASSERT(MVT_ANCHOR == 13); + return mvt_tag; + case MVT_TO_THEM: + BUILD_ASSERT(MVT_TO_THEM == 14); + return mvt_tag; + case MVT_PENALIZED: + BUILD_ASSERT(MVT_PENALIZED == 15); + return mvt_tag; + case MVT_STOLEN: + BUILD_ASSERT(MVT_STOLEN == 16); + return mvt_tag; + case MVT_TO_MINER: + BUILD_ASSERT(MVT_TO_MINER == 17); + return mvt_tag; + case MVT_OPENER: + BUILD_ASSERT(MVT_OPENER == 18); + return mvt_tag; + case MVT_LEASE_FEE: + BUILD_ASSERT(MVT_LEASE_FEE == 19); + return mvt_tag; + case MVT_LEASED: + BUILD_ASSERT(MVT_LEASED == 20); + return mvt_tag; + case MVT_STEALABLE: + BUILD_ASSERT(MVT_STEALABLE == 21); + return mvt_tag; + case MVT_CHANNEL_PROPOSED: + BUILD_ASSERT(MVT_CHANNEL_PROPOSED == 22); + return mvt_tag; + case MVT_SPLICE: + BUILD_ASSERT(MVT_SPLICE == 23); + return mvt_tag; + case MVT_PENALTY_ADJ: + BUILD_ASSERT(MVT_PENALTY_ADJ == 24); + return mvt_tag; + case MVT_JOURNAL: + BUILD_ASSERT(MVT_JOURNAL == 25); + return mvt_tag; + case MVT_FOREIGN: + BUILD_ASSERT(MVT_FOREIGN == 26); + return mvt_tag; + } + abort(); +} + const char *mvt_tag_str(enum mvt_tag tag) { + assert((unsigned)tag < NUM_MVT_TAGS); return mvt_tags[tag]; } -static void tag_set(struct mvt_tags *tags, enum mvt_tag tag) +void mvt_tag_set(struct mvt_tags *tags, enum mvt_tag tag) { - u64 bitnum = tag; + u64 bitnum = mvt_tag_in_db(tag); assert(bitnum < NUM_MVT_TAGS); /* Not already set! */ assert((tags->bits & (1ULL << bitnum)) == 0); tags->bits |= (1ULL << bitnum); } -static bool mvt_tags_valid(struct mvt_tags tags) +bool mvt_tags_valid(struct mvt_tags tags) { u64 primaries = (tags.bits & PRIMARY_TAG_BITS); /* Must have exactly one primary. */ @@ -117,21 +207,22 @@ struct mvt_account_id *new_mvt_account_id(const tal_t *ctx, return acct; } -struct channel_coin_mvt *new_channel_coin_mvt(const tal_t *ctx, - const struct channel *channel, - u64 timestamp, - const struct sha256 *payment_hash TAKES, - const u64 *part_id, - const u64 *group_id, - enum coin_mvt_dir direction, - struct amount_msat amount, - struct mvt_tags tags, - struct amount_msat fees) +struct channel_coin_mvt *new_channel_coin_mvt_general(const tal_t *ctx, + const struct channel *channel, + const struct channel_id *cid, + u64 timestamp, + const struct sha256 *payment_hash TAKES, + const u64 *part_id, + const u64 *group_id, + enum coin_mvt_dir direction, + struct amount_msat amount, + struct mvt_tags tags, + struct amount_msat fees) { struct channel_coin_mvt *mvt = tal(ctx, struct channel_coin_mvt); assert(mvt_tags_valid(tags)); - set_mvt_account_id(&mvt->account, channel, NULL); + set_mvt_account_id(&mvt->account, channel, cid ? take(fmt_channel_id(NULL, cid)) : NULL); mvt->timestamp = timestamp; mvt->payment_hash = tal_dup_or_null(mvt, struct sha256, payment_hash); if (!part_id) { @@ -161,6 +252,21 @@ struct channel_coin_mvt *new_channel_coin_mvt(const tal_t *ctx, abort(); } +struct channel_coin_mvt *new_channel_coin_mvt(const tal_t *ctx, + const struct channel *channel, + u64 timestamp, + const struct sha256 *payment_hash TAKES, + const u64 *part_id, + const u64 *group_id, + enum coin_mvt_dir direction, + struct amount_msat amount, + struct mvt_tags tags, + struct amount_msat fees) +{ + return new_channel_coin_mvt_general(ctx, channel, NULL, timestamp, payment_hash, + part_id, group_id, direction, amount, tags, fees); +} + static struct chain_coin_mvt *new_chain_coin_mvt(const tal_t *ctx, const struct channel *channel, const char *account_name TAKES, @@ -304,10 +410,10 @@ struct chain_coin_mvt *new_coin_channel_open_proposed(const tal_t *ctx, /* If we're the opener, add to the tag list */ if (is_opener) - tag_set(&tags, MVT_OPENER); + mvt_tag_set(&tags, MVT_OPENER); if (is_leased) - tag_set(&tags, MVT_LEASED); + mvt_tag_set(&tags, MVT_LEASED); mvt = new_chain_coin_mvt(ctx, channel, NULL, time_now().ts.tv_sec, NULL, out, NULL, 0, @@ -318,27 +424,30 @@ struct chain_coin_mvt *new_coin_channel_open_proposed(const tal_t *ctx, return mvt; } -struct chain_coin_mvt *new_coin_channel_open(const tal_t *ctx, - const struct channel *channel, - const struct bitcoin_outpoint *out, - const struct node_id *peer_id, - u32 blockheight, - const struct amount_msat amount, - const struct amount_sat output_val, - bool is_opener, - bool is_leased) +struct chain_coin_mvt *new_coin_channel_open_general(const tal_t *ctx, + const struct channel *channel, + const struct channel_id *cid, + u64 timestamp, + const struct bitcoin_outpoint *out, + const struct node_id *peer_id, + u32 blockheight, + const struct amount_msat amount, + const struct amount_sat output_val, + bool is_opener, + bool is_leased) { struct chain_coin_mvt *mvt; struct mvt_tags tags = tag_to_mvt_tags(MVT_CHANNEL_OPEN); /* If we're the opener, add to the tag list */ if (is_opener) - tag_set(&tags, MVT_OPENER); + mvt_tag_set(&tags, MVT_OPENER); if (is_leased) - tag_set(&tags, MVT_LEASED); + mvt_tag_set(&tags, MVT_LEASED); - mvt = new_chain_coin_mvt(ctx, channel, NULL, time_now().ts.tv_sec, + mvt = new_chain_coin_mvt(ctx, channel, cid ? take(fmt_channel_id(NULL, cid)) : NULL, + timestamp, NULL, out, NULL, blockheight, tags, COIN_CREDIT, amount, @@ -348,6 +457,22 @@ struct chain_coin_mvt *new_coin_channel_open(const tal_t *ctx, return mvt; } +struct chain_coin_mvt *new_coin_channel_open(const tal_t *ctx, + const struct channel *channel, + const struct bitcoin_outpoint *out, + const struct node_id *peer_id, + u32 blockheight, + const struct amount_msat amount, + const struct amount_sat output_val, + bool is_opener, + bool is_leased) +{ + return new_coin_channel_open_general(ctx, channel, NULL, + time_now().ts.tv_sec, + out, peer_id, blockheight, + amount, output_val, is_opener, is_leased); +} + struct chain_coin_mvt *new_onchain_htlc_deposit(const tal_t *ctx, const struct bitcoin_outpoint *outpoint, u32 blockheight, @@ -432,16 +557,66 @@ struct chain_coin_mvt *new_coin_wallet_withdraw(const tal_t *ctx, COIN_DEBIT, amount); } +struct channel_coin_mvt *new_coin_channel_push_general(const tal_t *ctx, + const struct channel *channel, + const struct channel_id *cid, + u64 timestamp, + enum coin_mvt_dir direction, + struct amount_msat amount, + struct mvt_tags tags) +{ + return new_channel_coin_mvt_general(ctx, channel, cid, timestamp, NULL, + NULL, NULL, direction, amount, + tags, + AMOUNT_MSAT(0)); +} + struct channel_coin_mvt *new_coin_channel_push(const tal_t *ctx, const struct channel *channel, enum coin_mvt_dir direction, struct amount_msat amount, struct mvt_tags tags) { - return new_channel_coin_mvt(ctx, channel, time_now().ts.tv_sec, NULL, - NULL, NULL, direction, amount, - tags, - AMOUNT_MSAT(0)); + return new_coin_channel_push_general(ctx, channel, NULL, + time_now().ts.tv_sec, + direction, amount, tags); +} + +struct chain_coin_mvt *new_foreign_deposit(const tal_t *ctx, + const struct bitcoin_outpoint *outpoint, + u32 blockheight, + struct amount_sat amount, + const char *account, + u64 timestamp) +{ + struct chain_coin_mvt *e; + + e = new_chain_coin_mvt_sat(ctx, NULL, account, NULL, outpoint, NULL, + blockheight, + mk_mvt_tags(MVT_DEPOSIT, MVT_FOREIGN), + COIN_CREDIT, + amount); + e->timestamp = timestamp; + return e; +} + +struct chain_coin_mvt *new_foreign_withdrawal(const tal_t *ctx, + const struct bitcoin_outpoint *outpoint, + const struct bitcoin_txid *spend_txid, + struct amount_sat amount, + u32 blockheight, + const char *account, + u64 timestamp) +{ + struct chain_coin_mvt *e; + + e = new_chain_coin_mvt_sat(ctx, NULL, account, spend_txid, outpoint, NULL, + blockheight, + mk_mvt_tags(MVT_WITHDRAWAL, MVT_FOREIGN), + COIN_DEBIT, + amount); + e->timestamp = timestamp; + return e; } const char **mvt_tag_strs(const tal_t *ctx, struct mvt_tags tags) @@ -557,10 +732,10 @@ struct mvt_tags mk_mvt_tags_(enum mvt_tag tag, ...) va_list ap; struct mvt_tags ret = { 0 }; - tag_set(&ret, tag); + mvt_tag_set(&ret, tag); va_start(ap, tag); while ((tag = va_arg(ap, enum mvt_tag)) != 999) - tag_set(&ret, tag); + mvt_tag_set(&ret, mvt_tag_in_db(tag)); va_end(ap); return ret; } diff --git a/common/coin_mvt.h b/common/coin_mvt.h index 682387a2e933..50d3e2b1fc12 100644 --- a/common/coin_mvt.h +++ b/common/coin_mvt.h @@ -11,6 +11,7 @@ #define ACCOUNT_NAME_WALLET "wallet" #define ACCOUNT_NAME_EXTERNAL "external" +/* /!\ You cannot change this order, it's committed to the db! /!\ */ enum mvt_tag { MVT_DEPOSIT = 0, MVT_WITHDRAWAL = 1, @@ -38,7 +39,8 @@ enum mvt_tag { MVT_SPLICE = 23, MVT_PENALTY_ADJ = 24, MVT_JOURNAL = 25, -#define NUM_MVT_TAGS (MVT_JOURNAL + 1) + MVT_FOREIGN = 26, +#define NUM_MVT_TAGS (MVT_FOREIGN + 1) }; struct mvt_tags { @@ -128,9 +130,15 @@ static inline struct mvt_tags tag_to_mvt_tags(enum mvt_tag tag) return tags; } +/* Add a tag */ +void mvt_tag_set(struct mvt_tags *tags, enum mvt_tag tag); + /* Extract the primary tag */ enum mvt_tag primary_mvt_tag(struct mvt_tags tags); +/* Useful for assertions */ +bool mvt_tags_valid(struct mvt_tags tags); + /* Useful constructor for mvt_account_id: exactly one of channel/account_name must be NULL */ void set_mvt_account_id(struct mvt_account_id *acct_id, const struct channel *channel, @@ -254,6 +262,57 @@ struct channel_coin_mvt *new_coin_channel_push(const tal_t *ctx, struct mvt_tags tags) NON_NULL_ARGS(2); +/* FIXME: Does not set originating_acct, caller must do that! */ +struct chain_coin_mvt *new_foreign_deposit(const tal_t *ctx, + const struct bitcoin_outpoint *outpoint, + u32 blockheight, + struct amount_sat amount, + const char *account, + u64 timestamp) + NON_NULL_ARGS(2, 5); + +struct chain_coin_mvt *new_foreign_withdrawal(const tal_t *ctx, + const struct bitcoin_outpoint *outpoint, + const struct bitcoin_txid *spend_txid, + struct amount_sat amount, + u32 blockheight, + const char *account, + u64 timestamp) + NON_NULL_ARGS(2, 3, 6); + +/* Generic versions (prefer the above ones: these are for migrations) */ +struct channel_coin_mvt *new_channel_coin_mvt_general(const tal_t *ctx, + const struct channel *channel, + const struct channel_id *cid, + u64 timestamp, + const struct sha256 *payment_hash TAKES, + const u64 *part_id, + const u64 *group_id, + enum coin_mvt_dir direction, + struct amount_msat amount, + struct mvt_tags tags, + struct amount_msat fees); + +struct chain_coin_mvt *new_coin_channel_open_general(const tal_t *ctx, + const struct channel *channel, + const struct channel_id *cid, + u64 timestamp, + const struct bitcoin_outpoint *out, + const struct node_id *peer_id, + u32 blockheight, + const struct amount_msat amount, + const struct amount_sat output_val, + bool is_opener, + bool is_leased); + +struct channel_coin_mvt *new_coin_channel_push_general(const tal_t *ctx, + const struct channel *channel, + const struct channel_id *cid, + u64 timestamp, + enum coin_mvt_dir direction, + struct amount_msat amount, + struct mvt_tags tags); + /* There are three standard accounts: * "wallet" for our internal wallet, * "external" for other bitcoin sources, diff --git a/common/json_param.c b/common/json_param.c index 1a761112cc38..454e6a55f34a 100644 --- a/common/json_param.c +++ b/common/json_param.c @@ -883,6 +883,19 @@ struct command_result *param_txid(struct command *cmd, "should be a txid"); } +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"); +} + struct command_result *param_bitcoin_address(struct command *cmd, const char *name, const char *buffer, diff --git a/common/json_param.h b/common/json_param.h index cb06e080a537..5944550f897e 100644 --- a/common/json_param.h +++ b/common/json_param.h @@ -322,6 +322,12 @@ struct command_result *param_txid(struct command *cmd, const jsmntok_t *tok, struct bitcoin_txid **txid); +struct command_result *param_outpoint(struct command *cmd, + const char *name, + const char *buffer, + const jsmntok_t *tok, + struct bitcoin_outpoint **outp); + enum address_parse_result { /* Not recognized as an onchain address */ ADDRESS_PARSE_UNRECOGNIZED, diff --git a/common/test/run-coin_mvt.c b/common/test/run-coin_mvt.c index 6746ad753a7a..6fbac51c2804 100644 --- a/common/test/run-coin_mvt.c +++ b/common/test/run-coin_mvt.c @@ -43,6 +43,9 @@ struct amount_asset amount_sat_to_asset(struct amount_sat *sat UNNEEDED, const u /* Generated stub for amount_tx_fee */ struct amount_sat amount_tx_fee(u32 fee_per_kw UNNEEDED, size_t weight UNNEEDED) { fprintf(stderr, "amount_tx_fee called!\n"); abort(); } +/* Generated stub for fmt_channel_id */ +char *fmt_channel_id(const tal_t *ctx UNNEEDED, const struct channel_id *channel_id UNNEEDED) +{ fprintf(stderr, "fmt_channel_id called!\n"); abort(); } /* Generated stub for fromwire */ const u8 *fromwire(const u8 **cursor UNNEEDED, size_t *max UNNEEDED, void *copy UNNEEDED, size_t n UNNEEDED) { fprintf(stderr, "fromwire called!\n"); abort(); } @@ -181,6 +184,8 @@ static bool mvt_tag_is_primary(enum mvt_tag tag) return true; case MVT_JOURNAL: return true; + case MVT_FOREIGN: + return false; } abort(); } diff --git a/common/test/run-route_blinding_test.c b/common/test/run-route_blinding_test.c index 799c540ef9bb..3358319dfae3 100644 --- a/common/test/run-route_blinding_test.c +++ b/common/test/run-route_blinding_test.c @@ -17,6 +17,9 @@ #include /* AUTOGENERATED MOCKS START */ +/* Generated stub for fmt_channel_id */ +char *fmt_channel_id(const tal_t *ctx UNNEEDED, const struct channel_id *channel_id UNNEEDED) +{ fprintf(stderr, "fmt_channel_id called!\n"); abort(); } /* Generated stub for fromwire_channel_id */ bool fromwire_channel_id(const u8 **cursor UNNEEDED, size_t *max UNNEEDED, struct channel_id *channel_id UNNEEDED) diff --git a/contrib/msggen/msggen/schema.json b/contrib/msggen/msggen/schema.json index 47140c4f08a3..bb7394af8244 100644 --- a/contrib/msggen/msggen/schema.json +++ b/contrib/msggen/msggen/schema.json @@ -16550,6 +16550,355 @@ } ] }, + "listchainmoves.json": { + "$schema": "../rpc-schema-draft.json", + "type": "object", + "rpc": "listchainmoves", + "title": "Command to get the audit list of all onchain coin movements.", + "added": "v25.09", + "description": [ + "The **listchainmoves** command returns the confirmed balance changes onchain over time." + ], + "categories": [ + "readonly" + ], + "request": { + "required": [], + "additionalProperties": false, + "properties": { + "index": { + "type": "string", + "enum": [ + "created" + ], + "description": [ + "How to interpret `start` and `limit`" + ] + }, + "start": { + "type": "u64", + "description": [ + "If `index` is specified, `start` may be specified to start from that value, which is generally returned from lightning-wait(7)." + ] + }, + "limit": { + "type": "u32", + "description": [ + "If `index` is specified, `limit` can be used to specify the maximum number of entries to return." + ] + } + }, + "dependentUpon": { + "index": [ + "start", + "limit" + ] + } + }, + "response": { + "required": [ + "chainmoves" + ], + "additionalProperties": false, + "properties": { + "chainmoves": { + "type": "array", + "items": { + "type": "object", + "additionalProperties": false, + "required": [ + "created_index", + "account_id", + "primary_tag", + "extra_tags", + "credit_msat", + "debit_msat", + "timestamp", + "utxo", + "output_msat", + "blockheight" + ], + "properties": { + "created_index": { + "type": "u64", + "description": [ + "1-based index indicating order this move was created in." + ] + }, + "account_id": { + "type": "string", + "description": [ + "This is either the channel_id corresponding to the channel involved, or a string such as `wallet` for the internal wallet, or `external` for some other external source." + ] + }, + "credit_msat": { + "type": "msat", + "description": [ + "Amount credited (one of this or debit_msat is zero)" + ] + }, + "debit_msat": { + "type": "msat", + "description": [ + "Amount debited (one of this or credit_msat is zero)" + ] + }, + "timestamp": { + "type": "u64", + "description": [ + "Time of this event in seconds since January 1 1970 UTC" + ] + }, + "primary_tag": { + "type": "string", + "enum": [ + "deposit", + "withdrawal", + "penalty", + "channel_open", + "channel_close", + "delayed_to_us", + "htlc_tx", + "htlc_timeout", + "htlc_fulfill", + "to_wallet", + "anchor", + "to_them", + "penalized", + "stolen", + "to_miner" + ], + "description": [ + "A set of one or more tags defining the nature of the change" + ] + }, + "extra_tags": { + "type": "array", + "items": { + "type": "string", + "enum": [ + "opener", + "leased", + "stealable", + "splice", + "foreign" + ] + }, + "description": [ + "A set of additional tags expanding on the details" + ] + }, + "peer_id": { + "type": "pubkey", + "description": [ + "The lightning peer associated with this onchain event" + ] + }, + "originating_account": { + "type": "string", + "description": [ + "This is either a channel_id corresponding to the source channel involved, or a string such as `wallet` for the internal wallet." + ] + }, + "spending_txid": { + "type": "txid", + "description": [ + "The transaction ID which did the spending." + ] + }, + "utxo": { + "type": "outpoint", + "description": [ + "The txid and outpoint number spent for this balance change." + ] + }, + "payment_hash": { + "type": "hash", + "description": [ + "The payment hash associated with this balance change." + ] + }, + "output_msat": { + "type": "msat", + "description": [ + "The output amount (always a whole number of sats). Note that in some cases (e.g. channel opens), not all these belong to us." + ] + }, + "output_count": { + "type": "u32", + "description": [ + "The number of outputs in the `txid` (so you can tell once you've seen events for all of them)." + ] + }, + "blockheight": { + "type": "u32", + "description": [ + "The block number where `txid` appeared (alternately, where `utxo` was spent)." + ] + } + } + } + } + } + }, + "errors": [ + "On failure, one of the following error codes may be returned:", + "", + "- -32602: Error in given parameters." + ], + "resources": [ + "Main web site: " + ] + }, + "listchannelmoves.json": { + "$schema": "../rpc-schema-draft.json", + "type": "object", + "rpc": "listchannelmoves", + "title": "Command to get the audit list of all channel coin movements.", + "added": "v25.09", + "description": [ + "The **listchannelmoves** command returns the confirmed balance changes within lightning channels over time." + ], + "categories": [ + "readonly" + ], + "request": { + "required": [], + "additionalProperties": false, + "properties": { + "index": { + "type": "string", + "enum": [ + "created" + ], + "description": [ + "How to interpret `start` and `limit`" + ] + }, + "start": { + "type": "u64", + "description": [ + "If `index` is specified, `start` may be specified to start from that value, which is generally returned from lightning-wait(7)." + ] + }, + "limit": { + "type": "u32", + "description": [ + "If `index` is specified, `limit` can be used to specify the maximum number of entries to return." + ] + } + }, + "dependentUpon": { + "index": [ + "start", + "limit" + ] + } + }, + "response": { + "required": [ + "channelmoves" + ], + "additionalProperties": false, + "properties": { + "channelmoves": { + "type": "array", + "items": { + "type": "object", + "additionalProperties": false, + "required": [ + "created_index", + "account_id", + "primary_tag", + "credit_msat", + "debit_msat", + "timestamp", + "fees_msat" + ], + "properties": { + "created_index": { + "type": "u64", + "description": [ + "1-based index indicating order this move was created in." + ] + }, + "account_id": { + "type": "string", + "description": [ + "The channel_id corresponding to the channel involved." + ] + }, + "credit_msat": { + "type": "msat", + "description": [ + "Amount credited (one of this or debit_msat is zero)" + ] + }, + "debit_msat": { + "type": "msat", + "description": [ + "Amount debited (one of this or credit_msat is zero)" + ] + }, + "timestamp": { + "type": "u64", + "description": [ + "Time of this event in seconds since January 1 1970 UTC" + ] + }, + "primary_tag": { + "type": "string", + "enum": [ + "invoice", + "routed", + "pushed", + "lease_fee", + "channel_proposed", + "penalty_adj", + "journal" + ], + "description": [ + "A set of one or more tags defining the nature of the change" + ] + }, + "payment_hash": { + "type": "hash", + "description": [ + "The hash associated with this payment (not present for leases or push funding)" + ] + }, + "part_id": { + "type": "u64", + "description": [ + "The part_id for the payment (the `payment_hash`, `group_id`, `part_id` tuple will be unique)" + ] + }, + "group_id": { + "type": "u64", + "description": [ + "The group_id for the payment (the `payment_hash`, `group_id`, `part_id` tuple will be unique)" + ] + }, + "fees_msat": { + "type": "msat", + "description": [ + "The fees paid for this payment" + ] + } + } + } + } + } + }, + "errors": [ + "On failure, one of the following error codes may be returned:", + "", + "- -32602: Error in given parameters." + ], + "resources": [ + "Main web site: " + ] + }, "listchannels.json": { "$schema": "../rpc-schema-draft.json", "type": "object", @@ -32671,7 +33020,7 @@ "* json_group_array" ], "tables": [ - "Note that the first column of every table is a unique integer called `rowid`: this is used for related tables to refer to specific rows in their parent. sqlite3 usually has this as an implicit column, but we make it explicit as the implicit version is not allowed to be used as a foreign key.", + "Note that tables which have a `created_index` field use that as the primary key (and `rowid` is an alias to this), otherwise an explicit `rowid` integer primary key is generated, whose value changes on each refresh. This field is used for related tables to refer to specific rows in their parent. (sqlite3 usually has this as an implicit column, but we make it explicit as the implicit version is not allowed to be used as a foreign key).", "" ], "errors": [ @@ -32682,8 +33031,17 @@ ], "see_also": [ "lightning-listtransactions(7)", + "lightning-listhtlcs(7)", + "lightning-listinvoices(7)", + "lightning-listoffers(7)", "lightning-listchannels(7)", "lightning-listpeers(7)", + "lightning-listpeerchannels(7)", + "lightning-listsendpays(7)", + "lightning-listchainmoves(7)", + "lightning-listchannelmoves(7)", + "lightning-bkpr-listaccountevents(7)", + "lightning-bkpr-listincome(7)", "lightning-listnodes(7)", "lightning-listforwards(7)" ], @@ -33841,13 +34199,17 @@ " `invoices`: corresponding to `listinvoices` (added in *v23.08*).", " `sendpays`: corresponding to `listsendpays` (added in *v23.11*).", " `forwards`: corresponding to `listforwards` (added in *v23.11*).", - " `htlcs`: corresponding to `listhtlcs` (added in *v25.05*)." + " `htlcs`: corresponding to `listhtlcs` (added in *v25.05*).", + " `chainmoves`: corresponding to `listchainmoves` (added in *v25.09*).", + " `channelmoves`: corresponding to `listchannelmoves` (added in *v25.09*)." ], "enum": [ "invoices", "forwards", "sendpays", - "htlcs" + "htlcs", + "chainmoves", + "channelmoves" ] }, "indexname": { @@ -33884,7 +34246,9 @@ "invoices", "forwards", "sendpays", - "htlcs" + "htlcs", + "chainmoves", + "channelmoves" ] }, "created": { @@ -33909,6 +34273,8 @@ "invoices": {}, "sendpays": {}, "htlcs": {}, + "chainmoves": {}, + "channelmoves": {}, "details": {} }, "allOf": [ @@ -34346,6 +34712,118 @@ } } } + }, + { + "if": { + "additionalProperties": true, + "properties": { + "subsystem": { + "type": "string", + "enum": [ + "chainmoves" + ] + } + } + }, + "then": { + "additionalProperties": false, + "properties": { + "subsystem": {}, + "created": {}, + "updated": {}, + "deleted": {}, + "channelmoves": {}, + "chainmoves": { + "added": "v25.09", + "type": "object", + "additionalProperties": false, + "required": [ + "account", + "credit_msat", + "debit_msat" + ], + "properties": { + "account": { + "added": "v25.09", + "type": "string", + "description": [ + "The account (e.g. channel id) associated with this movement." + ] + }, + "credit_msat": { + "added": "v25.09", + "type": "msat", + "description": [ + "The amount incoming" + ] + }, + "debit_msat": { + "added": "v25.09", + "type": "msat", + "description": [ + "The amount outgoing" + ] + } + } + } + } + } + }, + { + "if": { + "additionalProperties": true, + "properties": { + "subsystem": { + "type": "string", + "enum": [ + "channelmoves" + ] + } + } + }, + "then": { + "additionalProperties": false, + "properties": { + "subsystem": {}, + "created": {}, + "updated": {}, + "deleted": {}, + "chainmoves": {}, + "channelmoves": { + "added": "v25.09", + "type": "object", + "additionalProperties": false, + "required": [ + "account", + "credit_msat", + "debit_msat" + ], + "properties": { + "account": { + "added": "v25.09", + "type": "string", + "description": [ + "The account (e.g. channel id) associated with this movement." + ] + }, + "credit_msat": { + "added": "v25.09", + "type": "msat", + "description": [ + "The amount incoming" + ] + }, + "debit_msat": { + "added": "v25.09", + "type": "msat", + "description": [ + "The amount outgoing" + ] + } + } + } + } + } } ] }, @@ -34383,7 +34861,9 @@ "lightning-listinvoices(7)", "lightning-listforwards(7)", "lightning-listsendpays(7)", - "lightning-listhtlcs(7)" + "lightning-listhtlcs(7)", + "lightning-listchainmoves(7)", + "lightning-listchannelmoves(7)" ], "resources": [ "Main web site: " diff --git a/contrib/pyln-grpc-proto/pyln/grpc/node_pb2.py b/contrib/pyln-grpc-proto/pyln/grpc/node_pb2.py index e24125239362..fa2f164195fc 100644 --- a/contrib/pyln-grpc-proto/pyln/grpc/node_pb2.py +++ b/contrib/pyln-grpc-proto/pyln/grpc/node_pb2.py @@ -25,7 +25,7 @@ from pyln.grpc import primitives_pb2 as primitives__pb2 -DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\nnode.proto\x12\x03\x63ln\x1a\x10primitives.proto\"\x10\n\x0eGetinfoRequest\"\xc0\x04\n\x0fGetinfoResponse\x12\n\n\x02id\x18\x01 \x01(\x0c\x12\x12\n\x05\x61lias\x18\x02 \x01(\tH\x00\x88\x01\x01\x12\r\n\x05\x63olor\x18\x03 \x01(\x0c\x12\x11\n\tnum_peers\x18\x04 \x01(\r\x12\x1c\n\x14num_pending_channels\x18\x05 \x01(\r\x12\x1b\n\x13num_active_channels\x18\x06 \x01(\r\x12\x1d\n\x15num_inactive_channels\x18\x07 \x01(\r\x12\x0f\n\x07version\x18\x08 \x01(\t\x12\x15\n\rlightning_dir\x18\t \x01(\t\x12\x32\n\x0cour_features\x18\n \x01(\x0b\x32\x17.cln.GetinfoOurFeaturesH\x01\x88\x01\x01\x12\x13\n\x0b\x62lockheight\x18\x0b \x01(\r\x12\x0f\n\x07network\x18\x0c \x01(\t\x12(\n\x13\x66\x65\x65s_collected_msat\x18\r \x01(\x0b\x32\x0b.cln.Amount\x12$\n\x07\x61\x64\x64ress\x18\x0e \x03(\x0b\x32\x13.cln.GetinfoAddress\x12$\n\x07\x62inding\x18\x0f \x03(\x0b\x32\x13.cln.GetinfoBinding\x12\"\n\x15warning_bitcoind_sync\x18\x10 \x01(\tH\x02\x88\x01\x01\x12$\n\x17warning_lightningd_sync\x18\x11 \x01(\tH\x03\x88\x01\x01\x42\x08\n\x06_aliasB\x0f\n\r_our_featuresB\x18\n\x16_warning_bitcoind_syncB\x1a\n\x18_warning_lightningd_sync\"R\n\x12GetinfoOurFeatures\x12\x0c\n\x04init\x18\x01 \x01(\x0c\x12\x0c\n\x04node\x18\x02 \x01(\x0c\x12\x0f\n\x07\x63hannel\x18\x03 \x01(\x0c\x12\x0f\n\x07invoice\x18\x04 \x01(\x0c\"\xc4\x01\n\x0eGetinfoAddress\x12\x39\n\titem_type\x18\x01 \x01(\x0e\x32&.cln.GetinfoAddress.GetinfoAddressType\x12\x0c\n\x04port\x18\x02 \x01(\r\x12\x14\n\x07\x61\x64\x64ress\x18\x03 \x01(\tH\x00\x88\x01\x01\"G\n\x12GetinfoAddressType\x12\x07\n\x03\x44NS\x10\x00\x12\x08\n\x04IPV4\x10\x01\x12\x08\n\x04IPV6\x10\x02\x12\t\n\x05TORV2\x10\x03\x12\t\n\x05TORV3\x10\x04\x42\n\n\x08_address\"\xac\x02\n\x0eGetinfoBinding\x12\x39\n\titem_type\x18\x01 \x01(\x0e\x32&.cln.GetinfoBinding.GetinfoBindingType\x12\x14\n\x07\x61\x64\x64ress\x18\x02 \x01(\tH\x00\x88\x01\x01\x12\x11\n\x04port\x18\x03 \x01(\rH\x01\x88\x01\x01\x12\x13\n\x06socket\x18\x04 \x01(\tH\x02\x88\x01\x01\x12\x14\n\x07subtype\x18\x05 \x01(\tH\x03\x88\x01\x01\"_\n\x12GetinfoBindingType\x12\x10\n\x0cLOCAL_SOCKET\x10\x00\x12\x08\n\x04IPV4\x10\x01\x12\x08\n\x04IPV6\x10\x02\x12\t\n\x05TORV2\x10\x03\x12\t\n\x05TORV3\x10\x04\x12\r\n\tWEBSOCKET\x10\x05\x42\n\n\x08_addressB\x07\n\x05_portB\t\n\x07_socketB\n\n\x08_subtype\"\xb5\x01\n\x10ListpeersRequest\x12\x0f\n\x02id\x18\x01 \x01(\x0cH\x00\x88\x01\x01\x12\x38\n\x05level\x18\x02 \x01(\x0e\x32$.cln.ListpeersRequest.ListpeersLevelH\x01\x88\x01\x01\"E\n\x0eListpeersLevel\x12\x06\n\x02IO\x10\x00\x12\t\n\x05\x44\x45\x42UG\x10\x01\x12\x08\n\x04INFO\x10\x02\x12\x0b\n\x07UNUSUAL\x10\x03\x12\t\n\x05TRACE\x10\x04\x42\x05\n\x03_idB\x08\n\x06_level\"7\n\x11ListpeersResponse\x12\"\n\x05peers\x18\x01 \x03(\x0b\x32\x13.cln.ListpeersPeers\"\xdf\x01\n\x0eListpeersPeers\x12\n\n\x02id\x18\x01 \x01(\x0c\x12\x11\n\tconnected\x18\x02 \x01(\x08\x12#\n\x03log\x18\x03 \x03(\x0b\x32\x16.cln.ListpeersPeersLog\x12\x0f\n\x07netaddr\x18\x05 \x03(\t\x12\x15\n\x08\x66\x65\x61tures\x18\x06 \x01(\x0cH\x00\x88\x01\x01\x12\x18\n\x0bremote_addr\x18\x07 \x01(\tH\x01\x88\x01\x01\x12\x19\n\x0cnum_channels\x18\x08 \x01(\rH\x02\x88\x01\x01\x42\x0b\n\t_featuresB\x0e\n\x0c_remote_addrB\x0f\n\r_num_channels\"\x88\x03\n\x11ListpeersPeersLog\x12?\n\titem_type\x18\x01 \x01(\x0e\x32,.cln.ListpeersPeersLog.ListpeersPeersLogType\x12\x18\n\x0bnum_skipped\x18\x02 \x01(\rH\x00\x88\x01\x01\x12\x11\n\x04time\x18\x03 \x01(\tH\x01\x88\x01\x01\x12\x13\n\x06source\x18\x04 \x01(\tH\x02\x88\x01\x01\x12\x10\n\x03log\x18\x05 \x01(\tH\x03\x88\x01\x01\x12\x14\n\x07node_id\x18\x06 \x01(\x0cH\x04\x88\x01\x01\x12\x11\n\x04\x64\x61ta\x18\x07 \x01(\x0cH\x05\x88\x01\x01\"t\n\x15ListpeersPeersLogType\x12\x0b\n\x07SKIPPED\x10\x00\x12\n\n\x06\x42ROKEN\x10\x01\x12\x0b\n\x07UNUSUAL\x10\x02\x12\x08\n\x04INFO\x10\x03\x12\t\n\x05\x44\x45\x42UG\x10\x04\x12\t\n\x05IO_IN\x10\x05\x12\n\n\x06IO_OUT\x10\x06\x12\t\n\x05TRACE\x10\x07\x42\x0e\n\x0c_num_skippedB\x07\n\x05_timeB\t\n\x07_sourceB\x06\n\x04_logB\n\n\x08_node_idB\x07\n\x05_data\"0\n\x10ListfundsRequest\x12\x12\n\x05spent\x18\x01 \x01(\x08H\x00\x88\x01\x01\x42\x08\n\x06_spent\"e\n\x11ListfundsResponse\x12&\n\x07outputs\x18\x01 \x03(\x0b\x32\x15.cln.ListfundsOutputs\x12(\n\x08\x63hannels\x18\x02 \x03(\x0b\x32\x16.cln.ListfundsChannels\"\xb9\x03\n\x10ListfundsOutputs\x12\x0c\n\x04txid\x18\x01 \x01(\x0c\x12\x0e\n\x06output\x18\x02 \x01(\r\x12 \n\x0b\x61mount_msat\x18\x03 \x01(\x0b\x32\x0b.cln.Amount\x12\x14\n\x0cscriptpubkey\x18\x04 \x01(\x0c\x12\x14\n\x07\x61\x64\x64ress\x18\x05 \x01(\tH\x00\x88\x01\x01\x12\x19\n\x0credeemscript\x18\x06 \x01(\x0cH\x01\x88\x01\x01\x12<\n\x06status\x18\x07 \x01(\x0e\x32,.cln.ListfundsOutputs.ListfundsOutputsStatus\x12\x18\n\x0b\x62lockheight\x18\x08 \x01(\rH\x02\x88\x01\x01\x12\x10\n\x08reserved\x18\t \x01(\x08\x12\x1e\n\x11reserved_to_block\x18\n \x01(\rH\x03\x88\x01\x01\"Q\n\x16ListfundsOutputsStatus\x12\x0f\n\x0bUNCONFIRMED\x10\x00\x12\r\n\tCONFIRMED\x10\x01\x12\t\n\x05SPENT\x10\x02\x12\x0c\n\x08IMMATURE\x10\x03\x42\n\n\x08_addressB\x0f\n\r_redeemscriptB\x0e\n\x0c_blockheightB\x14\n\x12_reserved_to_block\"\xab\x02\n\x11ListfundsChannels\x12\x0f\n\x07peer_id\x18\x01 \x01(\x0c\x12$\n\x0four_amount_msat\x18\x02 \x01(\x0b\x32\x0b.cln.Amount\x12 \n\x0b\x61mount_msat\x18\x03 \x01(\x0b\x32\x0b.cln.Amount\x12\x14\n\x0c\x66unding_txid\x18\x04 \x01(\x0c\x12\x16\n\x0e\x66unding_output\x18\x05 \x01(\r\x12\x11\n\tconnected\x18\x06 \x01(\x08\x12 \n\x05state\x18\x07 \x01(\x0e\x32\x11.cln.ChannelState\x12\x1d\n\x10short_channel_id\x18\x08 \x01(\tH\x00\x88\x01\x01\x12\x17\n\nchannel_id\x18\t \x01(\x0cH\x01\x88\x01\x01\x42\x13\n\x11_short_channel_idB\r\n\x0b_channel_id\"\xbb\x03\n\x0eSendpayRequest\x12 \n\x05route\x18\x01 \x03(\x0b\x32\x11.cln.SendpayRoute\x12\x14\n\x0cpayment_hash\x18\x02 \x01(\x0c\x12\x12\n\x05label\x18\x03 \x01(\tH\x00\x88\x01\x01\x12\x13\n\x06\x62olt11\x18\x05 \x01(\tH\x01\x88\x01\x01\x12\x1b\n\x0epayment_secret\x18\x06 \x01(\x0cH\x02\x88\x01\x01\x12\x13\n\x06partid\x18\x07 \x01(\x04H\x03\x88\x01\x01\x12\x14\n\x07groupid\x18\t \x01(\x04H\x04\x88\x01\x01\x12%\n\x0b\x61mount_msat\x18\n \x01(\x0b\x32\x0b.cln.AmountH\x05\x88\x01\x01\x12\x1a\n\rlocalinvreqid\x18\x0b \x01(\x0cH\x06\x88\x01\x01\x12\x1d\n\x10payment_metadata\x18\x0c \x01(\x0cH\x07\x88\x01\x01\x12\x18\n\x0b\x64\x65scription\x18\r \x01(\tH\x08\x88\x01\x01\x42\x08\n\x06_labelB\t\n\x07_bolt11B\x11\n\x0f_payment_secretB\t\n\x07_partidB\n\n\x08_groupidB\x0e\n\x0c_amount_msatB\x10\n\x0e_localinvreqidB\x13\n\x11_payment_metadataB\x0e\n\x0c_description\"\xad\x05\n\x0fSendpayResponse\x12\n\n\x02id\x18\x01 \x01(\x04\x12\x14\n\x07groupid\x18\x02 \x01(\x04H\x00\x88\x01\x01\x12\x14\n\x0cpayment_hash\x18\x03 \x01(\x0c\x12\x32\n\x06status\x18\x04 \x01(\x0e\x32\".cln.SendpayResponse.SendpayStatus\x12%\n\x0b\x61mount_msat\x18\x05 \x01(\x0b\x32\x0b.cln.AmountH\x01\x88\x01\x01\x12\x18\n\x0b\x64\x65stination\x18\x06 \x01(\x0cH\x02\x88\x01\x01\x12\x12\n\ncreated_at\x18\x07 \x01(\x04\x12%\n\x10\x61mount_sent_msat\x18\x08 \x01(\x0b\x32\x0b.cln.Amount\x12\x12\n\x05label\x18\t \x01(\tH\x03\x88\x01\x01\x12\x13\n\x06partid\x18\n \x01(\x04H\x04\x88\x01\x01\x12\x13\n\x06\x62olt11\x18\x0b \x01(\tH\x05\x88\x01\x01\x12\x13\n\x06\x62olt12\x18\x0c \x01(\tH\x06\x88\x01\x01\x12\x1d\n\x10payment_preimage\x18\r \x01(\x0cH\x07\x88\x01\x01\x12\x14\n\x07message\x18\x0e \x01(\tH\x08\x88\x01\x01\x12\x19\n\x0c\x63ompleted_at\x18\x0f \x01(\x04H\t\x88\x01\x01\x12\x1a\n\rcreated_index\x18\x10 \x01(\x04H\n\x88\x01\x01\x12\x1a\n\rupdated_index\x18\x11 \x01(\x04H\x0b\x88\x01\x01\"*\n\rSendpayStatus\x12\x0b\n\x07PENDING\x10\x00\x12\x0c\n\x08\x43OMPLETE\x10\x01\x42\n\n\x08_groupidB\x0e\n\x0c_amount_msatB\x0e\n\x0c_destinationB\x08\n\x06_labelB\t\n\x07_partidB\t\n\x07_bolt11B\t\n\x07_bolt12B\x13\n\x11_payment_preimageB\n\n\x08_messageB\x0f\n\r_completed_atB\x10\n\x0e_created_indexB\x10\n\x0e_updated_index\"\\\n\x0cSendpayRoute\x12\n\n\x02id\x18\x02 \x01(\x0c\x12\r\n\x05\x64\x65lay\x18\x03 \x01(\r\x12\x0f\n\x07\x63hannel\x18\x04 \x01(\t\x12 \n\x0b\x61mount_msat\x18\x05 \x01(\x0b\x32\x0b.cln.Amount\"\x93\x01\n\x13ListchannelsRequest\x12\x1d\n\x10short_channel_id\x18\x01 \x01(\tH\x00\x88\x01\x01\x12\x13\n\x06source\x18\x02 \x01(\x0cH\x01\x88\x01\x01\x12\x18\n\x0b\x64\x65stination\x18\x03 \x01(\x0cH\x02\x88\x01\x01\x42\x13\n\x11_short_channel_idB\t\n\x07_sourceB\x0e\n\x0c_destination\"C\n\x14ListchannelsResponse\x12+\n\x08\x63hannels\x18\x01 \x03(\x0b\x32\x19.cln.ListchannelsChannels\"\xb3\x03\n\x14ListchannelsChannels\x12\x0e\n\x06source\x18\x01 \x01(\x0c\x12\x13\n\x0b\x64\x65stination\x18\x02 \x01(\x0c\x12\x18\n\x10short_channel_id\x18\x03 \x01(\t\x12\x0e\n\x06public\x18\x04 \x01(\x08\x12 \n\x0b\x61mount_msat\x18\x05 \x01(\x0b\x32\x0b.cln.Amount\x12\x15\n\rmessage_flags\x18\x06 \x01(\r\x12\x15\n\rchannel_flags\x18\x07 \x01(\r\x12\x0e\n\x06\x61\x63tive\x18\x08 \x01(\x08\x12\x13\n\x0blast_update\x18\t \x01(\r\x12\x1d\n\x15\x62\x61se_fee_millisatoshi\x18\n \x01(\r\x12\x19\n\x11\x66\x65\x65_per_millionth\x18\x0b \x01(\r\x12\r\n\x05\x64\x65lay\x18\x0c \x01(\r\x12&\n\x11htlc_minimum_msat\x18\r \x01(\x0b\x32\x0b.cln.Amount\x12+\n\x11htlc_maximum_msat\x18\x0e \x01(\x0b\x32\x0b.cln.AmountH\x00\x88\x01\x01\x12\x10\n\x08\x66\x65\x61tures\x18\x0f \x01(\x0c\x12\x11\n\tdirection\x18\x10 \x01(\rB\x14\n\x12_htlc_maximum_msat\"#\n\x10\x41\x64\x64gossipRequest\x12\x0f\n\x07message\x18\x01 \x01(\x0c\"\x13\n\x11\x41\x64\x64gossipResponse\"\xac\x01\n\x14\x41\x64\x64psbtoutputRequest\x12\x1c\n\x07satoshi\x18\x01 \x01(\x0b\x32\x0b.cln.Amount\x12\x15\n\x08locktime\x18\x02 \x01(\rH\x00\x88\x01\x01\x12\x18\n\x0binitialpsbt\x18\x03 \x01(\tH\x01\x88\x01\x01\x12\x18\n\x0b\x64\x65stination\x18\x04 \x01(\tH\x02\x88\x01\x01\x42\x0b\n\t_locktimeB\x0e\n\x0c_initialpsbtB\x0e\n\x0c_destination\"U\n\x15\x41\x64\x64psbtoutputResponse\x12\x0c\n\x04psbt\x18\x01 \x01(\t\x12\x1e\n\x16\x65stimated_added_weight\x18\x02 \x01(\r\x12\x0e\n\x06outnum\x18\x03 \x01(\r\"O\n\x14\x41utocleanonceRequest\x12*\n\tsubsystem\x18\x01 \x01(\x0e\x32\x17.cln.AutocleanSubsystem\x12\x0b\n\x03\x61ge\x18\x02 \x01(\x04\"G\n\x15\x41utocleanonceResponse\x12.\n\tautoclean\x18\x01 \x01(\x0b\x32\x1b.cln.AutocleanonceAutoclean\"\xb1\x04\n\x16\x41utocleanonceAutoclean\x12L\n\x11succeededforwards\x18\x01 \x01(\x0b\x32,.cln.AutocleanonceAutocleanSucceededforwardsH\x00\x88\x01\x01\x12\x46\n\x0e\x66\x61iledforwards\x18\x02 \x01(\x0b\x32).cln.AutocleanonceAutocleanFailedforwardsH\x01\x88\x01\x01\x12\x44\n\rsucceededpays\x18\x03 \x01(\x0b\x32(.cln.AutocleanonceAutocleanSucceededpaysH\x02\x88\x01\x01\x12>\n\nfailedpays\x18\x04 \x01(\x0b\x32%.cln.AutocleanonceAutocleanFailedpaysH\x03\x88\x01\x01\x12\x42\n\x0cpaidinvoices\x18\x05 \x01(\x0b\x32\'.cln.AutocleanonceAutocleanPaidinvoicesH\x04\x88\x01\x01\x12H\n\x0f\x65xpiredinvoices\x18\x06 \x01(\x0b\x32*.cln.AutocleanonceAutocleanExpiredinvoicesH\x05\x88\x01\x01\x42\x14\n\x12_succeededforwardsB\x11\n\x0f_failedforwardsB\x10\n\x0e_succeededpaysB\r\n\x0b_failedpaysB\x0f\n\r_paidinvoicesB\x12\n\x10_expiredinvoices\"M\n\'AutocleanonceAutocleanSucceededforwards\x12\x0f\n\x07\x63leaned\x18\x01 \x01(\x04\x12\x11\n\tuncleaned\x18\x02 \x01(\x04\"J\n$AutocleanonceAutocleanFailedforwards\x12\x0f\n\x07\x63leaned\x18\x01 \x01(\x04\x12\x11\n\tuncleaned\x18\x02 \x01(\x04\"I\n#AutocleanonceAutocleanSucceededpays\x12\x0f\n\x07\x63leaned\x18\x01 \x01(\x04\x12\x11\n\tuncleaned\x18\x02 \x01(\x04\"F\n AutocleanonceAutocleanFailedpays\x12\x0f\n\x07\x63leaned\x18\x01 \x01(\x04\x12\x11\n\tuncleaned\x18\x02 \x01(\x04\"H\n\"AutocleanonceAutocleanPaidinvoices\x12\x0f\n\x07\x63leaned\x18\x01 \x01(\x04\x12\x11\n\tuncleaned\x18\x02 \x01(\x04\"K\n%AutocleanonceAutocleanExpiredinvoices\x12\x0f\n\x07\x63leaned\x18\x01 \x01(\x04\x12\x11\n\tuncleaned\x18\x02 \x01(\x04\"W\n\x16\x41utocleanstatusRequest\x12/\n\tsubsystem\x18\x01 \x01(\x0e\x32\x17.cln.AutocleanSubsystemH\x00\x88\x01\x01\x42\x0c\n\n_subsystem\"K\n\x17\x41utocleanstatusResponse\x12\x30\n\tautoclean\x18\x01 \x01(\x0b\x32\x1d.cln.AutocleanstatusAutoclean\"\xbf\x04\n\x18\x41utocleanstatusAutoclean\x12N\n\x11succeededforwards\x18\x01 \x01(\x0b\x32..cln.AutocleanstatusAutocleanSucceededforwardsH\x00\x88\x01\x01\x12H\n\x0e\x66\x61iledforwards\x18\x02 \x01(\x0b\x32+.cln.AutocleanstatusAutocleanFailedforwardsH\x01\x88\x01\x01\x12\x46\n\rsucceededpays\x18\x03 \x01(\x0b\x32*.cln.AutocleanstatusAutocleanSucceededpaysH\x02\x88\x01\x01\x12@\n\nfailedpays\x18\x04 \x01(\x0b\x32\'.cln.AutocleanstatusAutocleanFailedpaysH\x03\x88\x01\x01\x12\x44\n\x0cpaidinvoices\x18\x05 \x01(\x0b\x32).cln.AutocleanstatusAutocleanPaidinvoicesH\x04\x88\x01\x01\x12J\n\x0f\x65xpiredinvoices\x18\x06 \x01(\x0b\x32,.cln.AutocleanstatusAutocleanExpiredinvoicesH\x05\x88\x01\x01\x42\x14\n\x12_succeededforwardsB\x11\n\x0f_failedforwardsB\x10\n\x0e_succeededpaysB\r\n\x0b_failedpaysB\x0f\n\r_paidinvoicesB\x12\n\x10_expiredinvoices\"g\n)AutocleanstatusAutocleanSucceededforwards\x12\x0f\n\x07\x65nabled\x18\x01 \x01(\x08\x12\x0f\n\x07\x63leaned\x18\x02 \x01(\x04\x12\x10\n\x03\x61ge\x18\x03 \x01(\x04H\x00\x88\x01\x01\x42\x06\n\x04_age\"d\n&AutocleanstatusAutocleanFailedforwards\x12\x0f\n\x07\x65nabled\x18\x01 \x01(\x08\x12\x0f\n\x07\x63leaned\x18\x02 \x01(\x04\x12\x10\n\x03\x61ge\x18\x03 \x01(\x04H\x00\x88\x01\x01\x42\x06\n\x04_age\"c\n%AutocleanstatusAutocleanSucceededpays\x12\x0f\n\x07\x65nabled\x18\x01 \x01(\x08\x12\x0f\n\x07\x63leaned\x18\x02 \x01(\x04\x12\x10\n\x03\x61ge\x18\x03 \x01(\x04H\x00\x88\x01\x01\x42\x06\n\x04_age\"`\n\"AutocleanstatusAutocleanFailedpays\x12\x0f\n\x07\x65nabled\x18\x01 \x01(\x08\x12\x0f\n\x07\x63leaned\x18\x02 \x01(\x04\x12\x10\n\x03\x61ge\x18\x03 \x01(\x04H\x00\x88\x01\x01\x42\x06\n\x04_age\"b\n$AutocleanstatusAutocleanPaidinvoices\x12\x0f\n\x07\x65nabled\x18\x01 \x01(\x08\x12\x0f\n\x07\x63leaned\x18\x02 \x01(\x04\x12\x10\n\x03\x61ge\x18\x03 \x01(\x04H\x00\x88\x01\x01\x42\x06\n\x04_age\"e\n\'AutocleanstatusAutocleanExpiredinvoices\x12\x0f\n\x07\x65nabled\x18\x01 \x01(\x08\x12\x0f\n\x07\x63leaned\x18\x02 \x01(\x04\x12\x10\n\x03\x61ge\x18\x03 \x01(\x04H\x00\x88\x01\x01\x42\x06\n\x04_age\"U\n\x13\x43heckmessageRequest\x12\x0f\n\x07message\x18\x01 \x01(\t\x12\r\n\x05zbase\x18\x02 \x01(\t\x12\x13\n\x06pubkey\x18\x03 \x01(\x0cH\x00\x88\x01\x01\x42\t\n\x07_pubkey\"8\n\x14\x43heckmessageResponse\x12\x10\n\x08verified\x18\x01 \x01(\x08\x12\x0e\n\x06pubkey\x18\x02 \x01(\x0c\"\xcb\x02\n\x0c\x43loseRequest\x12\n\n\x02id\x18\x01 \x01(\t\x12\x1e\n\x11unilateraltimeout\x18\x02 \x01(\rH\x00\x88\x01\x01\x12\x18\n\x0b\x64\x65stination\x18\x03 \x01(\tH\x01\x88\x01\x01\x12!\n\x14\x66\x65\x65_negotiation_step\x18\x04 \x01(\tH\x02\x88\x01\x01\x12)\n\rwrong_funding\x18\x05 \x01(\x0b\x32\r.cln.OutpointH\x03\x88\x01\x01\x12\x1f\n\x12\x66orce_lease_closed\x18\x06 \x01(\x08H\x04\x88\x01\x01\x12\x1e\n\x08\x66\x65\x65range\x18\x07 \x03(\x0b\x32\x0c.cln.FeerateB\x14\n\x12_unilateraltimeoutB\x0e\n\x0c_destinationB\x17\n\x15_fee_negotiation_stepB\x10\n\x0e_wrong_fundingB\x15\n\x13_force_lease_closed\"\xc7\x01\n\rCloseResponse\x12/\n\titem_type\x18\x01 \x01(\x0e\x32\x1c.cln.CloseResponse.CloseType\x12\x0f\n\x02tx\x18\x02 \x01(\x0cH\x00\x88\x01\x01\x12\x11\n\x04txid\x18\x03 \x01(\x0cH\x01\x88\x01\x01\x12\x0b\n\x03txs\x18\x04 \x03(\x0c\x12\r\n\x05txids\x18\x05 \x03(\x0c\"5\n\tCloseType\x12\n\n\x06MUTUAL\x10\x00\x12\x0e\n\nUNILATERAL\x10\x01\x12\x0c\n\x08UNOPENED\x10\x02\x42\x05\n\x03_txB\x07\n\x05_txid\"T\n\x0e\x43onnectRequest\x12\n\n\x02id\x18\x01 \x01(\t\x12\x11\n\x04host\x18\x02 \x01(\tH\x00\x88\x01\x01\x12\x11\n\x04port\x18\x03 \x01(\rH\x01\x88\x01\x01\x42\x07\n\x05_hostB\x07\n\x05_port\"\xb4\x01\n\x0f\x43onnectResponse\x12\n\n\x02id\x18\x01 \x01(\x0c\x12\x10\n\x08\x66\x65\x61tures\x18\x02 \x01(\x0c\x12\x38\n\tdirection\x18\x03 \x01(\x0e\x32%.cln.ConnectResponse.ConnectDirection\x12$\n\x07\x61\x64\x64ress\x18\x04 \x01(\x0b\x32\x13.cln.ConnectAddress\"#\n\x10\x43onnectDirection\x12\x06\n\x02IN\x10\x00\x12\x07\n\x03OUT\x10\x01\"\xfb\x01\n\x0e\x43onnectAddress\x12\x39\n\titem_type\x18\x01 \x01(\x0e\x32&.cln.ConnectAddress.ConnectAddressType\x12\x13\n\x06socket\x18\x02 \x01(\tH\x00\x88\x01\x01\x12\x14\n\x07\x61\x64\x64ress\x18\x03 \x01(\tH\x01\x88\x01\x01\x12\x11\n\x04port\x18\x04 \x01(\rH\x02\x88\x01\x01\"P\n\x12\x43onnectAddressType\x12\x10\n\x0cLOCAL_SOCKET\x10\x00\x12\x08\n\x04IPV4\x10\x01\x12\x08\n\x04IPV6\x10\x02\x12\t\n\x05TORV2\x10\x03\x12\t\n\x05TORV3\x10\x04\x42\t\n\x07_socketB\n\n\x08_addressB\x07\n\x05_port\"J\n\x14\x43reateinvoiceRequest\x12\x11\n\tinvstring\x18\x01 \x01(\t\x12\r\n\x05label\x18\x02 \x01(\t\x12\x10\n\x08preimage\x18\x03 \x01(\x0c\"\xfd\x05\n\x15\x43reateinvoiceResponse\x12\r\n\x05label\x18\x01 \x01(\t\x12\x13\n\x06\x62olt11\x18\x02 \x01(\tH\x00\x88\x01\x01\x12\x13\n\x06\x62olt12\x18\x03 \x01(\tH\x01\x88\x01\x01\x12\x14\n\x0cpayment_hash\x18\x04 \x01(\x0c\x12%\n\x0b\x61mount_msat\x18\x05 \x01(\x0b\x32\x0b.cln.AmountH\x02\x88\x01\x01\x12>\n\x06status\x18\x06 \x01(\x0e\x32..cln.CreateinvoiceResponse.CreateinvoiceStatus\x12\x13\n\x0b\x64\x65scription\x18\x07 \x01(\t\x12\x12\n\nexpires_at\x18\x08 \x01(\x04\x12\x16\n\tpay_index\x18\t \x01(\x04H\x03\x88\x01\x01\x12.\n\x14\x61mount_received_msat\x18\n \x01(\x0b\x32\x0b.cln.AmountH\x04\x88\x01\x01\x12\x14\n\x07paid_at\x18\x0b \x01(\x04H\x05\x88\x01\x01\x12\x1d\n\x10payment_preimage\x18\x0c \x01(\x0cH\x06\x88\x01\x01\x12\x1b\n\x0elocal_offer_id\x18\r \x01(\x0cH\x07\x88\x01\x01\x12\x1e\n\x11invreq_payer_note\x18\x0f \x01(\tH\x08\x88\x01\x01\x12\x1a\n\rcreated_index\x18\x10 \x01(\x04H\t\x88\x01\x01\x12:\n\rpaid_outpoint\x18\x11 \x01(\x0b\x32\x1e.cln.CreateinvoicePaidOutpointH\n\x88\x01\x01\"8\n\x13\x43reateinvoiceStatus\x12\x08\n\x04PAID\x10\x00\x12\x0b\n\x07\x45XPIRED\x10\x01\x12\n\n\x06UNPAID\x10\x02\x42\t\n\x07_bolt11B\t\n\x07_bolt12B\x0e\n\x0c_amount_msatB\x0c\n\n_pay_indexB\x17\n\x15_amount_received_msatB\n\n\x08_paid_atB\x13\n\x11_payment_preimageB\x11\n\x0f_local_offer_idB\x14\n\x12_invreq_payer_noteB\x10\n\x0e_created_indexB\x10\n\x0e_paid_outpoint\"9\n\x19\x43reateinvoicePaidOutpoint\x12\x0c\n\x04txid\x18\x01 \x01(\x0c\x12\x0e\n\x06outnum\x18\x02 \x01(\r\"\xb4\x02\n\x10\x44\x61tastoreRequest\x12\x10\n\x03hex\x18\x02 \x01(\x0cH\x00\x88\x01\x01\x12\x36\n\x04mode\x18\x03 \x01(\x0e\x32#.cln.DatastoreRequest.DatastoreModeH\x01\x88\x01\x01\x12\x17\n\ngeneration\x18\x04 \x01(\x04H\x02\x88\x01\x01\x12\x0b\n\x03key\x18\x05 \x03(\t\x12\x13\n\x06string\x18\x06 \x01(\tH\x03\x88\x01\x01\"p\n\rDatastoreMode\x12\x0f\n\x0bMUST_CREATE\x10\x00\x12\x10\n\x0cMUST_REPLACE\x10\x01\x12\x15\n\x11\x43REATE_OR_REPLACE\x10\x02\x12\x0f\n\x0bMUST_APPEND\x10\x03\x12\x14\n\x10\x43REATE_OR_APPEND\x10\x04\x42\x06\n\x04_hexB\x07\n\x05_modeB\r\n\x0b_generationB\t\n\x07_string\"\x82\x01\n\x11\x44\x61tastoreResponse\x12\x17\n\ngeneration\x18\x02 \x01(\x04H\x00\x88\x01\x01\x12\x10\n\x03hex\x18\x03 \x01(\x0cH\x01\x88\x01\x01\x12\x13\n\x06string\x18\x04 \x01(\tH\x02\x88\x01\x01\x12\x0b\n\x03key\x18\x05 \x03(\tB\r\n\x0b_generationB\x06\n\x04_hexB\t\n\x07_string\"$\n\x15\x44\x61tastoreusageRequest\x12\x0b\n\x03key\x18\x01 \x03(\t\"S\n\x16\x44\x61tastoreusageResponse\x12\x39\n\x0e\x64\x61tastoreusage\x18\x01 \x01(\x0b\x32!.cln.DatastoreusageDatastoreusage\"@\n\x1c\x44\x61tastoreusageDatastoreusage\x12\x0b\n\x03key\x18\x01 \x01(\t\x12\x13\n\x0btotal_bytes\x18\x02 \x01(\x04\"\x9d\x01\n\x12\x43reateonionRequest\x12\"\n\x04hops\x18\x01 \x03(\x0b\x32\x14.cln.CreateonionHops\x12\x11\n\tassocdata\x18\x02 \x01(\x0c\x12\x18\n\x0bsession_key\x18\x03 \x01(\x0cH\x00\x88\x01\x01\x12\x17\n\nonion_size\x18\x04 \x01(\rH\x01\x88\x01\x01\x42\x0e\n\x0c_session_keyB\r\n\x0b_onion_size\"<\n\x13\x43reateonionResponse\x12\r\n\x05onion\x18\x01 \x01(\x0c\x12\x16\n\x0eshared_secrets\x18\x02 \x03(\x0c\"2\n\x0f\x43reateonionHops\x12\x0e\n\x06pubkey\x18\x01 \x01(\x0c\x12\x0f\n\x07payload\x18\x02 \x01(\x0c\"J\n\x13\x44\x65ldatastoreRequest\x12\x17\n\ngeneration\x18\x02 \x01(\x04H\x00\x88\x01\x01\x12\x0b\n\x03key\x18\x03 \x03(\tB\r\n\x0b_generation\"\x85\x01\n\x14\x44\x65ldatastoreResponse\x12\x17\n\ngeneration\x18\x02 \x01(\x04H\x00\x88\x01\x01\x12\x10\n\x03hex\x18\x03 \x01(\x0cH\x01\x88\x01\x01\x12\x13\n\x06string\x18\x04 \x01(\tH\x02\x88\x01\x01\x12\x0b\n\x03key\x18\x05 \x03(\tB\r\n\x0b_generationB\x06\n\x04_hexB\t\n\x07_string\"\xb6\x01\n\x11\x44\x65linvoiceRequest\x12\r\n\x05label\x18\x01 \x01(\t\x12\x37\n\x06status\x18\x02 \x01(\x0e\x32\'.cln.DelinvoiceRequest.DelinvoiceStatus\x12\x15\n\x08\x64\x65sconly\x18\x03 \x01(\x08H\x00\x88\x01\x01\"5\n\x10\x44\x65linvoiceStatus\x12\x08\n\x04PAID\x10\x00\x12\x0b\n\x07\x45XPIRED\x10\x01\x12\n\n\x06UNPAID\x10\x02\x42\x0b\n\t_desconly\"\xe6\x05\n\x12\x44\x65linvoiceResponse\x12\r\n\x05label\x18\x01 \x01(\t\x12\x13\n\x06\x62olt11\x18\x02 \x01(\tH\x00\x88\x01\x01\x12\x13\n\x06\x62olt12\x18\x03 \x01(\tH\x01\x88\x01\x01\x12%\n\x0b\x61mount_msat\x18\x04 \x01(\x0b\x32\x0b.cln.AmountH\x02\x88\x01\x01\x12\x18\n\x0b\x64\x65scription\x18\x05 \x01(\tH\x03\x88\x01\x01\x12\x14\n\x0cpayment_hash\x18\x06 \x01(\x0c\x12\x38\n\x06status\x18\x07 \x01(\x0e\x32(.cln.DelinvoiceResponse.DelinvoiceStatus\x12\x12\n\nexpires_at\x18\x08 \x01(\x04\x12\x1b\n\x0elocal_offer_id\x18\t \x01(\x0cH\x04\x88\x01\x01\x12\x1e\n\x11invreq_payer_note\x18\x0b \x01(\tH\x05\x88\x01\x01\x12\x1a\n\rcreated_index\x18\x0c \x01(\x04H\x06\x88\x01\x01\x12\x1a\n\rupdated_index\x18\r \x01(\x04H\x07\x88\x01\x01\x12\x16\n\tpay_index\x18\x0e \x01(\x04H\x08\x88\x01\x01\x12.\n\x14\x61mount_received_msat\x18\x0f \x01(\x0b\x32\x0b.cln.AmountH\t\x88\x01\x01\x12\x14\n\x07paid_at\x18\x10 \x01(\x04H\n\x88\x01\x01\x12\x1d\n\x10payment_preimage\x18\x11 \x01(\x0cH\x0b\x88\x01\x01\"5\n\x10\x44\x65linvoiceStatus\x12\x08\n\x04PAID\x10\x00\x12\x0b\n\x07\x45XPIRED\x10\x01\x12\n\n\x06UNPAID\x10\x02\x42\t\n\x07_bolt11B\t\n\x07_bolt12B\x0e\n\x0c_amount_msatB\x0e\n\x0c_descriptionB\x11\n\x0f_local_offer_idB\x14\n\x12_invreq_payer_noteB\x10\n\x0e_created_indexB\x10\n\x0e_updated_indexB\x0c\n\n_pay_indexB\x17\n\x15_amount_received_msatB\n\n\x08_paid_atB\x13\n\x11_payment_preimage\"\x9f\x01\n\x17\x44\x65vforgetchannelRequest\x12\n\n\x02id\x18\x01 \x01(\x0c\x12\x1d\n\x10short_channel_id\x18\x02 \x01(\tH\x00\x88\x01\x01\x12\x17\n\nchannel_id\x18\x03 \x01(\x0cH\x01\x88\x01\x01\x12\x12\n\x05\x66orce\x18\x04 \x01(\x08H\x02\x88\x01\x01\x42\x13\n\x11_short_channel_idB\r\n\x0b_channel_idB\x08\n\x06_force\"Y\n\x18\x44\x65vforgetchannelResponse\x12\x0e\n\x06\x66orced\x18\x01 \x01(\x08\x12\x17\n\x0f\x66unding_unspent\x18\x02 \x01(\x08\x12\x14\n\x0c\x66unding_txid\x18\x03 \x01(\x0c\"\x19\n\x17\x45mergencyrecoverRequest\")\n\x18\x45mergencyrecoverResponse\x12\r\n\x05stubs\x18\x01 \x03(\x0c\" \n\x1eGetemergencyrecoverdataRequest\"3\n\x1fGetemergencyrecoverdataResponse\x12\x10\n\x08\x66iledata\x18\x01 \x01(\x0c\"Q\n\x13\x45xposesecretRequest\x12\x12\n\npassphrase\x18\x01 \x01(\t\x12\x17\n\nidentifier\x18\x02 \x01(\tH\x00\x88\x01\x01\x42\r\n\x0b_identifier\";\n\x14\x45xposesecretResponse\x12\x12\n\nidentifier\x18\x01 \x01(\t\x12\x0f\n\x07\x63odex32\x18\x02 \x01(\t\"#\n\x0eRecoverRequest\x12\x11\n\thsmsecret\x18\x01 \x01(\t\"\x88\x01\n\x0fRecoverResponse\x12\x37\n\x06result\x18\x01 \x01(\x0e\x32\".cln.RecoverResponse.RecoverResultH\x00\x88\x01\x01\"1\n\rRecoverResult\x12 \n\x1cRECOVERY_RESTART_IN_PROGRESS\x10\x00\x42\t\n\x07_result\"$\n\x15RecoverchannelRequest\x12\x0b\n\x03scb\x18\x01 \x03(\x0c\"\'\n\x16RecoverchannelResponse\x12\r\n\x05stubs\x18\x01 \x03(\t\"\x99\x02\n\x0eInvoiceRequest\x12\x13\n\x0b\x64\x65scription\x18\x02 \x01(\t\x12\r\n\x05label\x18\x03 \x01(\t\x12\x11\n\tfallbacks\x18\x04 \x03(\t\x12\x15\n\x08preimage\x18\x05 \x01(\x0cH\x00\x88\x01\x01\x12\x11\n\x04\x63ltv\x18\x06 \x01(\rH\x01\x88\x01\x01\x12\x13\n\x06\x65xpiry\x18\x07 \x01(\x04H\x02\x88\x01\x01\x12\x1d\n\x15\x65xposeprivatechannels\x18\x08 \x03(\t\x12\x19\n\x0c\x64\x65schashonly\x18\t \x01(\x08H\x03\x88\x01\x01\x12%\n\x0b\x61mount_msat\x18\n \x01(\x0b\x32\x10.cln.AmountOrAnyB\x0b\n\t_preimageB\x07\n\x05_cltvB\t\n\x07_expiryB\x0f\n\r_deschashonly\"\x95\x03\n\x0fInvoiceResponse\x12\x0e\n\x06\x62olt11\x18\x01 \x01(\t\x12\x14\n\x0cpayment_hash\x18\x02 \x01(\x0c\x12\x16\n\x0epayment_secret\x18\x03 \x01(\x0c\x12\x12\n\nexpires_at\x18\x04 \x01(\x04\x12\x1d\n\x10warning_capacity\x18\x05 \x01(\tH\x00\x88\x01\x01\x12\x1c\n\x0fwarning_offline\x18\x06 \x01(\tH\x01\x88\x01\x01\x12\x1d\n\x10warning_deadends\x18\x07 \x01(\tH\x02\x88\x01\x01\x12#\n\x16warning_private_unused\x18\x08 \x01(\tH\x03\x88\x01\x01\x12\x18\n\x0bwarning_mpp\x18\t \x01(\tH\x04\x88\x01\x01\x12\x1a\n\rcreated_index\x18\n \x01(\x04H\x05\x88\x01\x01\x42\x13\n\x11_warning_capacityB\x12\n\x10_warning_offlineB\x13\n\x11_warning_deadendsB\x19\n\x17_warning_private_unusedB\x0e\n\x0c_warning_mppB\x10\n\x0e_created_index\"\xe1\x01\n\x15InvoicerequestRequest\x12\x1b\n\x06\x61mount\x18\x01 \x01(\x0b\x32\x0b.cln.Amount\x12\x13\n\x0b\x64\x65scription\x18\x02 \x01(\t\x12\x13\n\x06issuer\x18\x03 \x01(\tH\x00\x88\x01\x01\x12\x12\n\x05label\x18\x04 \x01(\tH\x01\x88\x01\x01\x12\x1c\n\x0f\x61\x62solute_expiry\x18\x05 \x01(\x04H\x02\x88\x01\x01\x12\x17\n\nsingle_use\x18\x06 \x01(\x08H\x03\x88\x01\x01\x42\t\n\x07_issuerB\x08\n\x06_labelB\x12\n\x10_absolute_expiryB\r\n\x0b_single_use\"\x8b\x01\n\x16InvoicerequestResponse\x12\x11\n\tinvreq_id\x18\x01 \x01(\x0c\x12\x0e\n\x06\x61\x63tive\x18\x02 \x01(\x08\x12\x12\n\nsingle_use\x18\x03 \x01(\x08\x12\x0e\n\x06\x62olt12\x18\x04 \x01(\t\x12\x0c\n\x04used\x18\x05 \x01(\x08\x12\x12\n\x05label\x18\x06 \x01(\tH\x00\x88\x01\x01\x42\x08\n\x06_label\"1\n\x1c\x44isableinvoicerequestRequest\x12\x11\n\tinvreq_id\x18\x01 \x01(\t\"\x92\x01\n\x1d\x44isableinvoicerequestResponse\x12\x11\n\tinvreq_id\x18\x01 \x01(\x0c\x12\x0e\n\x06\x61\x63tive\x18\x02 \x01(\x08\x12\x12\n\nsingle_use\x18\x03 \x01(\x08\x12\x0e\n\x06\x62olt12\x18\x04 \x01(\t\x12\x0c\n\x04used\x18\x05 \x01(\x08\x12\x12\n\x05label\x18\x06 \x01(\tH\x00\x88\x01\x01\x42\x08\n\x06_label\"l\n\x1aListinvoicerequestsRequest\x12\x16\n\tinvreq_id\x18\x01 \x01(\tH\x00\x88\x01\x01\x12\x18\n\x0b\x61\x63tive_only\x18\x02 \x01(\x08H\x01\x88\x01\x01\x42\x0c\n\n_invreq_idB\x0e\n\x0c_active_only\"_\n\x1bListinvoicerequestsResponse\x12@\n\x0finvoicerequests\x18\x01 \x03(\x0b\x32\'.cln.ListinvoicerequestsInvoicerequests\"\x97\x01\n\"ListinvoicerequestsInvoicerequests\x12\x11\n\tinvreq_id\x18\x01 \x01(\x0c\x12\x0e\n\x06\x61\x63tive\x18\x02 \x01(\x08\x12\x12\n\nsingle_use\x18\x03 \x01(\x08\x12\x0e\n\x06\x62olt12\x18\x04 \x01(\t\x12\x0c\n\x04used\x18\x05 \x01(\x08\x12\x12\n\x05label\x18\x06 \x01(\tH\x00\x88\x01\x01\x42\x08\n\x06_label\"#\n\x14ListdatastoreRequest\x12\x0b\n\x03key\x18\x02 \x03(\t\"G\n\x15ListdatastoreResponse\x12.\n\tdatastore\x18\x01 \x03(\x0b\x32\x1b.cln.ListdatastoreDatastore\"\x87\x01\n\x16ListdatastoreDatastore\x12\x0b\n\x03key\x18\x01 \x03(\t\x12\x17\n\ngeneration\x18\x02 \x01(\x04H\x00\x88\x01\x01\x12\x10\n\x03hex\x18\x03 \x01(\x0cH\x01\x88\x01\x01\x12\x13\n\x06string\x18\x04 \x01(\tH\x02\x88\x01\x01\x42\r\n\x0b_generationB\x06\n\x04_hexB\t\n\x07_string\"\xde\x02\n\x13ListinvoicesRequest\x12\x12\n\x05label\x18\x01 \x01(\tH\x00\x88\x01\x01\x12\x16\n\tinvstring\x18\x02 \x01(\tH\x01\x88\x01\x01\x12\x19\n\x0cpayment_hash\x18\x03 \x01(\x0cH\x02\x88\x01\x01\x12\x15\n\x08offer_id\x18\x04 \x01(\tH\x03\x88\x01\x01\x12>\n\x05index\x18\x05 \x01(\x0e\x32*.cln.ListinvoicesRequest.ListinvoicesIndexH\x04\x88\x01\x01\x12\x12\n\x05start\x18\x06 \x01(\x04H\x05\x88\x01\x01\x12\x12\n\x05limit\x18\x07 \x01(\rH\x06\x88\x01\x01\"-\n\x11ListinvoicesIndex\x12\x0b\n\x07\x43REATED\x10\x00\x12\x0b\n\x07UPDATED\x10\x01\x42\x08\n\x06_labelB\x0c\n\n_invstringB\x0f\n\r_payment_hashB\x0b\n\t_offer_idB\x08\n\x06_indexB\x08\n\x06_startB\x08\n\x06_limit\"C\n\x14ListinvoicesResponse\x12+\n\x08invoices\x18\x01 \x03(\x0b\x32\x19.cln.ListinvoicesInvoices\"\xd3\x06\n\x14ListinvoicesInvoices\x12\r\n\x05label\x18\x01 \x01(\t\x12\x18\n\x0b\x64\x65scription\x18\x02 \x01(\tH\x00\x88\x01\x01\x12\x14\n\x0cpayment_hash\x18\x03 \x01(\x0c\x12\x44\n\x06status\x18\x04 \x01(\x0e\x32\x34.cln.ListinvoicesInvoices.ListinvoicesInvoicesStatus\x12\x12\n\nexpires_at\x18\x05 \x01(\x04\x12%\n\x0b\x61mount_msat\x18\x06 \x01(\x0b\x32\x0b.cln.AmountH\x01\x88\x01\x01\x12\x13\n\x06\x62olt11\x18\x07 \x01(\tH\x02\x88\x01\x01\x12\x13\n\x06\x62olt12\x18\x08 \x01(\tH\x03\x88\x01\x01\x12\x1b\n\x0elocal_offer_id\x18\t \x01(\x0cH\x04\x88\x01\x01\x12\x16\n\tpay_index\x18\x0b \x01(\x04H\x05\x88\x01\x01\x12.\n\x14\x61mount_received_msat\x18\x0c \x01(\x0b\x32\x0b.cln.AmountH\x06\x88\x01\x01\x12\x14\n\x07paid_at\x18\r \x01(\x04H\x07\x88\x01\x01\x12\x1d\n\x10payment_preimage\x18\x0e \x01(\x0cH\x08\x88\x01\x01\x12\x1e\n\x11invreq_payer_note\x18\x0f \x01(\tH\t\x88\x01\x01\x12\x1a\n\rcreated_index\x18\x10 \x01(\x04H\n\x88\x01\x01\x12\x1a\n\rupdated_index\x18\x11 \x01(\x04H\x0b\x88\x01\x01\x12\x41\n\rpaid_outpoint\x18\x12 \x01(\x0b\x32%.cln.ListinvoicesInvoicesPaidOutpointH\x0c\x88\x01\x01\"?\n\x1aListinvoicesInvoicesStatus\x12\n\n\x06UNPAID\x10\x00\x12\x08\n\x04PAID\x10\x01\x12\x0b\n\x07\x45XPIRED\x10\x02\x42\x0e\n\x0c_descriptionB\x0e\n\x0c_amount_msatB\t\n\x07_bolt11B\t\n\x07_bolt12B\x11\n\x0f_local_offer_idB\x0c\n\n_pay_indexB\x17\n\x15_amount_received_msatB\n\n\x08_paid_atB\x13\n\x11_payment_preimageB\x14\n\x12_invreq_payer_noteB\x10\n\x0e_created_indexB\x10\n\x0e_updated_indexB\x10\n\x0e_paid_outpoint\"@\n ListinvoicesInvoicesPaidOutpoint\x12\x0c\n\x04txid\x18\x01 \x01(\x0c\x12\x0e\n\x06outnum\x18\x02 \x01(\r\"\xf6\x03\n\x10SendonionRequest\x12\r\n\x05onion\x18\x01 \x01(\x0c\x12)\n\tfirst_hop\x18\x02 \x01(\x0b\x32\x16.cln.SendonionFirstHop\x12\x14\n\x0cpayment_hash\x18\x03 \x01(\x0c\x12\x12\n\x05label\x18\x04 \x01(\tH\x00\x88\x01\x01\x12\x16\n\x0eshared_secrets\x18\x05 \x03(\x0c\x12\x13\n\x06partid\x18\x06 \x01(\rH\x01\x88\x01\x01\x12\x13\n\x06\x62olt11\x18\x07 \x01(\tH\x02\x88\x01\x01\x12\x18\n\x0b\x64\x65stination\x18\t \x01(\x0cH\x03\x88\x01\x01\x12\x14\n\x07groupid\x18\x0b \x01(\x04H\x04\x88\x01\x01\x12%\n\x0b\x61mount_msat\x18\x0c \x01(\x0b\x32\x0b.cln.AmountH\x05\x88\x01\x01\x12\x1a\n\rlocalinvreqid\x18\r \x01(\x0cH\x06\x88\x01\x01\x12\x18\n\x0b\x64\x65scription\x18\x0e \x01(\tH\x07\x88\x01\x01\x12+\n\x11total_amount_msat\x18\x0f \x01(\x0b\x32\x0b.cln.AmountH\x08\x88\x01\x01\x42\x08\n\x06_labelB\t\n\x07_partidB\t\n\x07_bolt11B\x0e\n\x0c_destinationB\n\n\x08_groupidB\x0e\n\x0c_amount_msatB\x10\n\x0e_localinvreqidB\x0e\n\x0c_descriptionB\x14\n\x12_total_amount_msat\"\xe7\x04\n\x11SendonionResponse\x12\n\n\x02id\x18\x01 \x01(\x04\x12\x14\n\x0cpayment_hash\x18\x02 \x01(\x0c\x12\x36\n\x06status\x18\x03 \x01(\x0e\x32&.cln.SendonionResponse.SendonionStatus\x12%\n\x0b\x61mount_msat\x18\x04 \x01(\x0b\x32\x0b.cln.AmountH\x00\x88\x01\x01\x12\x18\n\x0b\x64\x65stination\x18\x05 \x01(\x0cH\x01\x88\x01\x01\x12\x12\n\ncreated_at\x18\x06 \x01(\x04\x12%\n\x10\x61mount_sent_msat\x18\x07 \x01(\x0b\x32\x0b.cln.Amount\x12\x12\n\x05label\x18\x08 \x01(\tH\x02\x88\x01\x01\x12\x13\n\x06\x62olt11\x18\t \x01(\tH\x03\x88\x01\x01\x12\x13\n\x06\x62olt12\x18\n \x01(\tH\x04\x88\x01\x01\x12\x1d\n\x10payment_preimage\x18\x0b \x01(\x0cH\x05\x88\x01\x01\x12\x14\n\x07message\x18\x0c \x01(\tH\x06\x88\x01\x01\x12\x13\n\x06partid\x18\r \x01(\x04H\x07\x88\x01\x01\x12\x1a\n\rcreated_index\x18\x0e \x01(\x04H\x08\x88\x01\x01\x12\x1a\n\rupdated_index\x18\x0f \x01(\x04H\t\x88\x01\x01\",\n\x0fSendonionStatus\x12\x0b\n\x07PENDING\x10\x00\x12\x0c\n\x08\x43OMPLETE\x10\x01\x42\x0e\n\x0c_amount_msatB\x0e\n\x0c_destinationB\x08\n\x06_labelB\t\n\x07_bolt11B\t\n\x07_bolt12B\x13\n\x11_payment_preimageB\n\n\x08_messageB\t\n\x07_partidB\x10\n\x0e_created_indexB\x10\n\x0e_updated_index\"P\n\x11SendonionFirstHop\x12\n\n\x02id\x18\x01 \x01(\x0c\x12 \n\x0b\x61mount_msat\x18\x02 \x01(\x0b\x32\x0b.cln.Amount\x12\r\n\x05\x64\x65lay\x18\x03 \x01(\r\"\xa0\x03\n\x13ListsendpaysRequest\x12\x13\n\x06\x62olt11\x18\x01 \x01(\tH\x00\x88\x01\x01\x12\x19\n\x0cpayment_hash\x18\x02 \x01(\x0cH\x01\x88\x01\x01\x12@\n\x06status\x18\x03 \x01(\x0e\x32+.cln.ListsendpaysRequest.ListsendpaysStatusH\x02\x88\x01\x01\x12>\n\x05index\x18\x04 \x01(\x0e\x32*.cln.ListsendpaysRequest.ListsendpaysIndexH\x03\x88\x01\x01\x12\x12\n\x05start\x18\x05 \x01(\x04H\x04\x88\x01\x01\x12\x12\n\x05limit\x18\x06 \x01(\rH\x05\x88\x01\x01\";\n\x12ListsendpaysStatus\x12\x0b\n\x07PENDING\x10\x00\x12\x0c\n\x08\x43OMPLETE\x10\x01\x12\n\n\x06\x46\x41ILED\x10\x02\"-\n\x11ListsendpaysIndex\x12\x0b\n\x07\x43REATED\x10\x00\x12\x0b\n\x07UPDATED\x10\x01\x42\t\n\x07_bolt11B\x0f\n\r_payment_hashB\t\n\x07_statusB\x08\n\x06_indexB\x08\n\x06_startB\x08\n\x06_limit\"C\n\x14ListsendpaysResponse\x12+\n\x08payments\x18\x01 \x03(\x0b\x32\x19.cln.ListsendpaysPayments\"\xfc\x05\n\x14ListsendpaysPayments\x12\n\n\x02id\x18\x01 \x01(\x04\x12\x0f\n\x07groupid\x18\x02 \x01(\x04\x12\x14\n\x0cpayment_hash\x18\x03 \x01(\x0c\x12\x44\n\x06status\x18\x04 \x01(\x0e\x32\x34.cln.ListsendpaysPayments.ListsendpaysPaymentsStatus\x12%\n\x0b\x61mount_msat\x18\x05 \x01(\x0b\x32\x0b.cln.AmountH\x00\x88\x01\x01\x12\x18\n\x0b\x64\x65stination\x18\x06 \x01(\x0cH\x01\x88\x01\x01\x12\x12\n\ncreated_at\x18\x07 \x01(\x04\x12%\n\x10\x61mount_sent_msat\x18\x08 \x01(\x0b\x32\x0b.cln.Amount\x12\x12\n\x05label\x18\t \x01(\tH\x02\x88\x01\x01\x12\x13\n\x06\x62olt11\x18\n \x01(\tH\x03\x88\x01\x01\x12\x13\n\x06\x62olt12\x18\x0b \x01(\tH\x04\x88\x01\x01\x12\x1d\n\x10payment_preimage\x18\x0c \x01(\x0cH\x05\x88\x01\x01\x12\x17\n\nerroronion\x18\r \x01(\x0cH\x06\x88\x01\x01\x12\x18\n\x0b\x64\x65scription\x18\x0e \x01(\tH\x07\x88\x01\x01\x12\x13\n\x06partid\x18\x0f \x01(\x04H\x08\x88\x01\x01\x12\x1a\n\rcreated_index\x18\x10 \x01(\x04H\t\x88\x01\x01\x12\x1a\n\rupdated_index\x18\x11 \x01(\x04H\n\x88\x01\x01\x12\x19\n\x0c\x63ompleted_at\x18\x12 \x01(\x04H\x0b\x88\x01\x01\"C\n\x1aListsendpaysPaymentsStatus\x12\x0b\n\x07PENDING\x10\x00\x12\n\n\x06\x46\x41ILED\x10\x01\x12\x0c\n\x08\x43OMPLETE\x10\x02\x42\x0e\n\x0c_amount_msatB\x0e\n\x0c_destinationB\x08\n\x06_labelB\t\n\x07_bolt11B\t\n\x07_bolt12B\x13\n\x11_payment_preimageB\r\n\x0b_erroronionB\x0e\n\x0c_descriptionB\t\n\x07_partidB\x10\n\x0e_created_indexB\x10\n\x0e_updated_indexB\x0f\n\r_completed_at\"\x19\n\x17ListtransactionsRequest\"S\n\x18ListtransactionsResponse\x12\x37\n\x0ctransactions\x18\x01 \x03(\x0b\x32!.cln.ListtransactionsTransactions\"\xf8\x01\n\x1cListtransactionsTransactions\x12\x0c\n\x04hash\x18\x01 \x01(\x0c\x12\r\n\x05rawtx\x18\x02 \x01(\x0c\x12\x13\n\x0b\x62lockheight\x18\x03 \x01(\r\x12\x0f\n\x07txindex\x18\x04 \x01(\r\x12\x10\n\x08locktime\x18\x07 \x01(\r\x12\x0f\n\x07version\x18\x08 \x01(\r\x12\x37\n\x06inputs\x18\t \x03(\x0b\x32\'.cln.ListtransactionsTransactionsInputs\x12\x39\n\x07outputs\x18\n \x03(\x0b\x32(.cln.ListtransactionsTransactionsOutputs\"S\n\"ListtransactionsTransactionsInputs\x12\x0c\n\x04txid\x18\x01 \x01(\x0c\x12\r\n\x05index\x18\x02 \x01(\r\x12\x10\n\x08sequence\x18\x03 \x01(\r\"l\n#ListtransactionsTransactionsOutputs\x12\r\n\x05index\x18\x01 \x01(\r\x12\x14\n\x0cscriptPubKey\x18\x03 \x01(\x0c\x12 \n\x0b\x61mount_msat\x18\x06 \x01(\x0b\x32\x0b.cln.Amount\"M\n\x11MakesecretRequest\x12\x10\n\x03hex\x18\x01 \x01(\x0cH\x00\x88\x01\x01\x12\x13\n\x06string\x18\x02 \x01(\tH\x01\x88\x01\x01\x42\x06\n\x04_hexB\t\n\x07_string\"$\n\x12MakesecretResponse\x12\x0e\n\x06secret\x18\x01 \x01(\x0c\"\x93\x04\n\nPayRequest\x12\x0e\n\x06\x62olt11\x18\x01 \x01(\t\x12\x12\n\x05label\x18\x03 \x01(\tH\x00\x88\x01\x01\x12\x1a\n\rmaxfeepercent\x18\x04 \x01(\x01H\x01\x88\x01\x01\x12\x16\n\tretry_for\x18\x05 \x01(\rH\x02\x88\x01\x01\x12\x15\n\x08maxdelay\x18\x06 \x01(\rH\x03\x88\x01\x01\x12#\n\texemptfee\x18\x07 \x01(\x0b\x32\x0b.cln.AmountH\x04\x88\x01\x01\x12\x17\n\nriskfactor\x18\x08 \x01(\x01H\x05\x88\x01\x01\x12\x0f\n\x07\x65xclude\x18\n \x03(\t\x12 \n\x06maxfee\x18\x0b \x01(\x0b\x32\x0b.cln.AmountH\x06\x88\x01\x01\x12\x18\n\x0b\x64\x65scription\x18\x0c \x01(\tH\x07\x88\x01\x01\x12%\n\x0b\x61mount_msat\x18\r \x01(\x0b\x32\x0b.cln.AmountH\x08\x88\x01\x01\x12\x1a\n\rlocalinvreqid\x18\x0e \x01(\x0cH\t\x88\x01\x01\x12&\n\x0cpartial_msat\x18\x0f \x01(\x0b\x32\x0b.cln.AmountH\n\x88\x01\x01\x42\x08\n\x06_labelB\x10\n\x0e_maxfeepercentB\x0c\n\n_retry_forB\x0b\n\t_maxdelayB\x0c\n\n_exemptfeeB\r\n\x0b_riskfactorB\t\n\x07_maxfeeB\x0e\n\x0c_descriptionB\x0e\n\x0c_amount_msatB\x10\n\x0e_localinvreqidB\x0f\n\r_partial_msat\"\xfb\x02\n\x0bPayResponse\x12\x18\n\x10payment_preimage\x18\x01 \x01(\x0c\x12\x18\n\x0b\x64\x65stination\x18\x02 \x01(\x0cH\x00\x88\x01\x01\x12\x14\n\x0cpayment_hash\x18\x03 \x01(\x0c\x12\x12\n\ncreated_at\x18\x04 \x01(\x01\x12\r\n\x05parts\x18\x05 \x01(\r\x12 \n\x0b\x61mount_msat\x18\x06 \x01(\x0b\x32\x0b.cln.Amount\x12%\n\x10\x61mount_sent_msat\x18\x07 \x01(\x0b\x32\x0b.cln.Amount\x12\'\n\x1awarning_partial_completion\x18\x08 \x01(\tH\x01\x88\x01\x01\x12*\n\x06status\x18\t \x01(\x0e\x32\x1a.cln.PayResponse.PayStatus\"2\n\tPayStatus\x12\x0c\n\x08\x43OMPLETE\x10\x00\x12\x0b\n\x07PENDING\x10\x01\x12\n\n\x06\x46\x41ILED\x10\x02\x42\x0e\n\x0c_destinationB\x1d\n\x1b_warning_partial_completion\"*\n\x10ListnodesRequest\x12\x0f\n\x02id\x18\x01 \x01(\x0cH\x00\x88\x01\x01\x42\x05\n\x03_id\"7\n\x11ListnodesResponse\x12\"\n\x05nodes\x18\x01 \x03(\x0b\x32\x13.cln.ListnodesNodes\"\xb8\x02\n\x0eListnodesNodes\x12\x0e\n\x06nodeid\x18\x01 \x01(\x0c\x12\x1b\n\x0elast_timestamp\x18\x02 \x01(\rH\x00\x88\x01\x01\x12\x12\n\x05\x61lias\x18\x03 \x01(\tH\x01\x88\x01\x01\x12\x12\n\x05\x63olor\x18\x04 \x01(\x0cH\x02\x88\x01\x01\x12\x15\n\x08\x66\x65\x61tures\x18\x05 \x01(\x0cH\x03\x88\x01\x01\x12/\n\taddresses\x18\x06 \x03(\x0b\x32\x1c.cln.ListnodesNodesAddresses\x12@\n\x10option_will_fund\x18\x07 \x01(\x0b\x32!.cln.ListnodesNodesOptionWillFundH\x04\x88\x01\x01\x42\x11\n\x0f_last_timestampB\x08\n\x06_aliasB\x08\n\x06_colorB\x0b\n\t_featuresB\x13\n\x11_option_will_fund\"\xf2\x01\n\x1cListnodesNodesOptionWillFund\x12(\n\x13lease_fee_base_msat\x18\x01 \x01(\x0b\x32\x0b.cln.Amount\x12\x17\n\x0flease_fee_basis\x18\x02 \x01(\r\x12\x16\n\x0e\x66unding_weight\x18\x03 \x01(\r\x12.\n\x19\x63hannel_fee_max_base_msat\x18\x04 \x01(\x0b\x32\x0b.cln.Amount\x12\x30\n(channel_fee_max_proportional_thousandths\x18\x05 \x01(\r\x12\x15\n\rcompact_lease\x18\x06 \x01(\x0c\"\xe8\x01\n\x17ListnodesNodesAddresses\x12K\n\titem_type\x18\x01 \x01(\x0e\x32\x38.cln.ListnodesNodesAddresses.ListnodesNodesAddressesType\x12\x0c\n\x04port\x18\x02 \x01(\r\x12\x14\n\x07\x61\x64\x64ress\x18\x03 \x01(\tH\x00\x88\x01\x01\"P\n\x1bListnodesNodesAddressesType\x12\x07\n\x03\x44NS\x10\x00\x12\x08\n\x04IPV4\x10\x01\x12\x08\n\x04IPV6\x10\x02\x12\t\n\x05TORV2\x10\x03\x12\t\n\x05TORV3\x10\x04\x42\n\n\x08_address\"g\n\x15WaitanyinvoiceRequest\x12\x1a\n\rlastpay_index\x18\x01 \x01(\x04H\x00\x88\x01\x01\x12\x14\n\x07timeout\x18\x02 \x01(\x04H\x01\x88\x01\x01\x42\x10\n\x0e_lastpay_indexB\n\n\x08_timeout\"\xd3\x05\n\x16WaitanyinvoiceResponse\x12\r\n\x05label\x18\x01 \x01(\t\x12\x18\n\x0b\x64\x65scription\x18\x02 \x01(\tH\x00\x88\x01\x01\x12\x14\n\x0cpayment_hash\x18\x03 \x01(\x0c\x12@\n\x06status\x18\x04 \x01(\x0e\x32\x30.cln.WaitanyinvoiceResponse.WaitanyinvoiceStatus\x12\x12\n\nexpires_at\x18\x05 \x01(\x04\x12%\n\x0b\x61mount_msat\x18\x06 \x01(\x0b\x32\x0b.cln.AmountH\x01\x88\x01\x01\x12\x13\n\x06\x62olt11\x18\x07 \x01(\tH\x02\x88\x01\x01\x12\x13\n\x06\x62olt12\x18\x08 \x01(\tH\x03\x88\x01\x01\x12\x16\n\tpay_index\x18\t \x01(\x04H\x04\x88\x01\x01\x12.\n\x14\x61mount_received_msat\x18\n \x01(\x0b\x32\x0b.cln.AmountH\x05\x88\x01\x01\x12\x14\n\x07paid_at\x18\x0b \x01(\x04H\x06\x88\x01\x01\x12\x1d\n\x10payment_preimage\x18\x0c \x01(\x0cH\x07\x88\x01\x01\x12\x1a\n\rcreated_index\x18\r \x01(\x04H\x08\x88\x01\x01\x12\x1a\n\rupdated_index\x18\x0e \x01(\x04H\t\x88\x01\x01\x12;\n\rpaid_outpoint\x18\x0f \x01(\x0b\x32\x1f.cln.WaitanyinvoicePaidOutpointH\n\x88\x01\x01\"-\n\x14WaitanyinvoiceStatus\x12\x08\n\x04PAID\x10\x00\x12\x0b\n\x07\x45XPIRED\x10\x01\x42\x0e\n\x0c_descriptionB\x0e\n\x0c_amount_msatB\t\n\x07_bolt11B\t\n\x07_bolt12B\x0c\n\n_pay_indexB\x17\n\x15_amount_received_msatB\n\n\x08_paid_atB\x13\n\x11_payment_preimageB\x10\n\x0e_created_indexB\x10\n\x0e_updated_indexB\x10\n\x0e_paid_outpoint\":\n\x1aWaitanyinvoicePaidOutpoint\x12\x0c\n\x04txid\x18\x01 \x01(\x0c\x12\x0e\n\x06outnum\x18\x02 \x01(\r\"#\n\x12WaitinvoiceRequest\x12\r\n\x05label\x18\x01 \x01(\t\"\xc4\x05\n\x13WaitinvoiceResponse\x12\r\n\x05label\x18\x01 \x01(\t\x12\x18\n\x0b\x64\x65scription\x18\x02 \x01(\tH\x00\x88\x01\x01\x12\x14\n\x0cpayment_hash\x18\x03 \x01(\x0c\x12:\n\x06status\x18\x04 \x01(\x0e\x32*.cln.WaitinvoiceResponse.WaitinvoiceStatus\x12\x12\n\nexpires_at\x18\x05 \x01(\x04\x12%\n\x0b\x61mount_msat\x18\x06 \x01(\x0b\x32\x0b.cln.AmountH\x01\x88\x01\x01\x12\x13\n\x06\x62olt11\x18\x07 \x01(\tH\x02\x88\x01\x01\x12\x13\n\x06\x62olt12\x18\x08 \x01(\tH\x03\x88\x01\x01\x12\x16\n\tpay_index\x18\t \x01(\x04H\x04\x88\x01\x01\x12.\n\x14\x61mount_received_msat\x18\n \x01(\x0b\x32\x0b.cln.AmountH\x05\x88\x01\x01\x12\x14\n\x07paid_at\x18\x0b \x01(\x04H\x06\x88\x01\x01\x12\x1d\n\x10payment_preimage\x18\x0c \x01(\x0cH\x07\x88\x01\x01\x12\x1a\n\rcreated_index\x18\r \x01(\x04H\x08\x88\x01\x01\x12\x1a\n\rupdated_index\x18\x0e \x01(\x04H\t\x88\x01\x01\x12\x38\n\rpaid_outpoint\x18\x0f \x01(\x0b\x32\x1c.cln.WaitinvoicePaidOutpointH\n\x88\x01\x01\"*\n\x11WaitinvoiceStatus\x12\x08\n\x04PAID\x10\x00\x12\x0b\n\x07\x45XPIRED\x10\x01\x42\x0e\n\x0c_descriptionB\x0e\n\x0c_amount_msatB\t\n\x07_bolt11B\t\n\x07_bolt12B\x0c\n\n_pay_indexB\x17\n\x15_amount_received_msatB\n\n\x08_paid_atB\x13\n\x11_payment_preimageB\x10\n\x0e_created_indexB\x10\n\x0e_updated_indexB\x10\n\x0e_paid_outpoint\"7\n\x17WaitinvoicePaidOutpoint\x12\x0c\n\x04txid\x18\x01 \x01(\x0c\x12\x0e\n\x06outnum\x18\x02 \x01(\r\"\x8e\x01\n\x12WaitsendpayRequest\x12\x14\n\x0cpayment_hash\x18\x01 \x01(\x0c\x12\x13\n\x06partid\x18\x02 \x01(\x04H\x00\x88\x01\x01\x12\x14\n\x07timeout\x18\x03 \x01(\rH\x01\x88\x01\x01\x12\x14\n\x07groupid\x18\x04 \x01(\x04H\x02\x88\x01\x01\x42\t\n\x07_partidB\n\n\x08_timeoutB\n\n\x08_groupid\"\x8e\x05\n\x13WaitsendpayResponse\x12\n\n\x02id\x18\x01 \x01(\x04\x12\x14\n\x07groupid\x18\x02 \x01(\x04H\x00\x88\x01\x01\x12\x14\n\x0cpayment_hash\x18\x03 \x01(\x0c\x12:\n\x06status\x18\x04 \x01(\x0e\x32*.cln.WaitsendpayResponse.WaitsendpayStatus\x12%\n\x0b\x61mount_msat\x18\x05 \x01(\x0b\x32\x0b.cln.AmountH\x01\x88\x01\x01\x12\x18\n\x0b\x64\x65stination\x18\x06 \x01(\x0cH\x02\x88\x01\x01\x12\x12\n\ncreated_at\x18\x07 \x01(\x04\x12%\n\x10\x61mount_sent_msat\x18\x08 \x01(\x0b\x32\x0b.cln.Amount\x12\x12\n\x05label\x18\t \x01(\tH\x03\x88\x01\x01\x12\x13\n\x06partid\x18\n \x01(\x04H\x04\x88\x01\x01\x12\x13\n\x06\x62olt11\x18\x0b \x01(\tH\x05\x88\x01\x01\x12\x13\n\x06\x62olt12\x18\x0c \x01(\tH\x06\x88\x01\x01\x12\x1d\n\x10payment_preimage\x18\r \x01(\x0cH\x07\x88\x01\x01\x12\x19\n\x0c\x63ompleted_at\x18\x0e \x01(\x01H\x08\x88\x01\x01\x12\x1a\n\rcreated_index\x18\x0f \x01(\x04H\t\x88\x01\x01\x12\x1a\n\rupdated_index\x18\x10 \x01(\x04H\n\x88\x01\x01\"!\n\x11WaitsendpayStatus\x12\x0c\n\x08\x43OMPLETE\x10\x00\x42\n\n\x08_groupidB\x0e\n\x0c_amount_msatB\x0e\n\x0c_destinationB\x08\n\x06_labelB\t\n\x07_partidB\t\n\x07_bolt11B\t\n\x07_bolt12B\x13\n\x11_payment_preimageB\x0f\n\r_completed_atB\x10\n\x0e_created_indexB\x10\n\x0e_updated_index\"\x97\x01\n\x0eNewaddrRequest\x12@\n\x0b\x61\x64\x64resstype\x18\x01 \x01(\x0e\x32&.cln.NewaddrRequest.NewaddrAddresstypeH\x00\x88\x01\x01\"3\n\x12NewaddrAddresstype\x12\n\n\x06\x42\x45\x43H32\x10\x00\x12\x07\n\x03\x41LL\x10\x02\x12\x08\n\x04P2TR\x10\x03\x42\x0e\n\x0c_addresstype\"M\n\x0fNewaddrResponse\x12\x13\n\x06\x62\x65\x63h32\x18\x01 \x01(\tH\x00\x88\x01\x01\x12\x11\n\x04p2tr\x18\x03 \x01(\tH\x01\x88\x01\x01\x42\t\n\x07_bech32B\x07\n\x05_p2tr\"\xb9\x01\n\x0fWithdrawRequest\x12\x13\n\x0b\x64\x65stination\x18\x01 \x01(\t\x12!\n\x07satoshi\x18\x02 \x01(\x0b\x32\x10.cln.AmountOrAll\x12\x14\n\x07minconf\x18\x03 \x01(\rH\x00\x88\x01\x01\x12\x1c\n\x05utxos\x18\x04 \x03(\x0b\x32\r.cln.Outpoint\x12\"\n\x07\x66\x65\x65rate\x18\x05 \x01(\x0b\x32\x0c.cln.FeerateH\x01\x88\x01\x01\x42\n\n\x08_minconfB\n\n\x08_feerate\":\n\x10WithdrawResponse\x12\n\n\x02tx\x18\x01 \x01(\x0c\x12\x0c\n\x04txid\x18\x02 \x01(\x0c\x12\x0c\n\x04psbt\x18\x03 \x01(\t\"\xaf\x03\n\x0eKeysendRequest\x12\x13\n\x0b\x64\x65stination\x18\x01 \x01(\x0c\x12\x12\n\x05label\x18\x03 \x01(\tH\x00\x88\x01\x01\x12\x1a\n\rmaxfeepercent\x18\x04 \x01(\x01H\x01\x88\x01\x01\x12\x16\n\tretry_for\x18\x05 \x01(\rH\x02\x88\x01\x01\x12\x15\n\x08maxdelay\x18\x06 \x01(\rH\x03\x88\x01\x01\x12#\n\texemptfee\x18\x07 \x01(\x0b\x32\x0b.cln.AmountH\x04\x88\x01\x01\x12+\n\nroutehints\x18\x08 \x01(\x0b\x32\x12.cln.RoutehintListH\x05\x88\x01\x01\x12&\n\textratlvs\x18\t \x01(\x0b\x32\x0e.cln.TlvStreamH\x06\x88\x01\x01\x12 \n\x0b\x61mount_msat\x18\n \x01(\x0b\x32\x0b.cln.Amount\x12 \n\x06maxfee\x18\x0b \x01(\x0b\x32\x0b.cln.AmountH\x07\x88\x01\x01\x42\x08\n\x06_labelB\x10\n\x0e_maxfeepercentB\x0c\n\n_retry_forB\x0b\n\t_maxdelayB\x0c\n\n_exemptfeeB\r\n\x0b_routehintsB\x0c\n\n_extratlvsB\t\n\x07_maxfee\"\xf2\x02\n\x0fKeysendResponse\x12\x18\n\x10payment_preimage\x18\x01 \x01(\x0c\x12\x18\n\x0b\x64\x65stination\x18\x02 \x01(\x0cH\x00\x88\x01\x01\x12\x14\n\x0cpayment_hash\x18\x03 \x01(\x0c\x12\x12\n\ncreated_at\x18\x04 \x01(\x01\x12\r\n\x05parts\x18\x05 \x01(\r\x12 \n\x0b\x61mount_msat\x18\x06 \x01(\x0b\x32\x0b.cln.Amount\x12%\n\x10\x61mount_sent_msat\x18\x07 \x01(\x0b\x32\x0b.cln.Amount\x12\'\n\x1awarning_partial_completion\x18\x08 \x01(\tH\x01\x88\x01\x01\x12\x32\n\x06status\x18\t \x01(\x0e\x32\".cln.KeysendResponse.KeysendStatus\"\x1d\n\rKeysendStatus\x12\x0c\n\x08\x43OMPLETE\x10\x00\x42\x0e\n\x0c_destinationB\x1d\n\x1b_warning_partial_completion\"\xa4\x03\n\x0f\x46undpsbtRequest\x12!\n\x07satoshi\x18\x01 \x01(\x0b\x32\x10.cln.AmountOrAll\x12\x1d\n\x07\x66\x65\x65rate\x18\x02 \x01(\x0b\x32\x0c.cln.Feerate\x12\x13\n\x0bstartweight\x18\x03 \x01(\r\x12\x14\n\x07minconf\x18\x04 \x01(\rH\x00\x88\x01\x01\x12\x14\n\x07reserve\x18\x05 \x01(\rH\x01\x88\x01\x01\x12\x15\n\x08locktime\x18\x06 \x01(\rH\x02\x88\x01\x01\x12\x1f\n\x12min_witness_weight\x18\x07 \x01(\rH\x03\x88\x01\x01\x12\x1d\n\x10\x65xcess_as_change\x18\x08 \x01(\x08H\x04\x88\x01\x01\x12\x17\n\nnonwrapped\x18\t \x01(\x08H\x05\x88\x01\x01\x12#\n\x16opening_anchor_channel\x18\n \x01(\x08H\x06\x88\x01\x01\x42\n\n\x08_minconfB\n\n\x08_reserveB\x0b\n\t_locktimeB\x15\n\x13_min_witness_weightB\x13\n\x11_excess_as_changeB\r\n\x0b_nonwrappedB\x19\n\x17_opening_anchor_channel\"\xd9\x01\n\x10\x46undpsbtResponse\x12\x0c\n\x04psbt\x18\x01 \x01(\t\x12\x16\n\x0e\x66\x65\x65rate_per_kw\x18\x02 \x01(\r\x12\x1e\n\x16\x65stimated_final_weight\x18\x03 \x01(\r\x12 \n\x0b\x65xcess_msat\x18\x04 \x01(\x0b\x32\x0b.cln.Amount\x12\x1a\n\rchange_outnum\x18\x05 \x01(\rH\x00\x88\x01\x01\x12/\n\x0creservations\x18\x06 \x03(\x0b\x32\x19.cln.FundpsbtReservationsB\x10\n\x0e_change_outnum\"u\n\x14\x46undpsbtReservations\x12\x0c\n\x04txid\x18\x01 \x01(\x0c\x12\x0c\n\x04vout\x18\x02 \x01(\r\x12\x14\n\x0cwas_reserved\x18\x03 \x01(\x08\x12\x10\n\x08reserved\x18\x04 \x01(\x08\x12\x19\n\x11reserved_to_block\x18\x05 \x01(\r\"A\n\x0fSendpsbtRequest\x12\x0c\n\x04psbt\x18\x01 \x01(\t\x12\x14\n\x07reserve\x18\x02 \x01(\rH\x00\x88\x01\x01\x42\n\n\x08_reserve\",\n\x10SendpsbtResponse\x12\n\n\x02tx\x18\x01 \x01(\x0c\x12\x0c\n\x04txid\x18\x02 \x01(\x0c\"1\n\x0fSignpsbtRequest\x12\x0c\n\x04psbt\x18\x01 \x01(\t\x12\x10\n\x08signonly\x18\x02 \x03(\r\"\'\n\x10SignpsbtResponse\x12\x13\n\x0bsigned_psbt\x18\x01 \x01(\t\"\xa0\x03\n\x0fUtxopsbtRequest\x12!\n\x07satoshi\x18\x01 \x01(\x0b\x32\x10.cln.AmountOrAll\x12\x1d\n\x07\x66\x65\x65rate\x18\x02 \x01(\x0b\x32\x0c.cln.Feerate\x12\x13\n\x0bstartweight\x18\x03 \x01(\r\x12\x1c\n\x05utxos\x18\x04 \x03(\x0b\x32\r.cln.Outpoint\x12\x14\n\x07reserve\x18\x05 \x01(\rH\x00\x88\x01\x01\x12\x15\n\x08locktime\x18\x06 \x01(\rH\x01\x88\x01\x01\x12\x1f\n\x12min_witness_weight\x18\x07 \x01(\rH\x02\x88\x01\x01\x12\x17\n\nreservedok\x18\x08 \x01(\x08H\x03\x88\x01\x01\x12\x1d\n\x10\x65xcess_as_change\x18\t \x01(\x08H\x04\x88\x01\x01\x12#\n\x16opening_anchor_channel\x18\n \x01(\x08H\x05\x88\x01\x01\x42\n\n\x08_reserveB\x0b\n\t_locktimeB\x15\n\x13_min_witness_weightB\r\n\x0b_reservedokB\x13\n\x11_excess_as_changeB\x19\n\x17_opening_anchor_channel\"\xd9\x01\n\x10UtxopsbtResponse\x12\x0c\n\x04psbt\x18\x01 \x01(\t\x12\x16\n\x0e\x66\x65\x65rate_per_kw\x18\x02 \x01(\r\x12\x1e\n\x16\x65stimated_final_weight\x18\x03 \x01(\r\x12 \n\x0b\x65xcess_msat\x18\x04 \x01(\x0b\x32\x0b.cln.Amount\x12\x1a\n\rchange_outnum\x18\x05 \x01(\rH\x00\x88\x01\x01\x12/\n\x0creservations\x18\x06 \x03(\x0b\x32\x19.cln.UtxopsbtReservationsB\x10\n\x0e_change_outnum\"u\n\x14UtxopsbtReservations\x12\x0c\n\x04txid\x18\x01 \x01(\x0c\x12\x0c\n\x04vout\x18\x02 \x01(\r\x12\x14\n\x0cwas_reserved\x18\x03 \x01(\x08\x12\x10\n\x08reserved\x18\x04 \x01(\x08\x12\x19\n\x11reserved_to_block\x18\x05 \x01(\r\" \n\x10TxdiscardRequest\x12\x0c\n\x04txid\x18\x01 \x01(\x0c\"6\n\x11TxdiscardResponse\x12\x13\n\x0bunsigned_tx\x18\x01 \x01(\x0c\x12\x0c\n\x04txid\x18\x02 \x01(\x0c\"\xa4\x01\n\x10TxprepareRequest\x12\"\n\x07\x66\x65\x65rate\x18\x02 \x01(\x0b\x32\x0c.cln.FeerateH\x00\x88\x01\x01\x12\x14\n\x07minconf\x18\x03 \x01(\rH\x01\x88\x01\x01\x12\x1c\n\x05utxos\x18\x04 \x03(\x0b\x32\r.cln.Outpoint\x12 \n\x07outputs\x18\x05 \x03(\x0b\x32\x0f.cln.OutputDescB\n\n\x08_feerateB\n\n\x08_minconf\"D\n\x11TxprepareResponse\x12\x0c\n\x04psbt\x18\x01 \x01(\t\x12\x13\n\x0bunsigned_tx\x18\x02 \x01(\x0c\x12\x0c\n\x04txid\x18\x03 \x01(\x0c\"\x1d\n\rTxsendRequest\x12\x0c\n\x04txid\x18\x01 \x01(\x0c\"8\n\x0eTxsendResponse\x12\x0c\n\x04psbt\x18\x01 \x01(\t\x12\n\n\x02tx\x18\x02 \x01(\x0c\x12\x0c\n\x04txid\x18\x03 \x01(\x0c\"e\n\x17ListpeerchannelsRequest\x12\x0f\n\x02id\x18\x01 \x01(\x0cH\x00\x88\x01\x01\x12\x1d\n\x10short_channel_id\x18\x02 \x01(\tH\x01\x88\x01\x01\x42\x05\n\x03_idB\x13\n\x11_short_channel_id\"K\n\x18ListpeerchannelsResponse\x12/\n\x08\x63hannels\x18\x01 \x03(\x0b\x32\x1d.cln.ListpeerchannelsChannels\"\xf4\x19\n\x18ListpeerchannelsChannels\x12\x0f\n\x07peer_id\x18\x01 \x01(\x0c\x12\x16\n\x0epeer_connected\x18\x02 \x01(\x08\x12 \n\x05state\x18\x03 \x01(\x0e\x32\x11.cln.ChannelState\x12\x19\n\x0cscratch_txid\x18\x04 \x01(\x0cH\x00\x88\x01\x01\x12:\n\x07\x66\x65\x65rate\x18\x06 \x01(\x0b\x32$.cln.ListpeerchannelsChannelsFeerateH\x01\x88\x01\x01\x12\x12\n\x05owner\x18\x07 \x01(\tH\x02\x88\x01\x01\x12\x1d\n\x10short_channel_id\x18\x08 \x01(\tH\x03\x88\x01\x01\x12\x17\n\nchannel_id\x18\t \x01(\x0cH\x04\x88\x01\x01\x12\x19\n\x0c\x66unding_txid\x18\n \x01(\x0cH\x05\x88\x01\x01\x12\x1b\n\x0e\x66unding_outnum\x18\x0b \x01(\rH\x06\x88\x01\x01\x12\x1c\n\x0finitial_feerate\x18\x0c \x01(\tH\x07\x88\x01\x01\x12\x19\n\x0clast_feerate\x18\r \x01(\tH\x08\x88\x01\x01\x12\x19\n\x0cnext_feerate\x18\x0e \x01(\tH\t\x88\x01\x01\x12\x1a\n\rnext_fee_step\x18\x0f \x01(\rH\n\x88\x01\x01\x12\x37\n\x08inflight\x18\x10 \x03(\x0b\x32%.cln.ListpeerchannelsChannelsInflight\x12\x15\n\x08\x63lose_to\x18\x11 \x01(\x0cH\x0b\x88\x01\x01\x12\x14\n\x07private\x18\x12 \x01(\x08H\x0c\x88\x01\x01\x12 \n\x06opener\x18\x13 \x01(\x0e\x32\x10.cln.ChannelSide\x12%\n\x06\x63loser\x18\x14 \x01(\x0e\x32\x10.cln.ChannelSideH\r\x88\x01\x01\x12:\n\x07\x66unding\x18\x16 \x01(\x0b\x32$.cln.ListpeerchannelsChannelsFundingH\x0e\x88\x01\x01\x12$\n\nto_us_msat\x18\x17 \x01(\x0b\x32\x0b.cln.AmountH\x0f\x88\x01\x01\x12(\n\x0emin_to_us_msat\x18\x18 \x01(\x0b\x32\x0b.cln.AmountH\x10\x88\x01\x01\x12(\n\x0emax_to_us_msat\x18\x19 \x01(\x0b\x32\x0b.cln.AmountH\x11\x88\x01\x01\x12$\n\ntotal_msat\x18\x1a \x01(\x0b\x32\x0b.cln.AmountH\x12\x88\x01\x01\x12\'\n\rfee_base_msat\x18\x1b \x01(\x0b\x32\x0b.cln.AmountH\x13\x88\x01\x01\x12(\n\x1b\x66\x65\x65_proportional_millionths\x18\x1c \x01(\rH\x14\x88\x01\x01\x12)\n\x0f\x64ust_limit_msat\x18\x1d \x01(\x0b\x32\x0b.cln.AmountH\x15\x88\x01\x01\x12\x30\n\x16max_total_htlc_in_msat\x18\x1e \x01(\x0b\x32\x0b.cln.AmountH\x16\x88\x01\x01\x12,\n\x12their_reserve_msat\x18\x1f \x01(\x0b\x32\x0b.cln.AmountH\x17\x88\x01\x01\x12*\n\x10our_reserve_msat\x18 \x01(\x0b\x32\x0b.cln.AmountH\x18\x88\x01\x01\x12(\n\x0espendable_msat\x18! \x01(\x0b\x32\x0b.cln.AmountH\x19\x88\x01\x01\x12)\n\x0freceivable_msat\x18\" \x01(\x0b\x32\x0b.cln.AmountH\x1a\x88\x01\x01\x12.\n\x14minimum_htlc_in_msat\x18# \x01(\x0b\x32\x0b.cln.AmountH\x1b\x88\x01\x01\x12/\n\x15minimum_htlc_out_msat\x18$ \x01(\x0b\x32\x0b.cln.AmountH\x1c\x88\x01\x01\x12/\n\x15maximum_htlc_out_msat\x18% \x01(\x0b\x32\x0b.cln.AmountH\x1d\x88\x01\x01\x12 \n\x13their_to_self_delay\x18& \x01(\rH\x1e\x88\x01\x01\x12\x1e\n\x11our_to_self_delay\x18\' \x01(\rH\x1f\x88\x01\x01\x12\x1f\n\x12max_accepted_htlcs\x18( \x01(\rH \x88\x01\x01\x12\x36\n\x05\x61lias\x18) \x01(\x0b\x32\".cln.ListpeerchannelsChannelsAliasH!\x88\x01\x01\x12\x0e\n\x06status\x18+ \x03(\t\x12 \n\x13in_payments_offered\x18, \x01(\x04H\"\x88\x01\x01\x12)\n\x0fin_offered_msat\x18- \x01(\x0b\x32\x0b.cln.AmountH#\x88\x01\x01\x12\"\n\x15in_payments_fulfilled\x18. \x01(\x04H$\x88\x01\x01\x12+\n\x11in_fulfilled_msat\x18/ \x01(\x0b\x32\x0b.cln.AmountH%\x88\x01\x01\x12!\n\x14out_payments_offered\x18\x30 \x01(\x04H&\x88\x01\x01\x12*\n\x10out_offered_msat\x18\x31 \x01(\x0b\x32\x0b.cln.AmountH\'\x88\x01\x01\x12#\n\x16out_payments_fulfilled\x18\x32 \x01(\x04H(\x88\x01\x01\x12,\n\x12out_fulfilled_msat\x18\x33 \x01(\x0b\x32\x0b.cln.AmountH)\x88\x01\x01\x12\x31\n\x05htlcs\x18\x34 \x03(\x0b\x32\".cln.ListpeerchannelsChannelsHtlcs\x12\x1a\n\rclose_to_addr\x18\x35 \x01(\tH*\x88\x01\x01\x12\x1e\n\x11ignore_fee_limits\x18\x36 \x01(\x08H+\x88\x01\x01\x12:\n\x07updates\x18\x37 \x01(\x0b\x32$.cln.ListpeerchannelsChannelsUpdatesH,\x88\x01\x01\x12#\n\x16last_stable_connection\x18\x38 \x01(\x04H-\x88\x01\x01\x12\x17\n\nlost_state\x18\x39 \x01(\x08H.\x88\x01\x01\x12\x1a\n\rreestablished\x18: \x01(\x08H/\x88\x01\x01\x12*\n\x10last_tx_fee_msat\x18; \x01(\x0b\x32\x0b.cln.AmountH0\x88\x01\x01\x12\x16\n\tdirection\x18< \x01(\rH1\x88\x01\x01\x12=\n#their_max_htlc_value_in_flight_msat\x18= \x01(\x0b\x32\x0b.cln.AmountH2\x88\x01\x01\x12;\n!our_max_htlc_value_in_flight_msat\x18> \x01(\x0b\x32\x0b.cln.AmountH3\x88\x01\x01\x42\x0f\n\r_scratch_txidB\n\n\x08_feerateB\x08\n\x06_ownerB\x13\n\x11_short_channel_idB\r\n\x0b_channel_idB\x0f\n\r_funding_txidB\x11\n\x0f_funding_outnumB\x12\n\x10_initial_feerateB\x0f\n\r_last_feerateB\x0f\n\r_next_feerateB\x10\n\x0e_next_fee_stepB\x0b\n\t_close_toB\n\n\x08_privateB\t\n\x07_closerB\n\n\x08_fundingB\r\n\x0b_to_us_msatB\x11\n\x0f_min_to_us_msatB\x11\n\x0f_max_to_us_msatB\r\n\x0b_total_msatB\x10\n\x0e_fee_base_msatB\x1e\n\x1c_fee_proportional_millionthsB\x12\n\x10_dust_limit_msatB\x19\n\x17_max_total_htlc_in_msatB\x15\n\x13_their_reserve_msatB\x13\n\x11_our_reserve_msatB\x11\n\x0f_spendable_msatB\x12\n\x10_receivable_msatB\x17\n\x15_minimum_htlc_in_msatB\x18\n\x16_minimum_htlc_out_msatB\x18\n\x16_maximum_htlc_out_msatB\x16\n\x14_their_to_self_delayB\x14\n\x12_our_to_self_delayB\x15\n\x13_max_accepted_htlcsB\x08\n\x06_aliasB\x16\n\x14_in_payments_offeredB\x12\n\x10_in_offered_msatB\x18\n\x16_in_payments_fulfilledB\x14\n\x12_in_fulfilled_msatB\x17\n\x15_out_payments_offeredB\x13\n\x11_out_offered_msatB\x19\n\x17_out_payments_fulfilledB\x15\n\x13_out_fulfilled_msatB\x10\n\x0e_close_to_addrB\x14\n\x12_ignore_fee_limitsB\n\n\x08_updatesB\x19\n\x17_last_stable_connectionB\r\n\x0b_lost_stateB\x10\n\x0e_reestablishedB\x13\n\x11_last_tx_fee_msatB\x0c\n\n_directionB&\n$_their_max_htlc_value_in_flight_msatB$\n\"_our_max_htlc_value_in_flight_msat\"\xa7\x01\n\x1fListpeerchannelsChannelsUpdates\x12\x38\n\x05local\x18\x01 \x01(\x0b\x32).cln.ListpeerchannelsChannelsUpdatesLocal\x12?\n\x06remote\x18\x02 \x01(\x0b\x32*.cln.ListpeerchannelsChannelsUpdatesRemoteH\x00\x88\x01\x01\x42\t\n\x07_remote\"\xda\x01\n$ListpeerchannelsChannelsUpdatesLocal\x12&\n\x11htlc_minimum_msat\x18\x01 \x01(\x0b\x32\x0b.cln.Amount\x12&\n\x11htlc_maximum_msat\x18\x02 \x01(\x0b\x32\x0b.cln.Amount\x12\x19\n\x11\x63ltv_expiry_delta\x18\x03 \x01(\r\x12\"\n\rfee_base_msat\x18\x04 \x01(\x0b\x32\x0b.cln.Amount\x12#\n\x1b\x66\x65\x65_proportional_millionths\x18\x05 \x01(\r\"\xdb\x01\n%ListpeerchannelsChannelsUpdatesRemote\x12&\n\x11htlc_minimum_msat\x18\x01 \x01(\x0b\x32\x0b.cln.Amount\x12&\n\x11htlc_maximum_msat\x18\x02 \x01(\x0b\x32\x0b.cln.Amount\x12\x19\n\x11\x63ltv_expiry_delta\x18\x03 \x01(\r\x12\"\n\rfee_base_msat\x18\x04 \x01(\x0b\x32\x0b.cln.Amount\x12#\n\x1b\x66\x65\x65_proportional_millionths\x18\x05 \x01(\r\"?\n\x1fListpeerchannelsChannelsFeerate\x12\r\n\x05perkw\x18\x01 \x01(\r\x12\r\n\x05perkb\x18\x02 \x01(\r\"\x8b\x02\n ListpeerchannelsChannelsInflight\x12\x14\n\x0c\x66unding_txid\x18\x01 \x01(\x0c\x12\x16\n\x0e\x66unding_outnum\x18\x02 \x01(\r\x12\x0f\n\x07\x66\x65\x65rate\x18\x03 \x01(\t\x12\'\n\x12total_funding_msat\x18\x04 \x01(\x0b\x32\x0b.cln.Amount\x12%\n\x10our_funding_msat\x18\x05 \x01(\x0b\x32\x0b.cln.Amount\x12\x19\n\x0cscratch_txid\x18\x06 \x01(\x0cH\x00\x88\x01\x01\x12\x1a\n\rsplice_amount\x18\x07 \x01(\x12H\x01\x88\x01\x01\x42\x0f\n\r_scratch_txidB\x10\n\x0e_splice_amount\"\x9d\x02\n\x1fListpeerchannelsChannelsFunding\x12%\n\x0bpushed_msat\x18\x01 \x01(\x0b\x32\x0b.cln.AmountH\x00\x88\x01\x01\x12%\n\x10local_funds_msat\x18\x02 \x01(\x0b\x32\x0b.cln.Amount\x12&\n\x11remote_funds_msat\x18\x03 \x01(\x0b\x32\x0b.cln.Amount\x12\'\n\rfee_paid_msat\x18\x04 \x01(\x0b\x32\x0b.cln.AmountH\x01\x88\x01\x01\x12\'\n\rfee_rcvd_msat\x18\x05 \x01(\x0b\x32\x0b.cln.AmountH\x02\x88\x01\x01\x42\x0e\n\x0c_pushed_msatB\x10\n\x0e_fee_paid_msatB\x10\n\x0e_fee_rcvd_msat\"]\n\x1dListpeerchannelsChannelsAlias\x12\x12\n\x05local\x18\x01 \x01(\tH\x00\x88\x01\x01\x12\x13\n\x06remote\x18\x02 \x01(\tH\x01\x88\x01\x01\x42\x08\n\x06_localB\t\n\x07_remote\"\xf9\x02\n\x1dListpeerchannelsChannelsHtlcs\x12\\\n\tdirection\x18\x01 \x01(\x0e\x32I.cln.ListpeerchannelsChannelsHtlcs.ListpeerchannelsChannelsHtlcsDirection\x12\n\n\x02id\x18\x02 \x01(\x04\x12 \n\x0b\x61mount_msat\x18\x03 \x01(\x0b\x32\x0b.cln.Amount\x12\x0e\n\x06\x65xpiry\x18\x04 \x01(\r\x12\x14\n\x0cpayment_hash\x18\x05 \x01(\x0c\x12\x1a\n\rlocal_trimmed\x18\x06 \x01(\x08H\x00\x88\x01\x01\x12\x13\n\x06status\x18\x07 \x01(\tH\x01\x88\x01\x01\x12\x1d\n\x05state\x18\x08 \x01(\x0e\x32\x0e.cln.HtlcState\"9\n&ListpeerchannelsChannelsHtlcsDirection\x12\x06\n\x02IN\x10\x00\x12\x07\n\x03OUT\x10\x01\x42\x10\n\x0e_local_trimmedB\t\n\x07_status\"3\n\x19ListclosedchannelsRequest\x12\x0f\n\x02id\x18\x01 \x01(\x0cH\x00\x88\x01\x01\x42\x05\n\x03_id\"[\n\x1aListclosedchannelsResponse\x12=\n\x0e\x63losedchannels\x18\x01 \x03(\x0b\x32%.cln.ListclosedchannelsClosedchannels\"\xf0\t\n ListclosedchannelsClosedchannels\x12\x14\n\x07peer_id\x18\x01 \x01(\x0cH\x00\x88\x01\x01\x12\x12\n\nchannel_id\x18\x02 \x01(\x0c\x12\x1d\n\x10short_channel_id\x18\x03 \x01(\tH\x01\x88\x01\x01\x12>\n\x05\x61lias\x18\x04 \x01(\x0b\x32*.cln.ListclosedchannelsClosedchannelsAliasH\x02\x88\x01\x01\x12 \n\x06opener\x18\x05 \x01(\x0e\x32\x10.cln.ChannelSide\x12%\n\x06\x63loser\x18\x06 \x01(\x0e\x32\x10.cln.ChannelSideH\x03\x88\x01\x01\x12\x0f\n\x07private\x18\x07 \x01(\x08\x12\x1f\n\x17total_local_commitments\x18\t \x01(\x04\x12 \n\x18total_remote_commitments\x18\n \x01(\x04\x12\x18\n\x10total_htlcs_sent\x18\x0b \x01(\x04\x12\x14\n\x0c\x66unding_txid\x18\x0c \x01(\x0c\x12\x16\n\x0e\x66unding_outnum\x18\r \x01(\r\x12\x0e\n\x06leased\x18\x0e \x01(\x08\x12/\n\x15\x66unding_fee_paid_msat\x18\x0f \x01(\x0b\x32\x0b.cln.AmountH\x04\x88\x01\x01\x12/\n\x15\x66unding_fee_rcvd_msat\x18\x10 \x01(\x0b\x32\x0b.cln.AmountH\x05\x88\x01\x01\x12-\n\x13\x66unding_pushed_msat\x18\x11 \x01(\x0b\x32\x0b.cln.AmountH\x06\x88\x01\x01\x12\x1f\n\ntotal_msat\x18\x12 \x01(\x0b\x32\x0b.cln.Amount\x12%\n\x10\x66inal_to_us_msat\x18\x13 \x01(\x0b\x32\x0b.cln.Amount\x12#\n\x0emin_to_us_msat\x18\x14 \x01(\x0b\x32\x0b.cln.Amount\x12#\n\x0emax_to_us_msat\x18\x15 \x01(\x0b\x32\x0b.cln.Amount\x12!\n\x14last_commitment_txid\x18\x16 \x01(\x0cH\x07\x88\x01\x01\x12\x32\n\x18last_commitment_fee_msat\x18\x17 \x01(\x0b\x32\x0b.cln.AmountH\x08\x88\x01\x01\x12\x65\n\x0b\x63lose_cause\x18\x18 \x01(\x0e\x32P.cln.ListclosedchannelsClosedchannels.ListclosedchannelsClosedchannelsCloseCause\x12#\n\x16last_stable_connection\x18\x19 \x01(\x04H\t\x88\x01\x01\"u\n*ListclosedchannelsClosedchannelsCloseCause\x12\x0b\n\x07UNKNOWN\x10\x00\x12\t\n\x05LOCAL\x10\x01\x12\x08\n\x04USER\x10\x02\x12\n\n\x06REMOTE\x10\x03\x12\x0c\n\x08PROTOCOL\x10\x04\x12\x0b\n\x07ONCHAIN\x10\x05\x42\n\n\x08_peer_idB\x13\n\x11_short_channel_idB\x08\n\x06_aliasB\t\n\x07_closerB\x18\n\x16_funding_fee_paid_msatB\x18\n\x16_funding_fee_rcvd_msatB\x16\n\x14_funding_pushed_msatB\x17\n\x15_last_commitment_txidB\x1b\n\x19_last_commitment_fee_msatB\x19\n\x17_last_stable_connection\"e\n%ListclosedchannelsClosedchannelsAlias\x12\x12\n\x05local\x18\x01 \x01(\tH\x00\x88\x01\x01\x12\x13\n\x06remote\x18\x02 \x01(\tH\x01\x88\x01\x01\x42\x08\n\x06_localB\t\n\x07_remote\"L\n\x10\x44\x65\x63odepayRequest\x12\x0e\n\x06\x62olt11\x18\x01 \x01(\t\x12\x18\n\x0b\x64\x65scription\x18\x02 \x01(\tH\x00\x88\x01\x01\x42\x0e\n\x0c_description\"\xc7\x04\n\x11\x44\x65\x63odepayResponse\x12\x10\n\x08\x63urrency\x18\x01 \x01(\t\x12\x12\n\ncreated_at\x18\x02 \x01(\x04\x12\x0e\n\x06\x65xpiry\x18\x03 \x01(\x04\x12\r\n\x05payee\x18\x04 \x01(\x0c\x12%\n\x0b\x61mount_msat\x18\x05 \x01(\x0b\x32\x0b.cln.AmountH\x00\x88\x01\x01\x12\x14\n\x0cpayment_hash\x18\x06 \x01(\x0c\x12\x11\n\tsignature\x18\x07 \x01(\t\x12\x18\n\x0b\x64\x65scription\x18\x08 \x01(\tH\x01\x88\x01\x01\x12\x1d\n\x10\x64\x65scription_hash\x18\t \x01(\x0cH\x02\x88\x01\x01\x12\x1d\n\x15min_final_cltv_expiry\x18\n \x01(\r\x12\x1b\n\x0epayment_secret\x18\x0b \x01(\x0cH\x03\x88\x01\x01\x12\x15\n\x08\x66\x65\x61tures\x18\x0c \x01(\x0cH\x04\x88\x01\x01\x12\x1d\n\x10payment_metadata\x18\r \x01(\x0cH\x05\x88\x01\x01\x12*\n\tfallbacks\x18\x0e \x03(\x0b\x32\x17.cln.DecodepayFallbacks\x12\"\n\x05\x65xtra\x18\x10 \x03(\x0b\x32\x13.cln.DecodepayExtra\x12-\n\x06routes\x18\x11 \x01(\x0b\x32\x18.cln.DecodeRoutehintListH\x06\x88\x01\x01\x42\x0e\n\x0c_amount_msatB\x0e\n\x0c_descriptionB\x13\n\x11_description_hashB\x11\n\x0f_payment_secretB\x0b\n\t_featuresB\x13\n\x11_payment_metadataB\t\n\x07_routes\"\xd0\x01\n\x12\x44\x65\x63odepayFallbacks\x12\x41\n\titem_type\x18\x01 \x01(\x0e\x32..cln.DecodepayFallbacks.DecodepayFallbacksType\x12\x11\n\x04\x61\x64\x64r\x18\x02 \x01(\tH\x00\x88\x01\x01\x12\x0b\n\x03hex\x18\x03 \x01(\x0c\"N\n\x16\x44\x65\x63odepayFallbacksType\x12\t\n\x05P2PKH\x10\x00\x12\x08\n\x04P2SH\x10\x01\x12\n\n\x06P2WPKH\x10\x02\x12\t\n\x05P2WSH\x10\x03\x12\x08\n\x04P2TR\x10\x04\x42\x07\n\x05_addr\"+\n\x0e\x44\x65\x63odepayExtra\x12\x0b\n\x03tag\x18\x01 \x01(\t\x12\x0c\n\x04\x64\x61ta\x18\x02 \x01(\t\"\x1f\n\rDecodeRequest\x12\x0e\n\x06string\x18\x01 \x01(\t\"\x8c(\n\x0e\x44\x65\x63odeResponse\x12\x31\n\titem_type\x18\x01 \x01(\x0e\x32\x1e.cln.DecodeResponse.DecodeType\x12\r\n\x05valid\x18\x02 \x01(\x08\x12\x15\n\x08offer_id\x18\x03 \x01(\x0cH\x00\x88\x01\x01\x12\x14\n\x0coffer_chains\x18\x04 \x03(\x0c\x12\x1b\n\x0eoffer_metadata\x18\x05 \x01(\x0cH\x01\x88\x01\x01\x12\x1b\n\x0eoffer_currency\x18\x06 \x01(\tH\x02\x88\x01\x01\x12+\n\x1ewarning_unknown_offer_currency\x18\x07 \x01(\tH\x03\x88\x01\x01\x12 \n\x13\x63urrency_minor_unit\x18\x08 \x01(\rH\x04\x88\x01\x01\x12\x19\n\x0coffer_amount\x18\t \x01(\x04H\x05\x88\x01\x01\x12+\n\x11offer_amount_msat\x18\n \x01(\x0b\x32\x0b.cln.AmountH\x06\x88\x01\x01\x12\x1e\n\x11offer_description\x18\x0b \x01(\tH\x07\x88\x01\x01\x12\x19\n\x0coffer_issuer\x18\x0c \x01(\tH\x08\x88\x01\x01\x12\x1b\n\x0eoffer_features\x18\r \x01(\x0cH\t\x88\x01\x01\x12\"\n\x15offer_absolute_expiry\x18\x0e \x01(\x04H\n\x88\x01\x01\x12\x1f\n\x12offer_quantity_max\x18\x0f \x01(\x04H\x0b\x88\x01\x01\x12*\n\x0boffer_paths\x18\x10 \x03(\x0b\x32\x15.cln.DecodeOfferPaths\x12\x1a\n\roffer_node_id\x18\x11 \x01(\x0cH\x0c\x88\x01\x01\x12*\n\x1dwarning_missing_offer_node_id\x18\x14 \x01(\tH\r\x88\x01\x01\x12.\n!warning_invalid_offer_description\x18\x15 \x01(\tH\x0e\x88\x01\x01\x12.\n!warning_missing_offer_description\x18\x16 \x01(\tH\x0f\x88\x01\x01\x12+\n\x1ewarning_invalid_offer_currency\x18\x17 \x01(\tH\x10\x88\x01\x01\x12)\n\x1cwarning_invalid_offer_issuer\x18\x18 \x01(\tH\x11\x88\x01\x01\x12\x1c\n\x0finvreq_metadata\x18\x19 \x01(\x0cH\x12\x88\x01\x01\x12\x1c\n\x0finvreq_payer_id\x18\x1a \x01(\x0cH\x13\x88\x01\x01\x12\x19\n\x0cinvreq_chain\x18\x1b \x01(\x0cH\x14\x88\x01\x01\x12,\n\x12invreq_amount_msat\x18\x1c \x01(\x0b\x32\x0b.cln.AmountH\x15\x88\x01\x01\x12\x1c\n\x0finvreq_features\x18\x1d \x01(\x0cH\x16\x88\x01\x01\x12\x1c\n\x0finvreq_quantity\x18\x1e \x01(\x04H\x17\x88\x01\x01\x12\x1e\n\x11invreq_payer_note\x18\x1f \x01(\tH\x18\x88\x01\x01\x12&\n\x19invreq_recurrence_counter\x18 \x01(\rH\x19\x88\x01\x01\x12$\n\x17invreq_recurrence_start\x18! \x01(\rH\x1a\x88\x01\x01\x12,\n\x1fwarning_missing_invreq_metadata\x18# \x01(\tH\x1b\x88\x01\x01\x12,\n\x1fwarning_missing_invreq_payer_id\x18$ \x01(\tH\x1c\x88\x01\x01\x12.\n!warning_invalid_invreq_payer_note\x18% \x01(\tH\x1d\x88\x01\x01\x12\x36\n)warning_missing_invoice_request_signature\x18& \x01(\tH\x1e\x88\x01\x01\x12\x36\n)warning_invalid_invoice_request_signature\x18\' \x01(\tH\x1f\x88\x01\x01\x12\x1f\n\x12invoice_created_at\x18) \x01(\x04H \x88\x01\x01\x12$\n\x17invoice_relative_expiry\x18* \x01(\rH!\x88\x01\x01\x12!\n\x14invoice_payment_hash\x18+ \x01(\x0cH\"\x88\x01\x01\x12-\n\x13invoice_amount_msat\x18, \x01(\x0b\x32\x0b.cln.AmountH#\x88\x01\x01\x12\x36\n\x11invoice_fallbacks\x18- \x03(\x0b\x32\x1b.cln.DecodeInvoiceFallbacks\x12\x1d\n\x10invoice_features\x18. \x01(\x0cH$\x88\x01\x01\x12\x1c\n\x0finvoice_node_id\x18/ \x01(\x0cH%\x88\x01\x01\x12(\n\x1binvoice_recurrence_basetime\x18\x30 \x01(\x04H&\x88\x01\x01\x12*\n\x1dwarning_missing_invoice_paths\x18\x32 \x01(\tH\'\x88\x01\x01\x12/\n\"warning_missing_invoice_blindedpay\x18\x33 \x01(\tH(\x88\x01\x01\x12/\n\"warning_missing_invoice_created_at\x18\x34 \x01(\tH)\x88\x01\x01\x12\x31\n$warning_missing_invoice_payment_hash\x18\x35 \x01(\tH*\x88\x01\x01\x12+\n\x1ewarning_missing_invoice_amount\x18\x36 \x01(\tH+\x88\x01\x01\x12\x38\n+warning_missing_invoice_recurrence_basetime\x18\x37 \x01(\tH,\x88\x01\x01\x12,\n\x1fwarning_missing_invoice_node_id\x18\x38 \x01(\tH-\x88\x01\x01\x12.\n!warning_missing_invoice_signature\x18\x39 \x01(\tH.\x88\x01\x01\x12.\n!warning_invalid_invoice_signature\x18: \x01(\tH/\x88\x01\x01\x12\'\n\tfallbacks\x18; \x03(\x0b\x32\x14.cln.DecodeFallbacks\x12\x17\n\ncreated_at\x18< \x01(\x04H0\x88\x01\x01\x12\x13\n\x06\x65xpiry\x18= \x01(\x04H1\x88\x01\x01\x12\x12\n\x05payee\x18> \x01(\x0cH2\x88\x01\x01\x12\x19\n\x0cpayment_hash\x18? \x01(\x0cH3\x88\x01\x01\x12\x1d\n\x10\x64\x65scription_hash\x18@ \x01(\x0cH4\x88\x01\x01\x12\"\n\x15min_final_cltv_expiry\x18\x41 \x01(\rH5\x88\x01\x01\x12\x1b\n\x0epayment_secret\x18\x42 \x01(\x0cH6\x88\x01\x01\x12\x1d\n\x10payment_metadata\x18\x43 \x01(\x0cH7\x88\x01\x01\x12\x1f\n\x05\x65xtra\x18\x45 \x03(\x0b\x32\x10.cln.DecodeExtra\x12\x16\n\tunique_id\x18\x46 \x01(\tH8\x88\x01\x01\x12\x14\n\x07version\x18G \x01(\tH9\x88\x01\x01\x12\x13\n\x06string\x18H \x01(\tH:\x88\x01\x01\x12-\n\x0crestrictions\x18I \x03(\x0b\x32\x17.cln.DecodeRestrictions\x12&\n\x19warning_rune_invalid_utf8\x18J \x01(\tH;\x88\x01\x01\x12\x10\n\x03hex\x18K \x01(\x0cH<\x88\x01\x01\x12\x16\n\tdecrypted\x18L \x01(\x0cH=\x88\x01\x01\x12\x16\n\tsignature\x18M \x01(\tH>\x88\x01\x01\x12\x15\n\x08\x63urrency\x18N \x01(\tH?\x88\x01\x01\x12%\n\x0b\x61mount_msat\x18O \x01(\x0b\x32\x0b.cln.AmountH@\x88\x01\x01\x12\x18\n\x0b\x64\x65scription\x18P \x01(\tHA\x88\x01\x01\x12\x15\n\x08\x66\x65\x61tures\x18Q \x01(\x0cHB\x88\x01\x01\x12-\n\x06routes\x18R \x01(\x0b\x32\x18.cln.DecodeRoutehintListHC\x88\x01\x01\x12\x1c\n\x0foffer_issuer_id\x18S \x01(\x0cHD\x88\x01\x01\x12,\n\x1fwarning_missing_offer_issuer_id\x18T \x01(\tHE\x88\x01\x01\x12,\n\x0cinvreq_paths\x18U \x03(\x0b\x32\x16.cln.DecodeInvreqPaths\x12\'\n\x1awarning_empty_blinded_path\x18V \x01(\tHF\x88\x01\x01\x12=\n\x13invreq_bip_353_name\x18W \x01(\x0b\x32\x1b.cln.DecodeInvreqBip353NameHG\x88\x01\x01\x12\x35\n(warning_invreq_bip_353_name_name_invalid\x18X \x01(\tHH\x88\x01\x01\x12\x37\n*warning_invreq_bip_353_name_domain_invalid\x18Y \x01(\tHI\x88\x01\x01\"\x83\x01\n\nDecodeType\x12\x10\n\x0c\x42OLT12_OFFER\x10\x00\x12\x12\n\x0e\x42OLT12_INVOICE\x10\x01\x12\x1a\n\x16\x42OLT12_INVOICE_REQUEST\x10\x02\x12\x12\n\x0e\x42OLT11_INVOICE\x10\x03\x12\x08\n\x04RUNE\x10\x04\x12\x15\n\x11\x45MERGENCY_RECOVER\x10\x05\x42\x0b\n\t_offer_idB\x11\n\x0f_offer_metadataB\x11\n\x0f_offer_currencyB!\n\x1f_warning_unknown_offer_currencyB\x16\n\x14_currency_minor_unitB\x0f\n\r_offer_amountB\x14\n\x12_offer_amount_msatB\x14\n\x12_offer_descriptionB\x0f\n\r_offer_issuerB\x11\n\x0f_offer_featuresB\x18\n\x16_offer_absolute_expiryB\x15\n\x13_offer_quantity_maxB\x10\n\x0e_offer_node_idB \n\x1e_warning_missing_offer_node_idB$\n\"_warning_invalid_offer_descriptionB$\n\"_warning_missing_offer_descriptionB!\n\x1f_warning_invalid_offer_currencyB\x1f\n\x1d_warning_invalid_offer_issuerB\x12\n\x10_invreq_metadataB\x12\n\x10_invreq_payer_idB\x0f\n\r_invreq_chainB\x15\n\x13_invreq_amount_msatB\x12\n\x10_invreq_featuresB\x12\n\x10_invreq_quantityB\x14\n\x12_invreq_payer_noteB\x1c\n\x1a_invreq_recurrence_counterB\x1a\n\x18_invreq_recurrence_startB\"\n _warning_missing_invreq_metadataB\"\n _warning_missing_invreq_payer_idB$\n\"_warning_invalid_invreq_payer_noteB,\n*_warning_missing_invoice_request_signatureB,\n*_warning_invalid_invoice_request_signatureB\x15\n\x13_invoice_created_atB\x1a\n\x18_invoice_relative_expiryB\x17\n\x15_invoice_payment_hashB\x16\n\x14_invoice_amount_msatB\x13\n\x11_invoice_featuresB\x12\n\x10_invoice_node_idB\x1e\n\x1c_invoice_recurrence_basetimeB \n\x1e_warning_missing_invoice_pathsB%\n#_warning_missing_invoice_blindedpayB%\n#_warning_missing_invoice_created_atB\'\n%_warning_missing_invoice_payment_hashB!\n\x1f_warning_missing_invoice_amountB.\n,_warning_missing_invoice_recurrence_basetimeB\"\n _warning_missing_invoice_node_idB$\n\"_warning_missing_invoice_signatureB$\n\"_warning_invalid_invoice_signatureB\r\n\x0b_created_atB\t\n\x07_expiryB\x08\n\x06_payeeB\x0f\n\r_payment_hashB\x13\n\x11_description_hashB\x18\n\x16_min_final_cltv_expiryB\x11\n\x0f_payment_secretB\x13\n\x11_payment_metadataB\x0c\n\n_unique_idB\n\n\x08_versionB\t\n\x07_stringB\x1c\n\x1a_warning_rune_invalid_utf8B\x06\n\x04_hexB\x0c\n\n_decryptedB\x0c\n\n_signatureB\x0b\n\t_currencyB\x0e\n\x0c_amount_msatB\x0e\n\x0c_descriptionB\x0b\n\t_featuresB\t\n\x07_routesB\x12\n\x10_offer_issuer_idB\"\n _warning_missing_offer_issuer_idB\x1d\n\x1b_warning_empty_blinded_pathB\x16\n\x14_invreq_bip_353_nameB+\n)_warning_invreq_bip_353_name_name_invalidB-\n+_warning_invreq_bip_353_name_domain_invalid\"\xec\x01\n\x10\x44\x65\x63odeOfferPaths\x12\x1a\n\rfirst_node_id\x18\x01 \x01(\x0cH\x00\x88\x01\x01\x12\x15\n\x08\x62linding\x18\x02 \x01(\x0cH\x01\x88\x01\x01\x12\x1b\n\x0e\x66irst_scid_dir\x18\x04 \x01(\rH\x02\x88\x01\x01\x12\x17\n\nfirst_scid\x18\x05 \x01(\tH\x03\x88\x01\x01\x12\x1b\n\x0e\x66irst_path_key\x18\x06 \x01(\x0cH\x04\x88\x01\x01\x42\x10\n\x0e_first_node_idB\x0b\n\t_blindingB\x11\n\x0f_first_scid_dirB\r\n\x0b_first_scidB\x11\n\x0f_first_path_key\"\x89\x01\n\x1e\x44\x65\x63odeOfferRecurrencePaywindow\x12\x16\n\x0eseconds_before\x18\x01 \x01(\r\x12\x15\n\rseconds_after\x18\x02 \x01(\r\x12 \n\x13proportional_amount\x18\x03 \x01(\x08H\x00\x88\x01\x01\x42\x16\n\x14_proportional_amount\"\x97\x02\n\x11\x44\x65\x63odeInvreqPaths\x12\x1b\n\x0e\x66irst_scid_dir\x18\x01 \x01(\rH\x00\x88\x01\x01\x12\x15\n\x08\x62linding\x18\x02 \x01(\x0cH\x01\x88\x01\x01\x12\x1a\n\rfirst_node_id\x18\x03 \x01(\x0cH\x02\x88\x01\x01\x12\x17\n\nfirst_scid\x18\x04 \x01(\tH\x03\x88\x01\x01\x12(\n\x04path\x18\x05 \x03(\x0b\x32\x1a.cln.DecodeInvreqPathsPath\x12\x1b\n\x0e\x66irst_path_key\x18\x06 \x01(\x0cH\x04\x88\x01\x01\x42\x11\n\x0f_first_scid_dirB\x0b\n\t_blindingB\x10\n\x0e_first_node_idB\r\n\x0b_first_scidB\x11\n\x0f_first_path_key\"R\n\x15\x44\x65\x63odeInvreqPathsPath\x12\x17\n\x0f\x62linded_node_id\x18\x01 \x01(\x0c\x12 \n\x18\x65ncrypted_recipient_data\x18\x02 \x01(\x0c\"T\n\x16\x44\x65\x63odeInvreqBip353Name\x12\x11\n\x04name\x18\x01 \x01(\tH\x00\x88\x01\x01\x12\x13\n\x06\x64omain\x18\x02 \x01(\tH\x01\x88\x01\x01\x42\x07\n\x05_nameB\t\n\x07_domain\"S\n\x16\x44\x65\x63odeInvoicePathsPath\x12\x17\n\x0f\x62linded_node_id\x18\x01 \x01(\x0c\x12 \n\x18\x65ncrypted_recipient_data\x18\x02 \x01(\x0c\"X\n\x16\x44\x65\x63odeInvoiceFallbacks\x12\x0f\n\x07version\x18\x01 \x01(\r\x12\x0b\n\x03hex\x18\x02 \x01(\x0c\x12\x14\n\x07\x61\x64\x64ress\x18\x03 \x01(\tH\x00\x88\x01\x01\x42\n\n\x08_address\"\xaa\x02\n\x0f\x44\x65\x63odeFallbacks\x12\x36\n)warning_invoice_fallbacks_version_invalid\x18\x01 \x01(\tH\x00\x88\x01\x01\x12;\n\titem_type\x18\x02 \x01(\x0e\x32(.cln.DecodeFallbacks.DecodeFallbacksType\x12\x11\n\x04\x61\x64\x64r\x18\x03 \x01(\tH\x01\x88\x01\x01\x12\x0b\n\x03hex\x18\x04 \x01(\x0c\"K\n\x13\x44\x65\x63odeFallbacksType\x12\t\n\x05P2PKH\x10\x00\x12\x08\n\x04P2SH\x10\x01\x12\n\n\x06P2WPKH\x10\x02\x12\t\n\x05P2WSH\x10\x03\x12\x08\n\x04P2TR\x10\x04\x42,\n*_warning_invoice_fallbacks_version_invalidB\x07\n\x05_addr\"(\n\x0b\x44\x65\x63odeExtra\x12\x0b\n\x03tag\x18\x01 \x01(\t\x12\x0c\n\x04\x64\x61ta\x18\x02 \x01(\t\";\n\x12\x44\x65\x63odeRestrictions\x12\x14\n\x0c\x61lternatives\x18\x01 \x03(\t\x12\x0f\n\x07summary\x18\x02 \x01(\t\"\xc2\x01\n\rDelpayRequest\x12\x14\n\x0cpayment_hash\x18\x01 \x01(\x0c\x12/\n\x06status\x18\x02 \x01(\x0e\x32\x1f.cln.DelpayRequest.DelpayStatus\x12\x13\n\x06partid\x18\x03 \x01(\x04H\x00\x88\x01\x01\x12\x14\n\x07groupid\x18\x04 \x01(\x04H\x01\x88\x01\x01\"(\n\x0c\x44\x65lpayStatus\x12\x0c\n\x08\x43OMPLETE\x10\x00\x12\n\n\x06\x46\x41ILED\x10\x01\x42\t\n\x07_partidB\n\n\x08_groupid\"7\n\x0e\x44\x65lpayResponse\x12%\n\x08payments\x18\x01 \x03(\x0b\x32\x13.cln.DelpayPayments\"\xcb\x05\n\x0e\x44\x65lpayPayments\x12\x1a\n\rcreated_index\x18\x01 \x01(\x04H\x00\x88\x01\x01\x12\n\n\x02id\x18\x02 \x01(\x04\x12\x14\n\x0cpayment_hash\x18\x03 \x01(\x0c\x12\x38\n\x06status\x18\x04 \x01(\x0e\x32(.cln.DelpayPayments.DelpayPaymentsStatus\x12%\n\x10\x61mount_sent_msat\x18\x05 \x01(\x0b\x32\x0b.cln.Amount\x12\x13\n\x06partid\x18\x06 \x01(\x04H\x01\x88\x01\x01\x12\x18\n\x0b\x64\x65stination\x18\x07 \x01(\x0cH\x02\x88\x01\x01\x12%\n\x0b\x61mount_msat\x18\x08 \x01(\x0b\x32\x0b.cln.AmountH\x03\x88\x01\x01\x12\x12\n\ncreated_at\x18\t \x01(\x04\x12\x1a\n\rupdated_index\x18\n \x01(\x04H\x04\x88\x01\x01\x12\x19\n\x0c\x63ompleted_at\x18\x0b \x01(\x04H\x05\x88\x01\x01\x12\x14\n\x07groupid\x18\x0c \x01(\x04H\x06\x88\x01\x01\x12\x1d\n\x10payment_preimage\x18\r \x01(\x0cH\x07\x88\x01\x01\x12\x12\n\x05label\x18\x0e \x01(\tH\x08\x88\x01\x01\x12\x13\n\x06\x62olt11\x18\x0f \x01(\tH\t\x88\x01\x01\x12\x13\n\x06\x62olt12\x18\x10 \x01(\tH\n\x88\x01\x01\x12\x17\n\nerroronion\x18\x11 \x01(\x0cH\x0b\x88\x01\x01\"=\n\x14\x44\x65lpayPaymentsStatus\x12\x0b\n\x07PENDING\x10\x00\x12\n\n\x06\x46\x41ILED\x10\x01\x12\x0c\n\x08\x43OMPLETE\x10\x02\x42\x10\n\x0e_created_indexB\t\n\x07_partidB\x0e\n\x0c_destinationB\x0e\n\x0c_amount_msatB\x10\n\x0e_updated_indexB\x0f\n\r_completed_atB\n\n\x08_groupidB\x13\n\x11_payment_preimageB\x08\n\x06_labelB\t\n\x07_bolt11B\t\n\x07_bolt12B\r\n\x0b_erroronion\"\xb3\x01\n\x11\x44\x65lforwardRequest\x12\x12\n\nin_channel\x18\x01 \x01(\t\x12\x12\n\nin_htlc_id\x18\x02 \x01(\x04\x12\x37\n\x06status\x18\x03 \x01(\x0e\x32\'.cln.DelforwardRequest.DelforwardStatus\"=\n\x10\x44\x65lforwardStatus\x12\x0b\n\x07SETTLED\x10\x00\x12\x10\n\x0cLOCAL_FAILED\x10\x01\x12\n\n\x06\x46\x41ILED\x10\x02\"\x14\n\x12\x44\x65lforwardResponse\"\'\n\x13\x44isableofferRequest\x12\x10\n\x08offer_id\x18\x01 \x01(\x0c\"\x88\x01\n\x14\x44isableofferResponse\x12\x10\n\x08offer_id\x18\x01 \x01(\x0c\x12\x0e\n\x06\x61\x63tive\x18\x02 \x01(\x08\x12\x12\n\nsingle_use\x18\x03 \x01(\x08\x12\x0e\n\x06\x62olt12\x18\x04 \x01(\t\x12\x0c\n\x04used\x18\x05 \x01(\x08\x12\x12\n\x05label\x18\x06 \x01(\tH\x00\x88\x01\x01\x42\x08\n\x06_label\"&\n\x12\x45nableofferRequest\x12\x10\n\x08offer_id\x18\x01 \x01(\x0c\"\x87\x01\n\x13\x45nableofferResponse\x12\x10\n\x08offer_id\x18\x01 \x01(\x0c\x12\x0e\n\x06\x61\x63tive\x18\x02 \x01(\x08\x12\x12\n\nsingle_use\x18\x03 \x01(\x08\x12\x0e\n\x06\x62olt12\x18\x04 \x01(\t\x12\x0c\n\x04used\x18\x05 \x01(\x08\x12\x12\n\x05label\x18\x06 \x01(\tH\x00\x88\x01\x01\x42\x08\n\x06_label\"=\n\x11\x44isconnectRequest\x12\n\n\x02id\x18\x01 \x01(\x0c\x12\x12\n\x05\x66orce\x18\x02 \x01(\x08H\x00\x88\x01\x01\x42\x08\n\x06_force\"\x14\n\x12\x44isconnectResponse\"k\n\x0f\x46\x65\x65ratesRequest\x12\x31\n\x05style\x18\x01 \x01(\x0e\x32\".cln.FeeratesRequest.FeeratesStyle\"%\n\rFeeratesStyle\x12\t\n\x05PERKB\x10\x00\x12\t\n\x05PERKW\x10\x01\"\x9a\x02\n\x10\x46\x65\x65ratesResponse\x12%\n\x18warning_missing_feerates\x18\x01 \x01(\tH\x00\x88\x01\x01\x12&\n\x05perkb\x18\x02 \x01(\x0b\x32\x12.cln.FeeratesPerkbH\x01\x88\x01\x01\x12&\n\x05perkw\x18\x03 \x01(\x0b\x32\x12.cln.FeeratesPerkwH\x02\x88\x01\x01\x12\x44\n\x15onchain_fee_estimates\x18\x04 \x01(\x0b\x32 .cln.FeeratesOnchainFeeEstimatesH\x03\x88\x01\x01\x42\x1b\n\x19_warning_missing_feeratesB\x08\n\x06_perkbB\x08\n\x06_perkwB\x18\n\x16_onchain_fee_estimates\"\xd3\x03\n\rFeeratesPerkb\x12\x16\n\x0emin_acceptable\x18\x01 \x01(\r\x12\x16\n\x0emax_acceptable\x18\x02 \x01(\r\x12\x14\n\x07opening\x18\x03 \x01(\rH\x00\x88\x01\x01\x12\x19\n\x0cmutual_close\x18\x04 \x01(\rH\x01\x88\x01\x01\x12\x1d\n\x10unilateral_close\x18\x05 \x01(\rH\x02\x88\x01\x01\x12\x1a\n\rdelayed_to_us\x18\x06 \x01(\rH\x03\x88\x01\x01\x12\x1c\n\x0fhtlc_resolution\x18\x07 \x01(\rH\x04\x88\x01\x01\x12\x14\n\x07penalty\x18\x08 \x01(\rH\x05\x88\x01\x01\x12.\n\testimates\x18\t \x03(\x0b\x32\x1b.cln.FeeratesPerkbEstimates\x12\x12\n\x05\x66loor\x18\n \x01(\rH\x06\x88\x01\x01\x12$\n\x17unilateral_anchor_close\x18\x0b \x01(\rH\x07\x88\x01\x01\x42\n\n\x08_openingB\x0f\n\r_mutual_closeB\x13\n\x11_unilateral_closeB\x10\n\x0e_delayed_to_usB\x12\n\x10_htlc_resolutionB\n\n\x08_penaltyB\x08\n\x06_floorB\x1a\n\x18_unilateral_anchor_close\"W\n\x16\x46\x65\x65ratesPerkbEstimates\x12\x12\n\nblockcount\x18\x01 \x01(\r\x12\x0f\n\x07\x66\x65\x65rate\x18\x02 \x01(\r\x12\x18\n\x10smoothed_feerate\x18\x03 \x01(\r\"\xd3\x03\n\rFeeratesPerkw\x12\x16\n\x0emin_acceptable\x18\x01 \x01(\r\x12\x16\n\x0emax_acceptable\x18\x02 \x01(\r\x12\x14\n\x07opening\x18\x03 \x01(\rH\x00\x88\x01\x01\x12\x19\n\x0cmutual_close\x18\x04 \x01(\rH\x01\x88\x01\x01\x12\x1d\n\x10unilateral_close\x18\x05 \x01(\rH\x02\x88\x01\x01\x12\x1a\n\rdelayed_to_us\x18\x06 \x01(\rH\x03\x88\x01\x01\x12\x1c\n\x0fhtlc_resolution\x18\x07 \x01(\rH\x04\x88\x01\x01\x12\x14\n\x07penalty\x18\x08 \x01(\rH\x05\x88\x01\x01\x12.\n\testimates\x18\t \x03(\x0b\x32\x1b.cln.FeeratesPerkwEstimates\x12\x12\n\x05\x66loor\x18\n \x01(\rH\x06\x88\x01\x01\x12$\n\x17unilateral_anchor_close\x18\x0b \x01(\rH\x07\x88\x01\x01\x42\n\n\x08_openingB\x0f\n\r_mutual_closeB\x13\n\x11_unilateral_closeB\x10\n\x0e_delayed_to_usB\x12\n\x10_htlc_resolutionB\n\n\x08_penaltyB\x08\n\x06_floorB\x1a\n\x18_unilateral_anchor_close\"W\n\x16\x46\x65\x65ratesPerkwEstimates\x12\x12\n\nblockcount\x18\x01 \x01(\r\x12\x0f\n\x07\x66\x65\x65rate\x18\x02 \x01(\r\x12\x18\n\x10smoothed_feerate\x18\x03 \x01(\r\"\x99\x02\n\x1b\x46\x65\x65ratesOnchainFeeEstimates\x12 \n\x18opening_channel_satoshis\x18\x01 \x01(\x04\x12\x1d\n\x15mutual_close_satoshis\x18\x02 \x01(\x04\x12!\n\x19unilateral_close_satoshis\x18\x03 \x01(\x04\x12\x1d\n\x15htlc_timeout_satoshis\x18\x04 \x01(\x04\x12\x1d\n\x15htlc_success_satoshis\x18\x05 \x01(\x04\x12\x30\n#unilateral_close_nonanchor_satoshis\x18\x06 \x01(\x04H\x00\x88\x01\x01\x42&\n$_unilateral_close_nonanchor_satoshis\"%\n\x12\x46\x65tchbip353Request\x12\x0f\n\x07\x61\x64\x64ress\x18\x01 \x01(\t\"X\n\x13\x46\x65tchbip353Response\x12\r\n\x05proof\x18\x01 \x01(\t\x12\x32\n\x0cinstructions\x18\x02 \x03(\x0b\x32\x1c.cln.Fetchbip353Instructions\"\xf7\x01\n\x17\x46\x65tchbip353Instructions\x12\x18\n\x0b\x64\x65scription\x18\x01 \x01(\tH\x00\x88\x01\x01\x12\x12\n\x05offer\x18\x02 \x01(\tH\x01\x88\x01\x01\x12\x14\n\x07onchain\x18\x03 \x01(\tH\x02\x88\x01\x01\x12!\n\x14offchain_amount_msat\x18\x04 \x01(\x04H\x03\x88\x01\x01\x12\x1f\n\x12onchain_amount_sat\x18\x05 \x01(\x04H\x04\x88\x01\x01\x42\x0e\n\x0c_descriptionB\x08\n\x06_offerB\n\n\x08_onchainB\x17\n\x15_offchain_amount_msatB\x15\n\x13_onchain_amount_sat\"\xb9\x03\n\x13\x46\x65tchinvoiceRequest\x12\r\n\x05offer\x18\x01 \x01(\t\x12%\n\x0b\x61mount_msat\x18\x02 \x01(\x0b\x32\x0b.cln.AmountH\x00\x88\x01\x01\x12\x15\n\x08quantity\x18\x03 \x01(\x04H\x01\x88\x01\x01\x12\x1f\n\x12recurrence_counter\x18\x04 \x01(\x04H\x02\x88\x01\x01\x12\x1d\n\x10recurrence_start\x18\x05 \x01(\x01H\x03\x88\x01\x01\x12\x1d\n\x10recurrence_label\x18\x06 \x01(\tH\x04\x88\x01\x01\x12\x14\n\x07timeout\x18\x07 \x01(\x01H\x05\x88\x01\x01\x12\x17\n\npayer_note\x18\x08 \x01(\tH\x06\x88\x01\x01\x12\x1b\n\x0epayer_metadata\x18\t \x01(\tH\x07\x88\x01\x01\x12\x13\n\x06\x62ip353\x18\n \x01(\tH\x08\x88\x01\x01\x42\x0e\n\x0c_amount_msatB\x0b\n\t_quantityB\x15\n\x13_recurrence_counterB\x13\n\x11_recurrence_startB\x13\n\x11_recurrence_labelB\n\n\x08_timeoutB\r\n\x0b_payer_noteB\x11\n\x0f_payer_metadataB\t\n\x07_bip353\"\x99\x01\n\x14\x46\x65tchinvoiceResponse\x12\x0f\n\x07invoice\x18\x01 \x01(\t\x12)\n\x07\x63hanges\x18\x02 \x01(\x0b\x32\x18.cln.FetchinvoiceChanges\x12\x35\n\x0bnext_period\x18\x03 \x01(\x0b\x32\x1b.cln.FetchinvoiceNextPeriodH\x00\x88\x01\x01\x42\x0e\n\x0c_next_period\"\x82\x02\n\x13\x46\x65tchinvoiceChanges\x12!\n\x14\x64\x65scription_appended\x18\x01 \x01(\tH\x00\x88\x01\x01\x12\x18\n\x0b\x64\x65scription\x18\x02 \x01(\tH\x01\x88\x01\x01\x12\x1b\n\x0evendor_removed\x18\x03 \x01(\tH\x02\x88\x01\x01\x12\x13\n\x06vendor\x18\x04 \x01(\tH\x03\x88\x01\x01\x12%\n\x0b\x61mount_msat\x18\x05 \x01(\x0b\x32\x0b.cln.AmountH\x04\x88\x01\x01\x42\x17\n\x15_description_appendedB\x0e\n\x0c_descriptionB\x11\n\x0f_vendor_removedB\t\n\x07_vendorB\x0e\n\x0c_amount_msat\"}\n\x16\x46\x65tchinvoiceNextPeriod\x12\x0f\n\x07\x63ounter\x18\x01 \x01(\x04\x12\x11\n\tstarttime\x18\x02 \x01(\x04\x12\x0f\n\x07\x65ndtime\x18\x03 \x01(\x04\x12\x17\n\x0fpaywindow_start\x18\x04 \x01(\x04\x12\x15\n\rpaywindow_end\x18\x05 \x01(\x04\"&\n\x18\x46undchannelCancelRequest\x12\n\n\x02id\x18\x01 \x01(\x0c\".\n\x19\x46undchannelCancelResponse\x12\x11\n\tcancelled\x18\x01 \x01(\t\"6\n\x1a\x46undchannelCompleteRequest\x12\n\n\x02id\x18\x01 \x01(\x0c\x12\x0c\n\x04psbt\x18\x02 \x01(\t\"N\n\x1b\x46undchannelCompleteResponse\x12\x12\n\nchannel_id\x18\x01 \x01(\x0c\x12\x1b\n\x13\x63ommitments_secured\x18\x02 \x01(\x08\"\xfb\x03\n\x12\x46undchannelRequest\x12 \n\x06\x61mount\x18\x01 \x01(\x0b\x32\x10.cln.AmountOrAll\x12\"\n\x07\x66\x65\x65rate\x18\x02 \x01(\x0b\x32\x0c.cln.FeerateH\x00\x88\x01\x01\x12\x15\n\x08\x61nnounce\x18\x03 \x01(\x08H\x01\x88\x01\x01\x12#\n\tpush_msat\x18\x05 \x01(\x0b\x32\x0b.cln.AmountH\x02\x88\x01\x01\x12\x15\n\x08\x63lose_to\x18\x06 \x01(\tH\x03\x88\x01\x01\x12%\n\x0brequest_amt\x18\x07 \x01(\x0b\x32\x0b.cln.AmountH\x04\x88\x01\x01\x12\x1a\n\rcompact_lease\x18\x08 \x01(\tH\x05\x88\x01\x01\x12\n\n\x02id\x18\t \x01(\x0c\x12\x14\n\x07minconf\x18\n \x01(\rH\x06\x88\x01\x01\x12\x1c\n\x05utxos\x18\x0b \x03(\x0b\x32\r.cln.Outpoint\x12\x15\n\x08mindepth\x18\x0c \x01(\rH\x07\x88\x01\x01\x12!\n\x07reserve\x18\r \x01(\x0b\x32\x0b.cln.AmountH\x08\x88\x01\x01\x12\x14\n\x0c\x63hannel_type\x18\x0e \x03(\rB\n\n\x08_feerateB\x0b\n\t_announceB\x0c\n\n_push_msatB\x0b\n\t_close_toB\x0e\n\x0c_request_amtB\x10\n\x0e_compact_leaseB\n\n\x08_minconfB\x0b\n\t_mindepthB\n\n\x08_reserve\"\xe4\x01\n\x13\x46undchannelResponse\x12\n\n\x02tx\x18\x01 \x01(\x0c\x12\x0c\n\x04txid\x18\x02 \x01(\x0c\x12\x0e\n\x06outnum\x18\x03 \x01(\r\x12\x12\n\nchannel_id\x18\x04 \x01(\x0c\x12\x15\n\x08\x63lose_to\x18\x05 \x01(\x0cH\x00\x88\x01\x01\x12\x15\n\x08mindepth\x18\x06 \x01(\rH\x01\x88\x01\x01\x12\x36\n\x0c\x63hannel_type\x18\x07 \x01(\x0b\x32\x1b.cln.FundchannelChannelTypeH\x02\x88\x01\x01\x42\x0b\n\t_close_toB\x0b\n\t_mindepthB\x0f\n\r_channel_type\"K\n\x16\x46undchannelChannelType\x12\x0c\n\x04\x62its\x18\x01 \x03(\r\x12#\n\x05names\x18\x02 \x03(\x0e\x32\x14.cln.ChannelTypeName\"\xd6\x02\n\x17\x46undchannelStartRequest\x12\n\n\x02id\x18\x01 \x01(\x0c\x12\x1b\n\x06\x61mount\x18\x02 \x01(\x0b\x32\x0b.cln.Amount\x12\"\n\x07\x66\x65\x65rate\x18\x03 \x01(\x0b\x32\x0c.cln.FeerateH\x00\x88\x01\x01\x12\x15\n\x08\x61nnounce\x18\x04 \x01(\x08H\x01\x88\x01\x01\x12\x15\n\x08\x63lose_to\x18\x05 \x01(\tH\x02\x88\x01\x01\x12#\n\tpush_msat\x18\x06 \x01(\x0b\x32\x0b.cln.AmountH\x03\x88\x01\x01\x12\x15\n\x08mindepth\x18\x07 \x01(\rH\x04\x88\x01\x01\x12!\n\x07reserve\x18\x08 \x01(\x0b\x32\x0b.cln.AmountH\x05\x88\x01\x01\x12\x14\n\x0c\x63hannel_type\x18\t \x03(\rB\n\n\x08_feerateB\x0b\n\t_announceB\x0b\n\t_close_toB\x0c\n\n_push_msatB\x0b\n\t_mindepthB\n\n\x08_reserve\"\xf6\x01\n\x18\x46undchannelStartResponse\x12\x17\n\x0f\x66unding_address\x18\x01 \x01(\t\x12\x14\n\x0cscriptpubkey\x18\x02 \x01(\x0c\x12;\n\x0c\x63hannel_type\x18\x03 \x01(\x0b\x32 .cln.FundchannelStartChannelTypeH\x00\x88\x01\x01\x12\x15\n\x08\x63lose_to\x18\x04 \x01(\x0cH\x01\x88\x01\x01\x12\x15\n\rwarning_usage\x18\x05 \x01(\t\x12\x15\n\x08mindepth\x18\x06 \x01(\rH\x02\x88\x01\x01\x42\x0f\n\r_channel_typeB\x0b\n\t_close_toB\x0b\n\t_mindepth\"P\n\x1b\x46undchannelStartChannelType\x12\x0c\n\x04\x62its\x18\x01 \x03(\r\x12#\n\x05names\x18\x02 \x03(\x0e\x32\x14.cln.ChannelTypeName\"\x9d\x01\n\rGetlogRequest\x12\x32\n\x05level\x18\x01 \x01(\x0e\x32\x1e.cln.GetlogRequest.GetlogLevelH\x00\x88\x01\x01\"N\n\x0bGetlogLevel\x12\n\n\x06\x42ROKEN\x10\x00\x12\x0b\n\x07UNUSUAL\x10\x01\x12\x08\n\x04INFO\x10\x02\x12\t\n\x05\x44\x45\x42UG\x10\x03\x12\x06\n\x02IO\x10\x04\x12\t\n\x05TRACE\x10\x05\x42\x08\n\x06_level\"h\n\x0eGetlogResponse\x12\x12\n\ncreated_at\x18\x01 \x01(\t\x12\x12\n\nbytes_used\x18\x02 \x01(\r\x12\x11\n\tbytes_max\x18\x03 \x01(\r\x12\x1b\n\x03log\x18\x04 \x03(\x0b\x32\x0e.cln.GetlogLog\"\xe8\x02\n\tGetlogLog\x12/\n\titem_type\x18\x01 \x01(\x0e\x32\x1c.cln.GetlogLog.GetlogLogType\x12\x18\n\x0bnum_skipped\x18\x02 \x01(\rH\x00\x88\x01\x01\x12\x11\n\x04time\x18\x03 \x01(\tH\x01\x88\x01\x01\x12\x13\n\x06source\x18\x04 \x01(\tH\x02\x88\x01\x01\x12\x10\n\x03log\x18\x05 \x01(\tH\x03\x88\x01\x01\x12\x14\n\x07node_id\x18\x06 \x01(\x0cH\x04\x88\x01\x01\x12\x11\n\x04\x64\x61ta\x18\x07 \x01(\x0cH\x05\x88\x01\x01\"l\n\rGetlogLogType\x12\x0b\n\x07SKIPPED\x10\x00\x12\n\n\x06\x42ROKEN\x10\x01\x12\x0b\n\x07UNUSUAL\x10\x02\x12\x08\n\x04INFO\x10\x03\x12\t\n\x05\x44\x45\x42UG\x10\x04\x12\t\n\x05IO_IN\x10\x05\x12\n\n\x06IO_OUT\x10\x06\x12\t\n\x05TRACE\x10\x07\x42\x0e\n\x0c_num_skippedB\x07\n\x05_timeB\t\n\x07_sourceB\x06\n\x04_logB\n\n\x08_node_idB\x07\n\x05_data\"\xd9\x08\n\x13\x46underupdateRequest\x12@\n\x06policy\x18\x01 \x01(\x0e\x32+.cln.FunderupdateRequest.FunderupdatePolicyH\x00\x88\x01\x01\x12$\n\npolicy_mod\x18\x02 \x01(\x0b\x32\x0b.cln.AmountH\x01\x88\x01\x01\x12\x18\n\x0bleases_only\x18\x03 \x01(\x08H\x02\x88\x01\x01\x12\x30\n\x16min_their_funding_msat\x18\x04 \x01(\x0b\x32\x0b.cln.AmountH\x03\x88\x01\x01\x12\x30\n\x16max_their_funding_msat\x18\x05 \x01(\x0b\x32\x0b.cln.AmountH\x04\x88\x01\x01\x12.\n\x14per_channel_min_msat\x18\x06 \x01(\x0b\x32\x0b.cln.AmountH\x05\x88\x01\x01\x12.\n\x14per_channel_max_msat\x18\x07 \x01(\x0b\x32\x0b.cln.AmountH\x06\x88\x01\x01\x12+\n\x11reserve_tank_msat\x18\x08 \x01(\x0b\x32\x0b.cln.AmountH\x07\x88\x01\x01\x12\x19\n\x0c\x66uzz_percent\x18\t \x01(\rH\x08\x88\x01\x01\x12\x1d\n\x10\x66und_probability\x18\n \x01(\rH\t\x88\x01\x01\x12-\n\x13lease_fee_base_msat\x18\x0b \x01(\x0b\x32\x0b.cln.AmountH\n\x88\x01\x01\x12\x1c\n\x0flease_fee_basis\x18\x0c \x01(\rH\x0b\x88\x01\x01\x12\x1b\n\x0e\x66unding_weight\x18\r \x01(\rH\x0c\x88\x01\x01\x12\x33\n\x19\x63hannel_fee_max_base_msat\x18\x0e \x01(\x0b\x32\x0b.cln.AmountH\r\x88\x01\x01\x12\x35\n(channel_fee_max_proportional_thousandths\x18\x0f \x01(\rH\x0e\x88\x01\x01\x12\x1a\n\rcompact_lease\x18\x10 \x01(\x0cH\x0f\x88\x01\x01\"9\n\x12\x46underupdatePolicy\x12\t\n\x05MATCH\x10\x00\x12\r\n\tAVAILABLE\x10\x01\x12\t\n\x05\x46IXED\x10\x02\x42\t\n\x07_policyB\r\n\x0b_policy_modB\x0e\n\x0c_leases_onlyB\x19\n\x17_min_their_funding_msatB\x19\n\x17_max_their_funding_msatB\x17\n\x15_per_channel_min_msatB\x17\n\x15_per_channel_max_msatB\x14\n\x12_reserve_tank_msatB\x0f\n\r_fuzz_percentB\x13\n\x11_fund_probabilityB\x16\n\x14_lease_fee_base_msatB\x12\n\x10_lease_fee_basisB\x11\n\x0f_funding_weightB\x1c\n\x1a_channel_fee_max_base_msatB+\n)_channel_fee_max_proportional_thousandthsB\x10\n\x0e_compact_lease\"\xdf\x06\n\x14\x46underupdateResponse\x12\x0f\n\x07summary\x18\x01 \x01(\t\x12<\n\x06policy\x18\x02 \x01(\x0e\x32,.cln.FunderupdateResponse.FunderupdatePolicy\x12\x12\n\npolicy_mod\x18\x03 \x01(\r\x12\x13\n\x0bleases_only\x18\x04 \x01(\x08\x12+\n\x16min_their_funding_msat\x18\x05 \x01(\x0b\x32\x0b.cln.Amount\x12+\n\x16max_their_funding_msat\x18\x06 \x01(\x0b\x32\x0b.cln.Amount\x12)\n\x14per_channel_min_msat\x18\x07 \x01(\x0b\x32\x0b.cln.Amount\x12)\n\x14per_channel_max_msat\x18\x08 \x01(\x0b\x32\x0b.cln.Amount\x12&\n\x11reserve_tank_msat\x18\t \x01(\x0b\x32\x0b.cln.Amount\x12\x14\n\x0c\x66uzz_percent\x18\n \x01(\r\x12\x18\n\x10\x66und_probability\x18\x0b \x01(\r\x12-\n\x13lease_fee_base_msat\x18\x0c \x01(\x0b\x32\x0b.cln.AmountH\x00\x88\x01\x01\x12\x1c\n\x0flease_fee_basis\x18\r \x01(\rH\x01\x88\x01\x01\x12\x1b\n\x0e\x66unding_weight\x18\x0e \x01(\rH\x02\x88\x01\x01\x12\x33\n\x19\x63hannel_fee_max_base_msat\x18\x0f \x01(\x0b\x32\x0b.cln.AmountH\x03\x88\x01\x01\x12\x35\n(channel_fee_max_proportional_thousandths\x18\x10 \x01(\rH\x04\x88\x01\x01\x12\x1a\n\rcompact_lease\x18\x11 \x01(\x0cH\x05\x88\x01\x01\"9\n\x12\x46underupdatePolicy\x12\t\n\x05MATCH\x10\x00\x12\r\n\tAVAILABLE\x10\x01\x12\t\n\x05\x46IXED\x10\x02\x42\x16\n\x14_lease_fee_base_msatB\x12\n\x10_lease_fee_basisB\x11\n\x0f_funding_weightB\x1c\n\x1a_channel_fee_max_base_msatB+\n)_channel_fee_max_proportional_thousandthsB\x10\n\x0e_compact_lease\"\xec\x01\n\x0fGetrouteRequest\x12\n\n\x02id\x18\x01 \x01(\x0c\x12\x12\n\nriskfactor\x18\x03 \x01(\x04\x12\x11\n\x04\x63ltv\x18\x04 \x01(\rH\x00\x88\x01\x01\x12\x13\n\x06\x66romid\x18\x05 \x01(\x0cH\x01\x88\x01\x01\x12\x18\n\x0b\x66uzzpercent\x18\x06 \x01(\rH\x02\x88\x01\x01\x12\x0f\n\x07\x65xclude\x18\x07 \x03(\t\x12\x14\n\x07maxhops\x18\x08 \x01(\rH\x03\x88\x01\x01\x12 \n\x0b\x61mount_msat\x18\t \x01(\x0b\x32\x0b.cln.AmountB\x07\n\x05_cltvB\t\n\x07_fromidB\x0e\n\x0c_fuzzpercentB\n\n\x08_maxhops\"5\n\x10GetrouteResponse\x12!\n\x05route\x18\x01 \x03(\x0b\x32\x12.cln.GetrouteRoute\"\xc5\x01\n\rGetrouteRoute\x12\n\n\x02id\x18\x01 \x01(\x0c\x12\x0f\n\x07\x63hannel\x18\x02 \x01(\t\x12\x11\n\tdirection\x18\x03 \x01(\r\x12 \n\x0b\x61mount_msat\x18\x04 \x01(\x0b\x32\x0b.cln.Amount\x12\r\n\x05\x64\x65lay\x18\x05 \x01(\r\x12\x34\n\x05style\x18\x06 \x01(\x0e\x32%.cln.GetrouteRoute.GetrouteRouteStyle\"\x1d\n\x12GetrouteRouteStyle\x12\x07\n\x03TLV\x10\x00\"t\n\x14ListaddressesRequest\x12\x14\n\x07\x61\x64\x64ress\x18\x01 \x01(\tH\x00\x88\x01\x01\x12\x12\n\x05start\x18\x02 \x01(\x04H\x01\x88\x01\x01\x12\x12\n\x05limit\x18\x03 \x01(\rH\x02\x88\x01\x01\x42\n\n\x08_addressB\x08\n\x06_startB\x08\n\x06_limit\"G\n\x15ListaddressesResponse\x12.\n\taddresses\x18\x01 \x03(\x0b\x32\x1b.cln.ListaddressesAddresses\"d\n\x16ListaddressesAddresses\x12\x0e\n\x06keyidx\x18\x01 \x01(\x04\x12\x13\n\x06\x62\x65\x63h32\x18\x02 \x01(\tH\x00\x88\x01\x01\x12\x11\n\x04p2tr\x18\x03 \x01(\tH\x01\x88\x01\x01\x42\t\n\x07_bech32B\x07\n\x05_p2tr\"\xb7\x03\n\x13ListforwardsRequest\x12@\n\x06status\x18\x01 \x01(\x0e\x32+.cln.ListforwardsRequest.ListforwardsStatusH\x00\x88\x01\x01\x12\x17\n\nin_channel\x18\x02 \x01(\tH\x01\x88\x01\x01\x12\x18\n\x0bout_channel\x18\x03 \x01(\tH\x02\x88\x01\x01\x12>\n\x05index\x18\x04 \x01(\x0e\x32*.cln.ListforwardsRequest.ListforwardsIndexH\x03\x88\x01\x01\x12\x12\n\x05start\x18\x05 \x01(\x04H\x04\x88\x01\x01\x12\x12\n\x05limit\x18\x06 \x01(\rH\x05\x88\x01\x01\"L\n\x12ListforwardsStatus\x12\x0b\n\x07OFFERED\x10\x00\x12\x0b\n\x07SETTLED\x10\x01\x12\x10\n\x0cLOCAL_FAILED\x10\x02\x12\n\n\x06\x46\x41ILED\x10\x03\"-\n\x11ListforwardsIndex\x12\x0b\n\x07\x43REATED\x10\x00\x12\x0b\n\x07UPDATED\x10\x01\x42\t\n\x07_statusB\r\n\x0b_in_channelB\x0e\n\x0c_out_channelB\x08\n\x06_indexB\x08\n\x06_startB\x08\n\x06_limit\"C\n\x14ListforwardsResponse\x12+\n\x08\x66orwards\x18\x01 \x03(\x0b\x32\x19.cln.ListforwardsForwards\"\xb4\x06\n\x14ListforwardsForwards\x12\x12\n\nin_channel\x18\x01 \x01(\t\x12\x1c\n\x07in_msat\x18\x02 \x01(\x0b\x32\x0b.cln.Amount\x12\x44\n\x06status\x18\x03 \x01(\x0e\x32\x34.cln.ListforwardsForwards.ListforwardsForwardsStatus\x12\x15\n\rreceived_time\x18\x04 \x01(\x01\x12\x18\n\x0bout_channel\x18\x05 \x01(\tH\x00\x88\x01\x01\x12\"\n\x08\x66\x65\x65_msat\x18\x07 \x01(\x0b\x32\x0b.cln.AmountH\x01\x88\x01\x01\x12\"\n\x08out_msat\x18\x08 \x01(\x0b\x32\x0b.cln.AmountH\x02\x88\x01\x01\x12G\n\x05style\x18\t \x01(\x0e\x32\x33.cln.ListforwardsForwards.ListforwardsForwardsStyleH\x03\x88\x01\x01\x12\x17\n\nin_htlc_id\x18\n \x01(\x04H\x04\x88\x01\x01\x12\x18\n\x0bout_htlc_id\x18\x0b \x01(\x04H\x05\x88\x01\x01\x12\x1a\n\rcreated_index\x18\x0c \x01(\x04H\x06\x88\x01\x01\x12\x1a\n\rupdated_index\x18\r \x01(\x04H\x07\x88\x01\x01\x12\x1a\n\rresolved_time\x18\x0e \x01(\x01H\x08\x88\x01\x01\x12\x15\n\x08\x66\x61ilcode\x18\x0f \x01(\rH\t\x88\x01\x01\x12\x17\n\nfailreason\x18\x10 \x01(\tH\n\x88\x01\x01\"T\n\x1aListforwardsForwardsStatus\x12\x0b\n\x07OFFERED\x10\x00\x12\x0b\n\x07SETTLED\x10\x01\x12\x10\n\x0cLOCAL_FAILED\x10\x02\x12\n\n\x06\x46\x41ILED\x10\x03\"0\n\x19ListforwardsForwardsStyle\x12\n\n\x06LEGACY\x10\x00\x12\x07\n\x03TLV\x10\x01\x42\x0e\n\x0c_out_channelB\x0b\n\t_fee_msatB\x0b\n\t_out_msatB\x08\n\x06_styleB\r\n\x0b_in_htlc_idB\x0e\n\x0c_out_htlc_idB\x10\n\x0e_created_indexB\x10\n\x0e_updated_indexB\x10\n\x0e_resolved_timeB\x0b\n\t_failcodeB\r\n\x0b_failreason\"a\n\x11ListoffersRequest\x12\x15\n\x08offer_id\x18\x01 \x01(\x0cH\x00\x88\x01\x01\x12\x18\n\x0b\x61\x63tive_only\x18\x02 \x01(\x08H\x01\x88\x01\x01\x42\x0b\n\t_offer_idB\x0e\n\x0c_active_only\";\n\x12ListoffersResponse\x12%\n\x06offers\x18\x01 \x03(\x0b\x32\x15.cln.ListoffersOffers\"\x84\x01\n\x10ListoffersOffers\x12\x10\n\x08offer_id\x18\x01 \x01(\x0c\x12\x0e\n\x06\x61\x63tive\x18\x02 \x01(\x08\x12\x12\n\nsingle_use\x18\x03 \x01(\x08\x12\x0e\n\x06\x62olt12\x18\x04 \x01(\t\x12\x0c\n\x04used\x18\x05 \x01(\x08\x12\x12\n\x05label\x18\x06 \x01(\tH\x00\x88\x01\x01\x42\x08\n\x06_label\"\x84\x03\n\x0fListpaysRequest\x12\x13\n\x06\x62olt11\x18\x01 \x01(\tH\x00\x88\x01\x01\x12\x19\n\x0cpayment_hash\x18\x02 \x01(\x0cH\x01\x88\x01\x01\x12\x38\n\x06status\x18\x03 \x01(\x0e\x32#.cln.ListpaysRequest.ListpaysStatusH\x02\x88\x01\x01\x12\x36\n\x05index\x18\x04 \x01(\x0e\x32\".cln.ListpaysRequest.ListpaysIndexH\x03\x88\x01\x01\x12\x12\n\x05start\x18\x05 \x01(\x04H\x04\x88\x01\x01\x12\x12\n\x05limit\x18\x06 \x01(\rH\x05\x88\x01\x01\"7\n\x0eListpaysStatus\x12\x0b\n\x07PENDING\x10\x00\x12\x0c\n\x08\x43OMPLETE\x10\x01\x12\n\n\x06\x46\x41ILED\x10\x02\")\n\rListpaysIndex\x12\x0b\n\x07\x43REATED\x10\x00\x12\x0b\n\x07UPDATED\x10\x01\x42\t\n\x07_bolt11B\x0f\n\r_payment_hashB\t\n\x07_statusB\x08\n\x06_indexB\x08\n\x06_startB\x08\n\x06_limit\"3\n\x10ListpaysResponse\x12\x1f\n\x04pays\x18\x01 \x03(\x0b\x32\x11.cln.ListpaysPays\"\xdb\x05\n\x0cListpaysPays\x12\x14\n\x0cpayment_hash\x18\x01 \x01(\x0c\x12\x34\n\x06status\x18\x02 \x01(\x0e\x32$.cln.ListpaysPays.ListpaysPaysStatus\x12\x18\n\x0b\x64\x65stination\x18\x03 \x01(\x0cH\x00\x88\x01\x01\x12\x12\n\ncreated_at\x18\x04 \x01(\x04\x12\x12\n\x05label\x18\x05 \x01(\tH\x01\x88\x01\x01\x12\x13\n\x06\x62olt11\x18\x06 \x01(\tH\x02\x88\x01\x01\x12\x13\n\x06\x62olt12\x18\x07 \x01(\tH\x03\x88\x01\x01\x12%\n\x0b\x61mount_msat\x18\x08 \x01(\x0b\x32\x0b.cln.AmountH\x04\x88\x01\x01\x12*\n\x10\x61mount_sent_msat\x18\t \x01(\x0b\x32\x0b.cln.AmountH\x05\x88\x01\x01\x12\x17\n\nerroronion\x18\n \x01(\x0cH\x06\x88\x01\x01\x12\x18\n\x0b\x64\x65scription\x18\x0b \x01(\tH\x07\x88\x01\x01\x12\x19\n\x0c\x63ompleted_at\x18\x0c \x01(\x04H\x08\x88\x01\x01\x12\x15\n\x08preimage\x18\r \x01(\x0cH\t\x88\x01\x01\x12\x1c\n\x0fnumber_of_parts\x18\x0e \x01(\x04H\n\x88\x01\x01\x12\x1a\n\rcreated_index\x18\x0f \x01(\x04H\x0b\x88\x01\x01\x12\x1a\n\rupdated_index\x18\x10 \x01(\x04H\x0c\x88\x01\x01\";\n\x12ListpaysPaysStatus\x12\x0b\n\x07PENDING\x10\x00\x12\n\n\x06\x46\x41ILED\x10\x01\x12\x0c\n\x08\x43OMPLETE\x10\x02\x42\x0e\n\x0c_destinationB\x08\n\x06_labelB\t\n\x07_bolt11B\t\n\x07_bolt12B\x0e\n\x0c_amount_msatB\x13\n\x11_amount_sent_msatB\r\n\x0b_erroronionB\x0e\n\x0c_descriptionB\x0f\n\r_completed_atB\x0b\n\t_preimageB\x12\n\x10_number_of_partsB\x10\n\x0e_created_indexB\x10\n\x0e_updated_index\"\xd6\x01\n\x10ListhtlcsRequest\x12\x0f\n\x02id\x18\x01 \x01(\tH\x00\x88\x01\x01\x12\x38\n\x05index\x18\x02 \x01(\x0e\x32$.cln.ListhtlcsRequest.ListhtlcsIndexH\x01\x88\x01\x01\x12\x12\n\x05start\x18\x03 \x01(\x04H\x02\x88\x01\x01\x12\x12\n\x05limit\x18\x04 \x01(\rH\x03\x88\x01\x01\"*\n\x0eListhtlcsIndex\x12\x0b\n\x07\x43REATED\x10\x00\x12\x0b\n\x07UPDATED\x10\x01\x42\x05\n\x03_idB\x08\n\x06_indexB\x08\n\x06_startB\x08\n\x06_limit\"7\n\x11ListhtlcsResponse\x12\"\n\x05htlcs\x18\x01 \x03(\x0b\x32\x13.cln.ListhtlcsHtlcs\"\xe5\x02\n\x0eListhtlcsHtlcs\x12\x18\n\x10short_channel_id\x18\x01 \x01(\t\x12\n\n\x02id\x18\x02 \x01(\x04\x12\x0e\n\x06\x65xpiry\x18\x03 \x01(\r\x12 \n\x0b\x61mount_msat\x18\x04 \x01(\x0b\x32\x0b.cln.Amount\x12>\n\tdirection\x18\x05 \x01(\x0e\x32+.cln.ListhtlcsHtlcs.ListhtlcsHtlcsDirection\x12\x14\n\x0cpayment_hash\x18\x06 \x01(\x0c\x12\x1d\n\x05state\x18\x07 \x01(\x0e\x32\x0e.cln.HtlcState\x12\x1a\n\rcreated_index\x18\x08 \x01(\x04H\x00\x88\x01\x01\x12\x1a\n\rupdated_index\x18\t \x01(\x04H\x01\x88\x01\x01\"*\n\x17ListhtlcsHtlcsDirection\x12\x07\n\x03OUT\x10\x00\x12\x06\n\x02IN\x10\x01\x42\x10\n\x0e_created_indexB\x10\n\x0e_updated_index\"\xb2\x02\n\x17MultifundchannelRequest\x12\x37\n\x0c\x64\x65stinations\x18\x01 \x03(\x0b\x32!.cln.MultifundchannelDestinations\x12\"\n\x07\x66\x65\x65rate\x18\x02 \x01(\x0b\x32\x0c.cln.FeerateH\x00\x88\x01\x01\x12\x14\n\x07minconf\x18\x03 \x01(\x12H\x01\x88\x01\x01\x12\x1c\n\x05utxos\x18\x04 \x03(\x0b\x32\r.cln.Outpoint\x12\x18\n\x0bminchannels\x18\x05 \x01(\x12H\x02\x88\x01\x01\x12-\n\x12\x63ommitment_feerate\x18\x06 \x01(\x0b\x32\x0c.cln.FeerateH\x03\x88\x01\x01\x42\n\n\x08_feerateB\n\n\x08_minconfB\x0e\n\x0c_minchannelsB\x15\n\x13_commitment_feerate\"\x97\x01\n\x18MultifundchannelResponse\x12\n\n\x02tx\x18\x01 \x01(\x0c\x12\x0c\n\x04txid\x18\x02 \x01(\x0c\x12\x34\n\x0b\x63hannel_ids\x18\x03 \x03(\x0b\x32\x1f.cln.MultifundchannelChannelIds\x12+\n\x06\x66\x61iled\x18\x04 \x03(\x0b\x32\x1b.cln.MultifundchannelFailed\"\xff\x02\n\x1cMultifundchannelDestinations\x12\n\n\x02id\x18\x01 \x01(\t\x12 \n\x06\x61mount\x18\x02 \x01(\x0b\x32\x10.cln.AmountOrAll\x12\x15\n\x08\x61nnounce\x18\x03 \x01(\x08H\x00\x88\x01\x01\x12#\n\tpush_msat\x18\x04 \x01(\x0b\x32\x0b.cln.AmountH\x01\x88\x01\x01\x12\x15\n\x08\x63lose_to\x18\x05 \x01(\tH\x02\x88\x01\x01\x12%\n\x0brequest_amt\x18\x06 \x01(\x0b\x32\x0b.cln.AmountH\x03\x88\x01\x01\x12\x1a\n\rcompact_lease\x18\x07 \x01(\tH\x04\x88\x01\x01\x12\x15\n\x08mindepth\x18\x08 \x01(\rH\x05\x88\x01\x01\x12!\n\x07reserve\x18\t \x01(\x0b\x32\x0b.cln.AmountH\x06\x88\x01\x01\x42\x0b\n\t_announceB\x0c\n\n_push_msatB\x0b\n\t_close_toB\x0e\n\x0c_request_amtB\x10\n\x0e_compact_leaseB\x0b\n\t_mindepthB\n\n\x08_reserve\"\xc8\x01\n\x1aMultifundchannelChannelIds\x12\n\n\x02id\x18\x01 \x01(\x0c\x12\x0e\n\x06outnum\x18\x02 \x01(\r\x12\x12\n\nchannel_id\x18\x03 \x01(\x0c\x12\x45\n\x0c\x63hannel_type\x18\x04 \x01(\x0b\x32*.cln.MultifundchannelChannelIdsChannelTypeH\x00\x88\x01\x01\x12\x15\n\x08\x63lose_to\x18\x05 \x01(\x0cH\x01\x88\x01\x01\x42\x0f\n\r_channel_typeB\x0b\n\t_close_to\"Z\n%MultifundchannelChannelIdsChannelType\x12\x0c\n\x04\x62its\x18\x01 \x03(\r\x12#\n\x05names\x18\x02 \x03(\x0e\x32\x14.cln.ChannelTypeName\"\x93\x02\n\x16MultifundchannelFailed\x12\n\n\x02id\x18\x01 \x01(\x0c\x12H\n\x06method\x18\x02 \x01(\x0e\x32\x38.cln.MultifundchannelFailed.MultifundchannelFailedMethod\x12/\n\x05\x65rror\x18\x03 \x01(\x0b\x32 .cln.MultifundchannelFailedError\"r\n\x1cMultifundchannelFailedMethod\x12\x0b\n\x07\x43ONNECT\x10\x00\x12\x14\n\x10OPENCHANNEL_INIT\x10\x01\x12\x15\n\x11\x46UNDCHANNEL_START\x10\x02\x12\x18\n\x14\x46UNDCHANNEL_COMPLETE\x10\x03\"<\n\x1bMultifundchannelFailedError\x12\x0c\n\x04\x63ode\x18\x01 \x01(\x12\x12\x0f\n\x07message\x18\x02 \x01(\t\"\xa8\x01\n\x14MultiwithdrawRequest\x12 \n\x07outputs\x18\x01 \x03(\x0b\x32\x0f.cln.OutputDesc\x12\"\n\x07\x66\x65\x65rate\x18\x02 \x01(\x0b\x32\x0c.cln.FeerateH\x00\x88\x01\x01\x12\x14\n\x07minconf\x18\x03 \x01(\rH\x01\x88\x01\x01\x12\x1c\n\x05utxos\x18\x04 \x03(\x0b\x32\r.cln.OutpointB\n\n\x08_feerateB\n\n\x08_minconf\"1\n\x15MultiwithdrawResponse\x12\n\n\x02tx\x18\x01 \x01(\x0c\x12\x0c\n\x04txid\x18\x02 \x01(\x0c\"\xa0\x04\n\x0cOfferRequest\x12\x0e\n\x06\x61mount\x18\x01 \x01(\t\x12\x18\n\x0b\x64\x65scription\x18\x02 \x01(\tH\x00\x88\x01\x01\x12\x13\n\x06issuer\x18\x03 \x01(\tH\x01\x88\x01\x01\x12\x12\n\x05label\x18\x04 \x01(\tH\x02\x88\x01\x01\x12\x19\n\x0cquantity_max\x18\x05 \x01(\x04H\x03\x88\x01\x01\x12\x1c\n\x0f\x61\x62solute_expiry\x18\x06 \x01(\x04H\x04\x88\x01\x01\x12\x17\n\nrecurrence\x18\x07 \x01(\tH\x05\x88\x01\x01\x12\x1c\n\x0frecurrence_base\x18\x08 \x01(\tH\x06\x88\x01\x01\x12!\n\x14recurrence_paywindow\x18\t \x01(\tH\x07\x88\x01\x01\x12\x1d\n\x10recurrence_limit\x18\n \x01(\rH\x08\x88\x01\x01\x12\x17\n\nsingle_use\x18\x0b \x01(\x08H\t\x88\x01\x01\x12(\n\x1brecurrence_start_any_period\x18\x0c \x01(\x08H\n\x88\x01\x01\x42\x0e\n\x0c_descriptionB\t\n\x07_issuerB\x08\n\x06_labelB\x0f\n\r_quantity_maxB\x12\n\x10_absolute_expiryB\r\n\x0b_recurrenceB\x12\n\x10_recurrence_baseB\x17\n\x15_recurrence_paywindowB\x13\n\x11_recurrence_limitB\r\n\x0b_single_useB\x1e\n\x1c_recurrence_start_any_period\"\x92\x01\n\rOfferResponse\x12\x10\n\x08offer_id\x18\x01 \x01(\x0c\x12\x0e\n\x06\x61\x63tive\x18\x02 \x01(\x08\x12\x12\n\nsingle_use\x18\x03 \x01(\x08\x12\x0e\n\x06\x62olt12\x18\x04 \x01(\t\x12\x0c\n\x04used\x18\x05 \x01(\x08\x12\x0f\n\x07\x63reated\x18\x06 \x01(\x08\x12\x12\n\x05label\x18\x07 \x01(\tH\x00\x88\x01\x01\x42\x08\n\x06_label\"-\n\x17OpenchannelAbortRequest\x12\x12\n\nchannel_id\x18\x01 \x01(\x0c\"X\n\x18OpenchannelAbortResponse\x12\x12\n\nchannel_id\x18\x01 \x01(\x0c\x12\x18\n\x10\x63hannel_canceled\x18\x02 \x01(\x08\x12\x0e\n\x06reason\x18\x03 \x01(\t\"\x9e\x01\n\x16OpenchannelBumpRequest\x12\x12\n\nchannel_id\x18\x01 \x01(\x0c\x12\x13\n\x0binitialpsbt\x18\x02 \x01(\t\x12*\n\x0f\x66unding_feerate\x18\x03 \x01(\x0b\x32\x0c.cln.FeerateH\x00\x88\x01\x01\x12\x1b\n\x06\x61mount\x18\x04 \x01(\x0b\x32\x0b.cln.AmountB\x12\n\x10_funding_feerate\"\x83\x02\n\x17OpenchannelBumpResponse\x12\x12\n\nchannel_id\x18\x01 \x01(\x0c\x12:\n\x0c\x63hannel_type\x18\x02 \x01(\x0b\x32\x1f.cln.OpenchannelBumpChannelTypeH\x00\x88\x01\x01\x12\x0c\n\x04psbt\x18\x03 \x01(\t\x12\x1b\n\x13\x63ommitments_secured\x18\x04 \x01(\x08\x12\x16\n\x0e\x66unding_serial\x18\x05 \x01(\x04\x12&\n\x19requires_confirmed_inputs\x18\x06 \x01(\x08H\x01\x88\x01\x01\x42\x0f\n\r_channel_typeB\x1c\n\x1a_requires_confirmed_inputs\"O\n\x1aOpenchannelBumpChannelType\x12\x0c\n\x04\x62its\x18\x01 \x03(\r\x12#\n\x05names\x18\x02 \x03(\x0e\x32\x14.cln.ChannelTypeName\"\x9f\x03\n\x16OpenchannelInitRequest\x12\n\n\x02id\x18\x01 \x01(\x0c\x12\x13\n\x0binitialpsbt\x18\x02 \x01(\t\x12-\n\x12\x63ommitment_feerate\x18\x03 \x01(\x0b\x32\x0c.cln.FeerateH\x00\x88\x01\x01\x12*\n\x0f\x66unding_feerate\x18\x04 \x01(\x0b\x32\x0c.cln.FeerateH\x01\x88\x01\x01\x12\x15\n\x08\x61nnounce\x18\x05 \x01(\x08H\x02\x88\x01\x01\x12\x15\n\x08\x63lose_to\x18\x06 \x01(\tH\x03\x88\x01\x01\x12%\n\x0brequest_amt\x18\x07 \x01(\x0b\x32\x0b.cln.AmountH\x04\x88\x01\x01\x12\x1a\n\rcompact_lease\x18\x08 \x01(\x0cH\x05\x88\x01\x01\x12\x14\n\x0c\x63hannel_type\x18\t \x03(\r\x12\x1b\n\x06\x61mount\x18\n \x01(\x0b\x32\x0b.cln.AmountB\x15\n\x13_commitment_feerateB\x12\n\x10_funding_feerateB\x0b\n\t_announceB\x0b\n\t_close_toB\x0e\n\x0c_request_amtB\x10\n\x0e_compact_lease\"\x83\x02\n\x17OpenchannelInitResponse\x12\x12\n\nchannel_id\x18\x01 \x01(\x0c\x12\x0c\n\x04psbt\x18\x02 \x01(\t\x12:\n\x0c\x63hannel_type\x18\x03 \x01(\x0b\x32\x1f.cln.OpenchannelInitChannelTypeH\x00\x88\x01\x01\x12\x1b\n\x13\x63ommitments_secured\x18\x04 \x01(\x08\x12\x16\n\x0e\x66unding_serial\x18\x05 \x01(\x04\x12&\n\x19requires_confirmed_inputs\x18\x06 \x01(\x08H\x01\x88\x01\x01\x42\x0f\n\r_channel_typeB\x1c\n\x1a_requires_confirmed_inputs\"O\n\x1aOpenchannelInitChannelType\x12\x0c\n\x04\x62its\x18\x01 \x03(\r\x12#\n\x05names\x18\x02 \x03(\x0e\x32\x14.cln.ChannelTypeName\"C\n\x18OpenchannelSignedRequest\x12\x12\n\nchannel_id\x18\x01 \x01(\x0c\x12\x13\n\x0bsigned_psbt\x18\x02 \x01(\t\"I\n\x19OpenchannelSignedResponse\x12\x12\n\nchannel_id\x18\x01 \x01(\x0c\x12\n\n\x02tx\x18\x02 \x01(\x0c\x12\x0c\n\x04txid\x18\x03 \x01(\x0c\"<\n\x18OpenchannelUpdateRequest\x12\x12\n\nchannel_id\x18\x01 \x01(\x0c\x12\x0c\n\x04psbt\x18\x02 \x01(\t\"\xab\x02\n\x19OpenchannelUpdateResponse\x12\x12\n\nchannel_id\x18\x01 \x01(\x0c\x12<\n\x0c\x63hannel_type\x18\x02 \x01(\x0b\x32!.cln.OpenchannelUpdateChannelTypeH\x00\x88\x01\x01\x12\x0c\n\x04psbt\x18\x03 \x01(\t\x12\x1b\n\x13\x63ommitments_secured\x18\x04 \x01(\x08\x12\x16\n\x0e\x66unding_outnum\x18\x05 \x01(\r\x12\x15\n\x08\x63lose_to\x18\x06 \x01(\x0cH\x01\x88\x01\x01\x12&\n\x19requires_confirmed_inputs\x18\x07 \x01(\x08H\x02\x88\x01\x01\x42\x0f\n\r_channel_typeB\x0b\n\t_close_toB\x1c\n\x1a_requires_confirmed_inputs\"Q\n\x1cOpenchannelUpdateChannelType\x12\x0c\n\x04\x62its\x18\x01 \x03(\r\x12#\n\x05names\x18\x02 \x03(\x0e\x32\x14.cln.ChannelTypeName\"Y\n\x0bPingRequest\x12\n\n\x02id\x18\x01 \x01(\x0c\x12\x10\n\x03len\x18\x02 \x01(\rH\x00\x88\x01\x01\x12\x16\n\tpongbytes\x18\x03 \x01(\rH\x01\x88\x01\x01\x42\x06\n\x04_lenB\x0c\n\n_pongbytes\"\x1e\n\x0cPingResponse\x12\x0e\n\x06totlen\x18\x01 \x01(\r\"\x91\x01\n\rPluginRequest\x12)\n\nsubcommand\x18\x01 \x01(\x0e\x32\x15.cln.PluginSubcommand\x12\x13\n\x06plugin\x18\x02 \x01(\tH\x00\x88\x01\x01\x12\x16\n\tdirectory\x18\x03 \x01(\tH\x01\x88\x01\x01\x12\x0f\n\x07options\x18\x04 \x03(\tB\t\n\x07_pluginB\x0c\n\n_directory\"}\n\x0ePluginResponse\x12&\n\x07\x63ommand\x18\x01 \x01(\x0e\x32\x15.cln.PluginSubcommand\x12#\n\x07plugins\x18\x02 \x03(\x0b\x32\x12.cln.PluginPlugins\x12\x13\n\x06result\x18\x03 \x01(\tH\x00\x88\x01\x01\x42\t\n\x07_result\">\n\rPluginPlugins\x12\x0c\n\x04name\x18\x01 \x01(\t\x12\x0e\n\x06\x61\x63tive\x18\x02 \x01(\x08\x12\x0f\n\x07\x64ynamic\x18\x03 \x01(\x08\"<\n\x14RenepaystatusRequest\x12\x16\n\tinvstring\x18\x01 \x01(\tH\x00\x88\x01\x01\x42\x0c\n\n_invstring\"G\n\x15RenepaystatusResponse\x12.\n\tpaystatus\x18\x01 \x03(\x0b\x32\x1b.cln.RenepaystatusPaystatus\"\xe2\x03\n\x16RenepaystatusPaystatus\x12\x0e\n\x06\x62olt11\x18\x01 \x01(\t\x12\x1d\n\x10payment_preimage\x18\x02 \x01(\x0cH\x00\x88\x01\x01\x12\x14\n\x0cpayment_hash\x18\x03 \x01(\x0c\x12\x12\n\ncreated_at\x18\x04 \x01(\x01\x12\x0f\n\x07groupid\x18\x05 \x01(\r\x12\x12\n\x05parts\x18\x06 \x01(\rH\x01\x88\x01\x01\x12 \n\x0b\x61mount_msat\x18\x07 \x01(\x0b\x32\x0b.cln.Amount\x12*\n\x10\x61mount_sent_msat\x18\x08 \x01(\x0b\x32\x0b.cln.AmountH\x02\x88\x01\x01\x12H\n\x06status\x18\t \x01(\x0e\x32\x38.cln.RenepaystatusPaystatus.RenepaystatusPaystatusStatus\x12\x18\n\x0b\x64\x65stination\x18\n \x01(\x0cH\x03\x88\x01\x01\x12\r\n\x05notes\x18\x0b \x03(\t\"E\n\x1cRenepaystatusPaystatusStatus\x12\x0c\n\x08\x43OMPLETE\x10\x00\x12\x0b\n\x07PENDING\x10\x01\x12\n\n\x06\x46\x41ILED\x10\x02\x42\x13\n\x11_payment_preimageB\x08\n\x06_partsB\x13\n\x11_amount_sent_msatB\x0e\n\x0c_destination\"\xda\x02\n\x0eRenepayRequest\x12\x11\n\tinvstring\x18\x01 \x01(\t\x12%\n\x0b\x61mount_msat\x18\x02 \x01(\x0b\x32\x0b.cln.AmountH\x00\x88\x01\x01\x12 \n\x06maxfee\x18\x03 \x01(\x0b\x32\x0b.cln.AmountH\x01\x88\x01\x01\x12\x15\n\x08maxdelay\x18\x04 \x01(\rH\x02\x88\x01\x01\x12\x16\n\tretry_for\x18\x05 \x01(\rH\x03\x88\x01\x01\x12\x18\n\x0b\x64\x65scription\x18\x06 \x01(\tH\x04\x88\x01\x01\x12\x12\n\x05label\x18\x07 \x01(\tH\x05\x88\x01\x01\x12\x1b\n\x0e\x64\x65v_use_shadow\x18\x08 \x01(\x08H\x06\x88\x01\x01\x12\x0f\n\x07\x65xclude\x18\t \x03(\tB\x0e\n\x0c_amount_msatB\t\n\x07_maxfeeB\x0b\n\t_maxdelayB\x0c\n\n_retry_forB\x0e\n\x0c_descriptionB\x08\n\x06_labelB\x11\n\x0f_dev_use_shadow\"\xa5\x03\n\x0fRenepayResponse\x12\x18\n\x10payment_preimage\x18\x01 \x01(\x0c\x12\x14\n\x0cpayment_hash\x18\x02 \x01(\x0c\x12\x12\n\ncreated_at\x18\x03 \x01(\x01\x12\r\n\x05parts\x18\x04 \x01(\r\x12 \n\x0b\x61mount_msat\x18\x05 \x01(\x0b\x32\x0b.cln.Amount\x12%\n\x10\x61mount_sent_msat\x18\x06 \x01(\x0b\x32\x0b.cln.Amount\x12\x32\n\x06status\x18\x07 \x01(\x0e\x32\".cln.RenepayResponse.RenepayStatus\x12\x18\n\x0b\x64\x65stination\x18\x08 \x01(\x0cH\x00\x88\x01\x01\x12\x13\n\x06\x62olt11\x18\t \x01(\tH\x01\x88\x01\x01\x12\x13\n\x06\x62olt12\x18\n \x01(\tH\x02\x88\x01\x01\x12\x14\n\x07groupid\x18\x0b \x01(\x04H\x03\x88\x01\x01\"6\n\rRenepayStatus\x12\x0c\n\x08\x43OMPLETE\x10\x00\x12\x0b\n\x07PENDING\x10\x01\x12\n\n\x06\x46\x41ILED\x10\x02\x42\x0e\n\x0c_destinationB\t\n\x07_bolt11B\t\n\x07_bolt12B\n\n\x08_groupid\"l\n\x14ReserveinputsRequest\x12\x0c\n\x04psbt\x18\x01 \x01(\t\x12\x16\n\texclusive\x18\x02 \x01(\x08H\x00\x88\x01\x01\x12\x14\n\x07reserve\x18\x03 \x01(\rH\x01\x88\x01\x01\x42\x0c\n\n_exclusiveB\n\n\x08_reserve\"M\n\x15ReserveinputsResponse\x12\x34\n\x0creservations\x18\x01 \x03(\x0b\x32\x1e.cln.ReserveinputsReservations\"z\n\x19ReserveinputsReservations\x12\x0c\n\x04txid\x18\x01 \x01(\x0c\x12\x0c\n\x04vout\x18\x02 \x01(\r\x12\x14\n\x0cwas_reserved\x18\x03 \x01(\x08\x12\x10\n\x08reserved\x18\x04 \x01(\x08\x12\x19\n\x11reserved_to_block\x18\x05 \x01(\r\"4\n\x14SendcustommsgRequest\x12\x0f\n\x07node_id\x18\x01 \x01(\x0c\x12\x0b\n\x03msg\x18\x02 \x01(\x0c\"\'\n\x15SendcustommsgResponse\x12\x0e\n\x06status\x18\x01 \x01(\t\"\xb0\x01\n\x12SendinvoiceRequest\x12\x0e\n\x06invreq\x18\x01 \x01(\t\x12\r\n\x05label\x18\x02 \x01(\t\x12%\n\x0b\x61mount_msat\x18\x03 \x01(\x0b\x32\x0b.cln.AmountH\x00\x88\x01\x01\x12\x14\n\x07timeout\x18\x04 \x01(\rH\x01\x88\x01\x01\x12\x15\n\x08quantity\x18\x05 \x01(\x04H\x02\x88\x01\x01\x42\x0e\n\x0c_amount_msatB\n\n\x08_timeoutB\x0b\n\t_quantity\"\xcf\x04\n\x13SendinvoiceResponse\x12\r\n\x05label\x18\x01 \x01(\t\x12\x13\n\x0b\x64\x65scription\x18\x02 \x01(\t\x12\x14\n\x0cpayment_hash\x18\x03 \x01(\x0c\x12:\n\x06status\x18\x04 \x01(\x0e\x32*.cln.SendinvoiceResponse.SendinvoiceStatus\x12\x12\n\nexpires_at\x18\x05 \x01(\x04\x12%\n\x0b\x61mount_msat\x18\x06 \x01(\x0b\x32\x0b.cln.AmountH\x00\x88\x01\x01\x12\x13\n\x06\x62olt12\x18\x07 \x01(\tH\x01\x88\x01\x01\x12\x1a\n\rcreated_index\x18\x08 \x01(\x04H\x02\x88\x01\x01\x12\x1a\n\rupdated_index\x18\t \x01(\x04H\x03\x88\x01\x01\x12\x16\n\tpay_index\x18\n \x01(\x04H\x04\x88\x01\x01\x12.\n\x14\x61mount_received_msat\x18\x0b \x01(\x0b\x32\x0b.cln.AmountH\x05\x88\x01\x01\x12\x14\n\x07paid_at\x18\x0c \x01(\x04H\x06\x88\x01\x01\x12\x1d\n\x10payment_preimage\x18\r \x01(\x0cH\x07\x88\x01\x01\"6\n\x11SendinvoiceStatus\x12\n\n\x06UNPAID\x10\x00\x12\x08\n\x04PAID\x10\x01\x12\x0b\n\x07\x45XPIRED\x10\x02\x42\x0e\n\x0c_amount_msatB\t\n\x07_bolt12B\x10\n\x0e_created_indexB\x10\n\x0e_updated_indexB\x0c\n\n_pay_indexB\x17\n\x15_amount_received_msatB\n\n\x08_paid_atB\x13\n\x11_payment_preimage\"\xaa\x02\n\x11SetchannelRequest\x12\n\n\x02id\x18\x01 \x01(\t\x12!\n\x07\x66\x65\x65\x62\x61se\x18\x02 \x01(\x0b\x32\x0b.cln.AmountH\x00\x88\x01\x01\x12\x13\n\x06\x66\x65\x65ppm\x18\x03 \x01(\rH\x01\x88\x01\x01\x12!\n\x07htlcmin\x18\x04 \x01(\x0b\x32\x0b.cln.AmountH\x02\x88\x01\x01\x12!\n\x07htlcmax\x18\x05 \x01(\x0b\x32\x0b.cln.AmountH\x03\x88\x01\x01\x12\x19\n\x0c\x65nforcedelay\x18\x06 \x01(\rH\x04\x88\x01\x01\x12\x1c\n\x0fignorefeelimits\x18\x07 \x01(\x08H\x05\x88\x01\x01\x42\n\n\x08_feebaseB\t\n\x07_feeppmB\n\n\x08_htlcminB\n\n\x08_htlcmaxB\x0f\n\r_enforcedelayB\x12\n\x10_ignorefeelimits\"?\n\x12SetchannelResponse\x12)\n\x08\x63hannels\x18\x01 \x03(\x0b\x32\x17.cln.SetchannelChannels\"\xca\x03\n\x12SetchannelChannels\x12\x0f\n\x07peer_id\x18\x01 \x01(\x0c\x12\x12\n\nchannel_id\x18\x02 \x01(\x0c\x12\x1d\n\x10short_channel_id\x18\x03 \x01(\tH\x00\x88\x01\x01\x12\"\n\rfee_base_msat\x18\x04 \x01(\x0b\x32\x0b.cln.Amount\x12#\n\x1b\x66\x65\x65_proportional_millionths\x18\x05 \x01(\r\x12*\n\x15minimum_htlc_out_msat\x18\x06 \x01(\x0b\x32\x0b.cln.Amount\x12$\n\x17warning_htlcmin_too_low\x18\x07 \x01(\tH\x01\x88\x01\x01\x12*\n\x15maximum_htlc_out_msat\x18\x08 \x01(\x0b\x32\x0b.cln.Amount\x12%\n\x18warning_htlcmax_too_high\x18\t \x01(\tH\x02\x88\x01\x01\x12\x1e\n\x11ignore_fee_limits\x18\n \x01(\x08H\x03\x88\x01\x01\x42\x13\n\x11_short_channel_idB\x1a\n\x18_warning_htlcmin_too_lowB\x1b\n\x19_warning_htlcmax_too_highB\x14\n\x12_ignore_fee_limits\"b\n\x10SetconfigRequest\x12\x0e\n\x06\x63onfig\x18\x01 \x01(\t\x12\x10\n\x03val\x18\x02 \x01(\tH\x00\x88\x01\x01\x12\x16\n\ttransient\x18\x03 \x01(\x08H\x01\x88\x01\x01\x42\x06\n\x04_valB\x0c\n\n_transient\"9\n\x11SetconfigResponse\x12$\n\x06\x63onfig\x18\x01 \x01(\x0b\x32\x14.cln.SetconfigConfig\"\xa5\x02\n\x0fSetconfigConfig\x12\x0e\n\x06\x63onfig\x18\x01 \x01(\t\x12\x0e\n\x06source\x18\x02 \x01(\t\x12\x13\n\x06plugin\x18\x03 \x01(\tH\x00\x88\x01\x01\x12\x0f\n\x07\x64ynamic\x18\x04 \x01(\x08\x12\x10\n\x03set\x18\x05 \x01(\x08H\x01\x88\x01\x01\x12\x16\n\tvalue_str\x18\x06 \x01(\tH\x02\x88\x01\x01\x12$\n\nvalue_msat\x18\x07 \x01(\x0b\x32\x0b.cln.AmountH\x03\x88\x01\x01\x12\x16\n\tvalue_int\x18\x08 \x01(\x12H\x04\x88\x01\x01\x12\x17\n\nvalue_bool\x18\t \x01(\x08H\x05\x88\x01\x01\x42\t\n\x07_pluginB\x06\n\x04_setB\x0c\n\n_value_strB\r\n\x0b_value_msatB\x0c\n\n_value_intB\r\n\x0b_value_bool\"6\n\x15SetpsbtversionRequest\x12\x0c\n\x04psbt\x18\x01 \x01(\t\x12\x0f\n\x07version\x18\x02 \x01(\r\"&\n\x16SetpsbtversionResponse\x12\x0c\n\x04psbt\x18\x01 \x01(\t\"\'\n\x12SigninvoiceRequest\x12\x11\n\tinvstring\x18\x01 \x01(\t\"%\n\x13SigninvoiceResponse\x12\x0e\n\x06\x62olt11\x18\x01 \x01(\t\"%\n\x12SignmessageRequest\x12\x0f\n\x07message\x18\x01 \x01(\t\"F\n\x13SignmessageResponse\x12\x11\n\tsignature\x18\x01 \x01(\x0c\x12\r\n\x05recid\x18\x02 \x01(\x0c\x12\r\n\x05zbase\x18\x03 \x01(\t\"\xc8\x01\n\x11SpliceInitRequest\x12\x12\n\nchannel_id\x18\x01 \x01(\x0c\x12\x17\n\x0frelative_amount\x18\x02 \x01(\x12\x12\x18\n\x0binitialpsbt\x18\x03 \x01(\tH\x00\x88\x01\x01\x12\x1b\n\x0e\x66\x65\x65rate_per_kw\x18\x04 \x01(\rH\x01\x88\x01\x01\x12\x1a\n\rforce_feerate\x18\x05 \x01(\x08H\x02\x88\x01\x01\x42\x0e\n\x0c_initialpsbtB\x11\n\x0f_feerate_per_kwB\x10\n\x0e_force_feerate\"\"\n\x12SpliceInitResponse\x12\x0c\n\x04psbt\x18\x01 \x01(\t\"_\n\x13SpliceSignedRequest\x12\x12\n\nchannel_id\x18\x01 \x01(\x0c\x12\x0c\n\x04psbt\x18\x02 \x01(\t\x12\x17\n\nsign_first\x18\x03 \x01(\x08H\x00\x88\x01\x01\x42\r\n\x0b_sign_first\"^\n\x14SpliceSignedResponse\x12\n\n\x02tx\x18\x01 \x01(\x0c\x12\x0c\n\x04txid\x18\x02 \x01(\x0c\x12\x13\n\x06outnum\x18\x03 \x01(\rH\x00\x88\x01\x01\x12\x0c\n\x04psbt\x18\x04 \x01(\tB\t\n\x07_outnum\"7\n\x13SpliceUpdateRequest\x12\x12\n\nchannel_id\x18\x01 \x01(\x0c\x12\x0c\n\x04psbt\x18\x02 \x01(\t\"y\n\x14SpliceUpdateResponse\x12\x0c\n\x04psbt\x18\x01 \x01(\t\x12\x1b\n\x13\x63ommitments_secured\x18\x02 \x01(\x08\x12\x1f\n\x12signatures_secured\x18\x03 \x01(\x08H\x00\x88\x01\x01\x42\x15\n\x13_signatures_secured\"\xc6\x01\n\x10\x44\x65vspliceRequest\x12\x16\n\x0escript_or_json\x18\x01 \x01(\t\x12\x13\n\x06\x64ryrun\x18\x02 \x01(\x08H\x00\x88\x01\x01\x12\x1a\n\rforce_feerate\x18\x03 \x01(\x08H\x01\x88\x01\x01\x12\x16\n\tdebug_log\x18\x04 \x01(\x08H\x02\x88\x01\x01\x12\x17\n\ndev_wetrun\x18\x05 \x01(\x08H\x03\x88\x01\x01\x42\t\n\x07_dryrunB\x10\n\x0e_force_feerateB\x0c\n\n_debug_logB\r\n\x0b_dev_wetrun\"\x80\x01\n\x11\x44\x65vspliceResponse\x12\x0e\n\x06\x64ryrun\x18\x01 \x03(\t\x12\x11\n\x04psbt\x18\x02 \x01(\tH\x00\x88\x01\x01\x12\x0f\n\x02tx\x18\x03 \x01(\tH\x01\x88\x01\x01\x12\x11\n\x04txid\x18\x04 \x01(\tH\x02\x88\x01\x01\x12\x0b\n\x03log\x18\x05 \x03(\tB\x07\n\x05_psbtB\x05\n\x03_txB\x07\n\x05_txid\"H\n\x16UnreserveinputsRequest\x12\x0c\n\x04psbt\x18\x01 \x01(\t\x12\x14\n\x07reserve\x18\x02 \x01(\rH\x00\x88\x01\x01\x42\n\n\x08_reserve\"Q\n\x17UnreserveinputsResponse\x12\x36\n\x0creservations\x18\x01 \x03(\x0b\x32 .cln.UnreserveinputsReservations\"\x97\x01\n\x1bUnreserveinputsReservations\x12\x0c\n\x04txid\x18\x01 \x01(\x0c\x12\x0c\n\x04vout\x18\x02 \x01(\r\x12\x14\n\x0cwas_reserved\x18\x03 \x01(\x08\x12\x10\n\x08reserved\x18\x04 \x01(\x08\x12\x1e\n\x11reserved_to_block\x18\x05 \x01(\rH\x00\x88\x01\x01\x42\x14\n\x12_reserved_to_block\"n\n\x14UpgradewalletRequest\x12\"\n\x07\x66\x65\x65rate\x18\x01 \x01(\x0b\x32\x0c.cln.FeerateH\x00\x88\x01\x01\x12\x17\n\nreservedok\x18\x02 \x01(\x08H\x01\x88\x01\x01\x42\n\n\x08_feerateB\r\n\x0b_reservedok\"\x95\x01\n\x15UpgradewalletResponse\x12\x1a\n\rupgraded_outs\x18\x01 \x01(\x04H\x00\x88\x01\x01\x12\x11\n\x04psbt\x18\x02 \x01(\tH\x01\x88\x01\x01\x12\x0f\n\x02tx\x18\x03 \x01(\x0cH\x02\x88\x01\x01\x12\x11\n\x04txid\x18\x04 \x01(\x0cH\x03\x88\x01\x01\x42\x10\n\x0e_upgraded_outsB\x07\n\x05_psbtB\x05\n\x03_txB\x07\n\x05_txid\"O\n\x16WaitblockheightRequest\x12\x13\n\x0b\x62lockheight\x18\x01 \x01(\r\x12\x14\n\x07timeout\x18\x02 \x01(\rH\x00\x88\x01\x01\x42\n\n\x08_timeout\".\n\x17WaitblockheightResponse\x12\x13\n\x0b\x62lockheight\x18\x01 \x01(\r\"\x84\x02\n\x0bWaitRequest\x12\x31\n\tsubsystem\x18\x01 \x01(\x0e\x32\x1e.cln.WaitRequest.WaitSubsystem\x12\x31\n\tindexname\x18\x02 \x01(\x0e\x32\x1e.cln.WaitRequest.WaitIndexname\x12\x11\n\tnextvalue\x18\x03 \x01(\x04\"D\n\rWaitSubsystem\x12\x0c\n\x08INVOICES\x10\x00\x12\x0c\n\x08\x46ORWARDS\x10\x01\x12\x0c\n\x08SENDPAYS\x10\x02\x12\t\n\x05HTLCS\x10\x03\"6\n\rWaitIndexname\x12\x0b\n\x07\x43REATED\x10\x00\x12\x0b\n\x07UPDATED\x10\x01\x12\x0b\n\x07\x44\x45LETED\x10\x02\"\xf5\x03\n\x0cWaitResponse\x12\x32\n\tsubsystem\x18\x01 \x01(\x0e\x32\x1f.cln.WaitResponse.WaitSubsystem\x12\x14\n\x07\x63reated\x18\x02 \x01(\x04H\x00\x88\x01\x01\x12\x14\n\x07updated\x18\x03 \x01(\x04H\x01\x88\x01\x01\x12\x14\n\x07\x64\x65leted\x18\x04 \x01(\x04H\x02\x88\x01\x01\x12&\n\x07\x64\x65tails\x18\x05 \x01(\x0b\x32\x10.cln.WaitDetailsH\x03\x88\x01\x01\x12(\n\x08\x66orwards\x18\x06 \x01(\x0b\x32\x11.cln.WaitForwardsH\x04\x88\x01\x01\x12(\n\x08invoices\x18\x07 \x01(\x0b\x32\x11.cln.WaitInvoicesH\x05\x88\x01\x01\x12(\n\x08sendpays\x18\x08 \x01(\x0b\x32\x11.cln.WaitSendpaysH\x06\x88\x01\x01\x12\"\n\x05htlcs\x18\t \x01(\x0b\x32\x0e.cln.WaitHtlcsH\x07\x88\x01\x01\"D\n\rWaitSubsystem\x12\x0c\n\x08INVOICES\x10\x00\x12\x0c\n\x08\x46ORWARDS\x10\x01\x12\x0c\n\x08SENDPAYS\x10\x02\x12\t\n\x05HTLCS\x10\x03\x42\n\n\x08_createdB\n\n\x08_updatedB\n\n\x08_deletedB\n\n\x08_detailsB\x0b\n\t_forwardsB\x0b\n\t_invoicesB\x0b\n\t_sendpaysB\x08\n\x06_htlcs\"\xcb\x02\n\x0cWaitForwards\x12\x39\n\x06status\x18\x01 \x01(\x0e\x32$.cln.WaitForwards.WaitForwardsStatusH\x00\x88\x01\x01\x12\x17\n\nin_channel\x18\x02 \x01(\tH\x01\x88\x01\x01\x12\x17\n\nin_htlc_id\x18\x03 \x01(\x04H\x02\x88\x01\x01\x12!\n\x07in_msat\x18\x04 \x01(\x0b\x32\x0b.cln.AmountH\x03\x88\x01\x01\x12\x18\n\x0bout_channel\x18\x05 \x01(\tH\x04\x88\x01\x01\"L\n\x12WaitForwardsStatus\x12\x0b\n\x07OFFERED\x10\x00\x12\x0b\n\x07SETTLED\x10\x01\x12\n\n\x06\x46\x41ILED\x10\x02\x12\x10\n\x0cLOCAL_FAILED\x10\x03\x42\t\n\x07_statusB\r\n\x0b_in_channelB\r\n\x0b_in_htlc_idB\n\n\x08_in_msatB\x0e\n\x0c_out_channel\"\x95\x02\n\x0cWaitInvoices\x12\x39\n\x06status\x18\x01 \x01(\x0e\x32$.cln.WaitInvoices.WaitInvoicesStatusH\x00\x88\x01\x01\x12\x12\n\x05label\x18\x02 \x01(\tH\x01\x88\x01\x01\x12\x18\n\x0b\x64\x65scription\x18\x03 \x01(\tH\x02\x88\x01\x01\x12\x13\n\x06\x62olt11\x18\x04 \x01(\tH\x03\x88\x01\x01\x12\x13\n\x06\x62olt12\x18\x05 \x01(\tH\x04\x88\x01\x01\"7\n\x12WaitInvoicesStatus\x12\n\n\x06UNPAID\x10\x00\x12\x08\n\x04PAID\x10\x01\x12\x0b\n\x07\x45XPIRED\x10\x02\x42\t\n\x07_statusB\x08\n\x06_labelB\x0e\n\x0c_descriptionB\t\n\x07_bolt11B\t\n\x07_bolt12\"\xff\x01\n\x0cWaitSendpays\x12\x39\n\x06status\x18\x01 \x01(\x0e\x32$.cln.WaitSendpays.WaitSendpaysStatusH\x00\x88\x01\x01\x12\x13\n\x06partid\x18\x02 \x01(\x04H\x01\x88\x01\x01\x12\x14\n\x07groupid\x18\x03 \x01(\x04H\x02\x88\x01\x01\x12\x19\n\x0cpayment_hash\x18\x04 \x01(\x0cH\x03\x88\x01\x01\";\n\x12WaitSendpaysStatus\x12\x0b\n\x07PENDING\x10\x00\x12\n\n\x06\x46\x41ILED\x10\x01\x12\x0c\n\x08\x43OMPLETE\x10\x02\x42\t\n\x07_statusB\t\n\x07_partidB\n\n\x08_groupidB\x0f\n\r_payment_hash\"\xa8\x07\n\tWaitHtlcs\x12\x31\n\x05state\x18\x01 \x01(\x0e\x32\x1d.cln.WaitHtlcs.WaitHtlcsStateH\x00\x88\x01\x01\x12\x14\n\x07htlc_id\x18\x02 \x01(\x04H\x01\x88\x01\x01\x12\x1d\n\x10short_channel_id\x18\x03 \x01(\tH\x02\x88\x01\x01\x12\x18\n\x0b\x63ltv_expiry\x18\x04 \x01(\rH\x03\x88\x01\x01\x12%\n\x0b\x61mount_msat\x18\x05 \x01(\x0b\x32\x0b.cln.AmountH\x04\x88\x01\x01\x12\x39\n\tdirection\x18\x06 \x01(\x0e\x32!.cln.WaitHtlcs.WaitHtlcsDirectionH\x05\x88\x01\x01\x12\x19\n\x0cpayment_hash\x18\x07 \x01(\x0cH\x06\x88\x01\x01\"\x8a\x04\n\x0eWaitHtlcsState\x12\x11\n\rSENT_ADD_HTLC\x10\x00\x12\x13\n\x0fSENT_ADD_COMMIT\x10\x01\x12\x17\n\x13RCVD_ADD_REVOCATION\x10\x02\x12\x17\n\x13RCVD_ADD_ACK_COMMIT\x10\x03\x12\x1b\n\x17SENT_ADD_ACK_REVOCATION\x10\x04\x12\x14\n\x10RCVD_REMOVE_HTLC\x10\x05\x12\x16\n\x12RCVD_REMOVE_COMMIT\x10\x06\x12\x1a\n\x16SENT_REMOVE_REVOCATION\x10\x07\x12\x1a\n\x16SENT_REMOVE_ACK_COMMIT\x10\x08\x12\x1e\n\x1aRCVD_REMOVE_ACK_REVOCATION\x10\t\x12\x11\n\rRCVD_ADD_HTLC\x10\n\x12\x13\n\x0fRCVD_ADD_COMMIT\x10\x0b\x12\x17\n\x13SENT_ADD_REVOCATION\x10\x0c\x12\x17\n\x13SENT_ADD_ACK_COMMIT\x10\r\x12\x1b\n\x17RCVD_ADD_ACK_REVOCATION\x10\x0e\x12\x14\n\x10SENT_REMOVE_HTLC\x10\x0f\x12\x16\n\x12SENT_REMOVE_COMMIT\x10\x10\x12\x1a\n\x16RCVD_REMOVE_REVOCATION\x10\x11\x12\x1a\n\x16RCVD_REMOVE_ACK_COMMIT\x10\x12\x12\x1e\n\x1aSENT_REMOVE_ACK_REVOCATION\x10\x13\"%\n\x12WaitHtlcsDirection\x12\x07\n\x03OUT\x10\x00\x12\x06\n\x02IN\x10\x01\x42\x08\n\x06_stateB\n\n\x08_htlc_idB\x13\n\x11_short_channel_idB\x0e\n\x0c_cltv_expiryB\x0e\n\x0c_amount_msatB\x0c\n\n_directionB\x0f\n\r_payment_hash\"\xfc\x04\n\x0bWaitDetails\x12\x37\n\x06status\x18\x01 \x01(\x0e\x32\".cln.WaitDetails.WaitDetailsStatusH\x00\x88\x01\x01\x12\x12\n\x05label\x18\x02 \x01(\tH\x01\x88\x01\x01\x12\x18\n\x0b\x64\x65scription\x18\x03 \x01(\tH\x02\x88\x01\x01\x12\x13\n\x06\x62olt11\x18\x04 \x01(\tH\x03\x88\x01\x01\x12\x13\n\x06\x62olt12\x18\x05 \x01(\tH\x04\x88\x01\x01\x12\x13\n\x06partid\x18\x06 \x01(\x04H\x05\x88\x01\x01\x12\x14\n\x07groupid\x18\x07 \x01(\x04H\x06\x88\x01\x01\x12\x19\n\x0cpayment_hash\x18\x08 \x01(\x0cH\x07\x88\x01\x01\x12\x17\n\nin_channel\x18\t \x01(\tH\x08\x88\x01\x01\x12\x17\n\nin_htlc_id\x18\n \x01(\x04H\t\x88\x01\x01\x12!\n\x07in_msat\x18\x0b \x01(\x0b\x32\x0b.cln.AmountH\n\x88\x01\x01\x12\x18\n\x0bout_channel\x18\x0c \x01(\tH\x0b\x88\x01\x01\"\x89\x01\n\x11WaitDetailsStatus\x12\n\n\x06UNPAID\x10\x00\x12\x08\n\x04PAID\x10\x01\x12\x0b\n\x07\x45XPIRED\x10\x02\x12\x0b\n\x07PENDING\x10\x03\x12\n\n\x06\x46\x41ILED\x10\x04\x12\x0c\n\x08\x43OMPLETE\x10\x05\x12\x0b\n\x07OFFERED\x10\x06\x12\x0b\n\x07SETTLED\x10\x07\x12\x10\n\x0cLOCAL_FAILED\x10\x08\x42\t\n\x07_statusB\x08\n\x06_labelB\x0e\n\x0c_descriptionB\t\n\x07_bolt11B\t\n\x07_bolt12B\t\n\x07_partidB\n\n\x08_groupidB\x0f\n\r_payment_hashB\r\n\x0b_in_channelB\r\n\x0b_in_htlc_idB\n\n\x08_in_msatB\x0e\n\x0c_out_channel\"4\n\x12ListconfigsRequest\x12\x13\n\x06\x63onfig\x18\x01 \x01(\tH\x00\x88\x01\x01\x42\t\n\x07_config\"P\n\x13ListconfigsResponse\x12-\n\x07\x63onfigs\x18\x01 \x01(\x0b\x32\x17.cln.ListconfigsConfigsH\x00\x88\x01\x01\x42\n\n\x08_configs\"\xe9.\n\x12ListconfigsConfigs\x12.\n\x04\x63onf\x18\x01 \x01(\x0b\x32\x1b.cln.ListconfigsConfigsConfH\x00\x88\x01\x01\x12\x38\n\tdeveloper\x18\x02 \x01(\x0b\x32 .cln.ListconfigsConfigsDeveloperH\x01\x88\x01\x01\x12?\n\rclear_plugins\x18\x03 \x01(\x0b\x32#.cln.ListconfigsConfigsClearpluginsH\x02\x88\x01\x01\x12;\n\x0b\x64isable_mpp\x18\x04 \x01(\x0b\x32!.cln.ListconfigsConfigsDisablemppH\x03\x88\x01\x01\x12\x34\n\x07mainnet\x18\x05 \x01(\x0b\x32\x1e.cln.ListconfigsConfigsMainnetH\x04\x88\x01\x01\x12\x34\n\x07regtest\x18\x06 \x01(\x0b\x32\x1e.cln.ListconfigsConfigsRegtestH\x05\x88\x01\x01\x12\x32\n\x06signet\x18\x07 \x01(\x0b\x32\x1d.cln.ListconfigsConfigsSignetH\x06\x88\x01\x01\x12\x34\n\x07testnet\x18\x08 \x01(\x0b\x32\x1e.cln.ListconfigsConfigsTestnetH\x07\x88\x01\x01\x12\x45\n\x10important_plugin\x18\t \x01(\x0b\x32&.cln.ListconfigsConfigsImportantpluginH\x08\x88\x01\x01\x12\x32\n\x06plugin\x18\n \x01(\x0b\x32\x1d.cln.ListconfigsConfigsPluginH\t\x88\x01\x01\x12\x39\n\nplugin_dir\x18\x0b \x01(\x0b\x32 .cln.ListconfigsConfigsPlugindirH\n\x88\x01\x01\x12?\n\rlightning_dir\x18\x0c \x01(\x0b\x32#.cln.ListconfigsConfigsLightningdirH\x0b\x88\x01\x01\x12\x34\n\x07network\x18\r \x01(\x0b\x32\x1e.cln.ListconfigsConfigsNetworkH\x0c\x88\x01\x01\x12N\n\x15\x61llow_deprecated_apis\x18\x0e \x01(\x0b\x32*.cln.ListconfigsConfigsAllowdeprecatedapisH\r\x88\x01\x01\x12\x35\n\x08rpc_file\x18\x0f \x01(\x0b\x32\x1e.cln.ListconfigsConfigsRpcfileH\x0e\x88\x01\x01\x12\x41\n\x0e\x64isable_plugin\x18\x10 \x01(\x0b\x32$.cln.ListconfigsConfigsDisablepluginH\x0f\x88\x01\x01\x12\x44\n\x10\x61lways_use_proxy\x18\x11 \x01(\x0b\x32%.cln.ListconfigsConfigsAlwaysuseproxyH\x10\x88\x01\x01\x12\x32\n\x06\x64\x61\x65mon\x18\x12 \x01(\x0b\x32\x1d.cln.ListconfigsConfigsDaemonH\x11\x88\x01\x01\x12\x32\n\x06wallet\x18\x13 \x01(\x0b\x32\x1d.cln.ListconfigsConfigsWalletH\x12\x88\x01\x01\x12\x41\n\x0elarge_channels\x18\x14 \x01(\x0b\x32$.cln.ListconfigsConfigsLargechannelsH\x13\x88\x01\x01\x12P\n\x16\x65xperimental_dual_fund\x18\x15 \x01(\x0b\x32+.cln.ListconfigsConfigsExperimentaldualfundH\x14\x88\x01\x01\x12O\n\x15\x65xperimental_splicing\x18\x16 \x01(\x0b\x32+.cln.ListconfigsConfigsExperimentalsplicingH\x15\x88\x01\x01\x12Z\n\x1b\x65xperimental_onion_messages\x18\x17 \x01(\x0b\x32\x30.cln.ListconfigsConfigsExperimentalonionmessagesH\x16\x88\x01\x01\x12K\n\x13\x65xperimental_offers\x18\x18 \x01(\x0b\x32).cln.ListconfigsConfigsExperimentaloffersH\x17\x88\x01\x01\x12i\n#experimental_shutdown_wrong_funding\x18\x19 \x01(\x0b\x32\x37.cln.ListconfigsConfigsExperimentalshutdownwrongfundingH\x18\x88\x01\x01\x12V\n\x19\x65xperimental_peer_storage\x18\x1a \x01(\x0b\x32..cln.ListconfigsConfigsExperimentalpeerstorageH\x19\x88\x01\x01\x12M\n\x14\x65xperimental_anchors\x18\x1b \x01(\x0b\x32*.cln.ListconfigsConfigsExperimentalanchorsH\x1a\x88\x01\x01\x12\x45\n\x10\x64\x61tabase_upgrade\x18\x1c \x01(\x0b\x32&.cln.ListconfigsConfigsDatabaseupgradeH\x1b\x88\x01\x01\x12,\n\x03rgb\x18\x1d \x01(\x0b\x32\x1a.cln.ListconfigsConfigsRgbH\x1c\x88\x01\x01\x12\x30\n\x05\x61lias\x18\x1e \x01(\x0b\x32\x1c.cln.ListconfigsConfigsAliasH\x1d\x88\x01\x01\x12\x35\n\x08pid_file\x18\x1f \x01(\x0b\x32\x1e.cln.ListconfigsConfigsPidfileH\x1e\x88\x01\x01\x12\x46\n\x11ignore_fee_limits\x18 \x01(\x0b\x32&.cln.ListconfigsConfigsIgnorefeelimitsH\x1f\x88\x01\x01\x12\x45\n\x10watchtime_blocks\x18! \x01(\x0b\x32&.cln.ListconfigsConfigsWatchtimeblocksH \x88\x01\x01\x12J\n\x13max_locktime_blocks\x18\" \x01(\x0b\x32(.cln.ListconfigsConfigsMaxlocktimeblocksH!\x88\x01\x01\x12\x45\n\x10\x66unding_confirms\x18# \x01(\x0b\x32&.cln.ListconfigsConfigsFundingconfirmsH\"\x88\x01\x01\x12\x39\n\ncltv_delta\x18$ \x01(\x0b\x32 .cln.ListconfigsConfigsCltvdeltaH#\x88\x01\x01\x12\x39\n\ncltv_final\x18% \x01(\x0b\x32 .cln.ListconfigsConfigsCltvfinalH$\x88\x01\x01\x12;\n\x0b\x63ommit_time\x18& \x01(\x0b\x32!.cln.ListconfigsConfigsCommittimeH%\x88\x01\x01\x12\x35\n\x08\x66\x65\x65_base\x18\' \x01(\x0b\x32\x1e.cln.ListconfigsConfigsFeebaseH&\x88\x01\x01\x12\x32\n\x06rescan\x18( \x01(\x0b\x32\x1d.cln.ListconfigsConfigsRescanH\'\x88\x01\x01\x12\x42\n\x0f\x66\x65\x65_per_satoshi\x18) \x01(\x0b\x32$.cln.ListconfigsConfigsFeepersatoshiH(\x88\x01\x01\x12L\n\x14max_concurrent_htlcs\x18* \x01(\x0b\x32).cln.ListconfigsConfigsMaxconcurrenthtlcsH)\x88\x01\x01\x12\x46\n\x11htlc_minimum_msat\x18+ \x01(\x0b\x32&.cln.ListconfigsConfigsHtlcminimummsatH*\x88\x01\x01\x12\x46\n\x11htlc_maximum_msat\x18, \x01(\x0b\x32&.cln.ListconfigsConfigsHtlcmaximummsatH+\x88\x01\x01\x12X\n\x1bmax_dust_htlc_exposure_msat\x18- \x01(\x0b\x32..cln.ListconfigsConfigsMaxdusthtlcexposuremsatH,\x88\x01\x01\x12\x44\n\x10min_capacity_sat\x18. \x01(\x0b\x32%.cln.ListconfigsConfigsMincapacitysatH-\x88\x01\x01\x12.\n\x04\x61\x64\x64r\x18/ \x01(\x0b\x32\x1b.cln.ListconfigsConfigsAddrH.\x88\x01\x01\x12?\n\rannounce_addr\x18\x30 \x01(\x0b\x32#.cln.ListconfigsConfigsAnnounceaddrH/\x88\x01\x01\x12\x37\n\tbind_addr\x18\x31 \x01(\x0b\x32\x1f.cln.ListconfigsConfigsBindaddrH0\x88\x01\x01\x12\x34\n\x07offline\x18\x32 \x01(\x0b\x32\x1e.cln.ListconfigsConfigsOfflineH1\x88\x01\x01\x12:\n\nautolisten\x18\x33 \x01(\x0b\x32!.cln.ListconfigsConfigsAutolistenH2\x88\x01\x01\x12\x30\n\x05proxy\x18\x34 \x01(\x0b\x32\x1c.cln.ListconfigsConfigsProxyH3\x88\x01\x01\x12;\n\x0b\x64isable_dns\x18\x35 \x01(\x0b\x32!.cln.ListconfigsConfigsDisablednsH4\x88\x01\x01\x12T\n\x18\x61nnounce_addr_discovered\x18\x36 \x01(\x0b\x32-.cln.ListconfigsConfigsAnnounceaddrdiscoveredH5\x88\x01\x01\x12]\n\x1d\x61nnounce_addr_discovered_port\x18\x37 \x01(\x0b\x32\x31.cln.ListconfigsConfigsAnnounceaddrdiscoveredportH6\x88\x01\x01\x12?\n\rencrypted_hsm\x18\x38 \x01(\x0b\x32#.cln.ListconfigsConfigsEncryptedhsmH7\x88\x01\x01\x12>\n\rrpc_file_mode\x18\x39 \x01(\x0b\x32\".cln.ListconfigsConfigsRpcfilemodeH8\x88\x01\x01\x12\x37\n\tlog_level\x18: \x01(\x0b\x32\x1f.cln.ListconfigsConfigsLoglevelH9\x88\x01\x01\x12\x39\n\nlog_prefix\x18; \x01(\x0b\x32 .cln.ListconfigsConfigsLogprefixH:\x88\x01\x01\x12\x35\n\x08log_file\x18< \x01(\x0b\x32\x1e.cln.ListconfigsConfigsLogfileH;\x88\x01\x01\x12\x41\n\x0elog_timestamps\x18= \x01(\x0b\x32$.cln.ListconfigsConfigsLogtimestampsH<\x88\x01\x01\x12\x41\n\x0e\x66orce_feerates\x18> \x01(\x0b\x32$.cln.ListconfigsConfigsForcefeeratesH=\x88\x01\x01\x12\x38\n\tsubdaemon\x18? \x01(\x0b\x32 .cln.ListconfigsConfigsSubdaemonH>\x88\x01\x01\x12Q\n\x16\x66\x65tchinvoice_noconnect\x18@ \x01(\x0b\x32,.cln.ListconfigsConfigsFetchinvoicenoconnectH?\x88\x01\x01\x12L\n\x14tor_service_password\x18\x42 \x01(\x0b\x32).cln.ListconfigsConfigsTorservicepasswordH@\x88\x01\x01\x12\x46\n\x11\x61nnounce_addr_dns\x18\x43 \x01(\x0b\x32&.cln.ListconfigsConfigsAnnounceaddrdnsHA\x88\x01\x01\x12T\n\x18require_confirmed_inputs\x18\x44 \x01(\x0b\x32-.cln.ListconfigsConfigsRequireconfirmedinputsHB\x88\x01\x01\x12\x39\n\ncommit_fee\x18\x45 \x01(\x0b\x32 .cln.ListconfigsConfigsCommitfeeHC\x88\x01\x01\x12N\n\x15\x63ommit_feerate_offset\x18\x46 \x01(\x0b\x32*.cln.ListconfigsConfigsCommitfeerateoffsetHD\x88\x01\x01\x12T\n\x18\x61utoconnect_seeker_peers\x18G \x01(\x0b\x32-.cln.ListconfigsConfigsAutoconnectseekerpeersHE\x88\x01\x01\x42\x07\n\x05_confB\x0c\n\n_developerB\x10\n\x0e_clear_pluginsB\x0e\n\x0c_disable_mppB\n\n\x08_mainnetB\n\n\x08_regtestB\t\n\x07_signetB\n\n\x08_testnetB\x13\n\x11_important_pluginB\t\n\x07_pluginB\r\n\x0b_plugin_dirB\x10\n\x0e_lightning_dirB\n\n\x08_networkB\x18\n\x16_allow_deprecated_apisB\x0b\n\t_rpc_fileB\x11\n\x0f_disable_pluginB\x13\n\x11_always_use_proxyB\t\n\x07_daemonB\t\n\x07_walletB\x11\n\x0f_large_channelsB\x19\n\x17_experimental_dual_fundB\x18\n\x16_experimental_splicingB\x1e\n\x1c_experimental_onion_messagesB\x16\n\x14_experimental_offersB&\n$_experimental_shutdown_wrong_fundingB\x1c\n\x1a_experimental_peer_storageB\x17\n\x15_experimental_anchorsB\x13\n\x11_database_upgradeB\x06\n\x04_rgbB\x08\n\x06_aliasB\x0b\n\t_pid_fileB\x14\n\x12_ignore_fee_limitsB\x13\n\x11_watchtime_blocksB\x16\n\x14_max_locktime_blocksB\x13\n\x11_funding_confirmsB\r\n\x0b_cltv_deltaB\r\n\x0b_cltv_finalB\x0e\n\x0c_commit_timeB\x0b\n\t_fee_baseB\t\n\x07_rescanB\x12\n\x10_fee_per_satoshiB\x17\n\x15_max_concurrent_htlcsB\x14\n\x12_htlc_minimum_msatB\x14\n\x12_htlc_maximum_msatB\x1e\n\x1c_max_dust_htlc_exposure_msatB\x13\n\x11_min_capacity_satB\x07\n\x05_addrB\x10\n\x0e_announce_addrB\x0c\n\n_bind_addrB\n\n\x08_offlineB\r\n\x0b_autolistenB\x08\n\x06_proxyB\x0e\n\x0c_disable_dnsB\x1b\n\x19_announce_addr_discoveredB \n\x1e_announce_addr_discovered_portB\x10\n\x0e_encrypted_hsmB\x10\n\x0e_rpc_file_modeB\x0c\n\n_log_levelB\r\n\x0b_log_prefixB\x0b\n\t_log_fileB\x11\n\x0f_log_timestampsB\x11\n\x0f_force_feeratesB\x0c\n\n_subdaemonB\x19\n\x17_fetchinvoice_noconnectB\x17\n\x15_tor_service_passwordB\x14\n\x12_announce_addr_dnsB\x1b\n\x19_require_confirmed_inputsB\r\n\x0b_commit_feeB\x18\n\x16_commit_feerate_offsetB\x1b\n\x19_autoconnect_seeker_peers\"\xa2\x01\n\x16ListconfigsConfigsConf\x12\x11\n\tvalue_str\x18\x01 \x01(\t\x12H\n\x06source\x18\x02 \x01(\x0e\x32\x38.cln.ListconfigsConfigsConf.ListconfigsConfigsConfSource\"+\n\x1cListconfigsConfigsConfSource\x12\x0b\n\x07\x43MDLINE\x10\x00\":\n\x1bListconfigsConfigsDeveloper\x12\x0b\n\x03set\x18\x01 \x01(\x08\x12\x0e\n\x06source\x18\x02 \x01(\t\"=\n\x1eListconfigsConfigsClearplugins\x12\x0b\n\x03set\x18\x01 \x01(\x08\x12\x0e\n\x06source\x18\x02 \x01(\t\"[\n\x1cListconfigsConfigsDisablempp\x12\x0b\n\x03set\x18\x01 \x01(\x08\x12\x0e\n\x06source\x18\x02 \x01(\t\x12\x13\n\x06plugin\x18\x03 \x01(\tH\x00\x88\x01\x01\x42\t\n\x07_plugin\"8\n\x19ListconfigsConfigsMainnet\x12\x0b\n\x03set\x18\x01 \x01(\x08\x12\x0e\n\x06source\x18\x02 \x01(\t\"8\n\x19ListconfigsConfigsRegtest\x12\x0b\n\x03set\x18\x01 \x01(\x08\x12\x0e\n\x06source\x18\x02 \x01(\t\"7\n\x18ListconfigsConfigsSignet\x12\x0b\n\x03set\x18\x01 \x01(\x08\x12\x0e\n\x06source\x18\x02 \x01(\t\"8\n\x19ListconfigsConfigsTestnet\x12\x0b\n\x03set\x18\x01 \x01(\x08\x12\x0e\n\x06source\x18\x02 \x01(\t\"H\n!ListconfigsConfigsImportantplugin\x12\x12\n\nvalues_str\x18\x01 \x03(\t\x12\x0f\n\x07sources\x18\x02 \x03(\t\"?\n\x18ListconfigsConfigsPlugin\x12\x12\n\nvalues_str\x18\x01 \x03(\t\x12\x0f\n\x07sources\x18\x02 \x03(\t\"B\n\x1bListconfigsConfigsPlugindir\x12\x12\n\nvalues_str\x18\x01 \x03(\t\x12\x0f\n\x07sources\x18\x02 \x03(\t\"C\n\x1eListconfigsConfigsLightningdir\x12\x11\n\tvalue_str\x18\x01 \x01(\t\x12\x0e\n\x06source\x18\x02 \x01(\t\">\n\x19ListconfigsConfigsNetwork\x12\x11\n\tvalue_str\x18\x01 \x01(\t\x12\x0e\n\x06source\x18\x02 \x01(\t\"K\n%ListconfigsConfigsAllowdeprecatedapis\x12\x12\n\nvalue_bool\x18\x01 \x01(\x08\x12\x0e\n\x06source\x18\x02 \x01(\t\">\n\x19ListconfigsConfigsRpcfile\x12\x11\n\tvalue_str\x18\x01 \x01(\t\x12\x0e\n\x06source\x18\x02 \x01(\t\"F\n\x1fListconfigsConfigsDisableplugin\x12\x12\n\nvalues_str\x18\x01 \x03(\t\x12\x0f\n\x07sources\x18\x02 \x03(\t\"F\n ListconfigsConfigsAlwaysuseproxy\x12\x12\n\nvalue_bool\x18\x01 \x01(\x08\x12\x0e\n\x06source\x18\x02 \x01(\t\"7\n\x18ListconfigsConfigsDaemon\x12\x0b\n\x03set\x18\x01 \x01(\x08\x12\x0e\n\x06source\x18\x02 \x01(\t\"=\n\x18ListconfigsConfigsWallet\x12\x11\n\tvalue_str\x18\x01 \x01(\t\x12\x0e\n\x06source\x18\x02 \x01(\t\">\n\x1fListconfigsConfigsLargechannels\x12\x0b\n\x03set\x18\x01 \x01(\x08\x12\x0e\n\x06source\x18\x02 \x01(\t\"E\n&ListconfigsConfigsExperimentaldualfund\x12\x0b\n\x03set\x18\x01 \x01(\x08\x12\x0e\n\x06source\x18\x02 \x01(\t\"E\n&ListconfigsConfigsExperimentalsplicing\x12\x0b\n\x03set\x18\x01 \x01(\x08\x12\x0e\n\x06source\x18\x02 \x01(\t\"J\n+ListconfigsConfigsExperimentalonionmessages\x12\x0b\n\x03set\x18\x01 \x01(\x08\x12\x0e\n\x06source\x18\x02 \x01(\t\"C\n$ListconfigsConfigsExperimentaloffers\x12\x0b\n\x03set\x18\x01 \x01(\x08\x12\x0e\n\x06source\x18\x02 \x01(\t\"Q\n2ListconfigsConfigsExperimentalshutdownwrongfunding\x12\x0b\n\x03set\x18\x01 \x01(\x08\x12\x0e\n\x06source\x18\x02 \x01(\t\"H\n)ListconfigsConfigsExperimentalpeerstorage\x12\x0b\n\x03set\x18\x01 \x01(\x08\x12\x0e\n\x06source\x18\x02 \x01(\t\"D\n%ListconfigsConfigsExperimentalanchors\x12\x0b\n\x03set\x18\x01 \x01(\x08\x12\x0e\n\x06source\x18\x02 \x01(\t\"G\n!ListconfigsConfigsDatabaseupgrade\x12\x12\n\nvalue_bool\x18\x01 \x01(\x08\x12\x0e\n\x06source\x18\x02 \x01(\t\":\n\x15ListconfigsConfigsRgb\x12\x11\n\tvalue_str\x18\x01 \x01(\x0c\x12\x0e\n\x06source\x18\x02 \x01(\t\"<\n\x17ListconfigsConfigsAlias\x12\x11\n\tvalue_str\x18\x01 \x01(\t\x12\x0e\n\x06source\x18\x02 \x01(\t\">\n\x19ListconfigsConfigsPidfile\x12\x11\n\tvalue_str\x18\x01 \x01(\t\x12\x0e\n\x06source\x18\x02 \x01(\t\"G\n!ListconfigsConfigsIgnorefeelimits\x12\x12\n\nvalue_bool\x18\x01 \x01(\x08\x12\x0e\n\x06source\x18\x02 \x01(\t\"F\n!ListconfigsConfigsWatchtimeblocks\x12\x11\n\tvalue_int\x18\x01 \x01(\r\x12\x0e\n\x06source\x18\x02 \x01(\t\"H\n#ListconfigsConfigsMaxlocktimeblocks\x12\x11\n\tvalue_int\x18\x01 \x01(\r\x12\x0e\n\x06source\x18\x02 \x01(\t\"F\n!ListconfigsConfigsFundingconfirms\x12\x11\n\tvalue_int\x18\x01 \x01(\r\x12\x0e\n\x06source\x18\x02 \x01(\t\"@\n\x1bListconfigsConfigsCltvdelta\x12\x11\n\tvalue_int\x18\x01 \x01(\r\x12\x0e\n\x06source\x18\x02 \x01(\t\"@\n\x1bListconfigsConfigsCltvfinal\x12\x11\n\tvalue_int\x18\x01 \x01(\r\x12\x0e\n\x06source\x18\x02 \x01(\t\"A\n\x1cListconfigsConfigsCommittime\x12\x11\n\tvalue_int\x18\x01 \x01(\r\x12\x0e\n\x06source\x18\x02 \x01(\t\">\n\x19ListconfigsConfigsFeebase\x12\x11\n\tvalue_int\x18\x01 \x01(\r\x12\x0e\n\x06source\x18\x02 \x01(\t\"=\n\x18ListconfigsConfigsRescan\x12\x11\n\tvalue_int\x18\x01 \x01(\x12\x12\x0e\n\x06source\x18\x02 \x01(\t\"D\n\x1fListconfigsConfigsFeepersatoshi\x12\x11\n\tvalue_int\x18\x01 \x01(\r\x12\x0e\n\x06source\x18\x02 \x01(\t\"I\n$ListconfigsConfigsMaxconcurrenthtlcs\x12\x11\n\tvalue_int\x18\x01 \x01(\r\x12\x0e\n\x06source\x18\x02 \x01(\t\"T\n!ListconfigsConfigsHtlcminimummsat\x12\x1f\n\nvalue_msat\x18\x01 \x01(\x0b\x32\x0b.cln.Amount\x12\x0e\n\x06source\x18\x02 \x01(\t\"T\n!ListconfigsConfigsHtlcmaximummsat\x12\x1f\n\nvalue_msat\x18\x01 \x01(\x0b\x32\x0b.cln.Amount\x12\x0e\n\x06source\x18\x02 \x01(\t\"\\\n)ListconfigsConfigsMaxdusthtlcexposuremsat\x12\x1f\n\nvalue_msat\x18\x01 \x01(\x0b\x32\x0b.cln.Amount\x12\x0e\n\x06source\x18\x02 \x01(\t\"g\n ListconfigsConfigsMincapacitysat\x12\x11\n\tvalue_int\x18\x01 \x01(\x04\x12\x0e\n\x06source\x18\x02 \x01(\t\x12\x14\n\x07\x64ynamic\x18\x03 \x01(\x08H\x00\x88\x01\x01\x42\n\n\x08_dynamic\"=\n\x16ListconfigsConfigsAddr\x12\x12\n\nvalues_str\x18\x01 \x03(\t\x12\x0f\n\x07sources\x18\x02 \x03(\t\"E\n\x1eListconfigsConfigsAnnounceaddr\x12\x12\n\nvalues_str\x18\x01 \x03(\t\x12\x0f\n\x07sources\x18\x02 \x03(\t\"A\n\x1aListconfigsConfigsBindaddr\x12\x12\n\nvalues_str\x18\x01 \x03(\t\x12\x0f\n\x07sources\x18\x02 \x03(\t\"8\n\x19ListconfigsConfigsOffline\x12\x0b\n\x03set\x18\x01 \x01(\x08\x12\x0e\n\x06source\x18\x02 \x01(\t\"B\n\x1cListconfigsConfigsAutolisten\x12\x12\n\nvalue_bool\x18\x01 \x01(\x08\x12\x0e\n\x06source\x18\x02 \x01(\t\"<\n\x17ListconfigsConfigsProxy\x12\x11\n\tvalue_str\x18\x01 \x01(\t\x12\x0e\n\x06source\x18\x02 \x01(\t\";\n\x1cListconfigsConfigsDisabledns\x12\x0b\n\x03set\x18\x01 \x01(\x08\x12\x0e\n\x06source\x18\x02 \x01(\t\"\x80\x02\n(ListconfigsConfigsAnnounceaddrdiscovered\x12q\n\tvalue_str\x18\x01 \x01(\x0e\x32^.cln.ListconfigsConfigsAnnounceaddrdiscovered.ListconfigsConfigsAnnounceaddrdiscoveredValueStr\x12\x0e\n\x06source\x18\x02 \x01(\t\"Q\n0ListconfigsConfigsAnnounceaddrdiscoveredValueStr\x12\x08\n\x04TRUE\x10\x00\x12\t\n\x05\x46\x41LSE\x10\x01\x12\x08\n\x04\x41UTO\x10\x02\"Q\n,ListconfigsConfigsAnnounceaddrdiscoveredport\x12\x11\n\tvalue_int\x18\x01 \x01(\r\x12\x0e\n\x06source\x18\x02 \x01(\t\"=\n\x1eListconfigsConfigsEncryptedhsm\x12\x0b\n\x03set\x18\x01 \x01(\x08\x12\x0e\n\x06source\x18\x02 \x01(\t\"B\n\x1dListconfigsConfigsRpcfilemode\x12\x11\n\tvalue_str\x18\x01 \x01(\t\x12\x0e\n\x06source\x18\x02 \x01(\t\"?\n\x1aListconfigsConfigsLoglevel\x12\x11\n\tvalue_str\x18\x01 \x01(\t\x12\x0e\n\x06source\x18\x02 \x01(\t\"@\n\x1bListconfigsConfigsLogprefix\x12\x11\n\tvalue_str\x18\x01 \x01(\t\x12\x0e\n\x06source\x18\x02 \x01(\t\"@\n\x19ListconfigsConfigsLogfile\x12\x12\n\nvalues_str\x18\x01 \x03(\t\x12\x0f\n\x07sources\x18\x02 \x03(\t\"E\n\x1fListconfigsConfigsLogtimestamps\x12\x12\n\nvalue_bool\x18\x01 \x01(\x08\x12\x0e\n\x06source\x18\x02 \x01(\t\"D\n\x1fListconfigsConfigsForcefeerates\x12\x11\n\tvalue_str\x18\x01 \x01(\t\x12\x0e\n\x06source\x18\x02 \x01(\t\"B\n\x1bListconfigsConfigsSubdaemon\x12\x12\n\nvalues_str\x18\x01 \x03(\t\x12\x0f\n\x07sources\x18\x02 \x03(\t\"f\n\'ListconfigsConfigsFetchinvoicenoconnect\x12\x0b\n\x03set\x18\x01 \x01(\x08\x12\x0e\n\x06source\x18\x02 \x01(\t\x12\x13\n\x06plugin\x18\x03 \x01(\tH\x00\x88\x01\x01\x42\t\n\x07_plugin\"I\n$ListconfigsConfigsTorservicepassword\x12\x11\n\tvalue_str\x18\x01 \x01(\t\x12\x0e\n\x06source\x18\x02 \x01(\t\"G\n!ListconfigsConfigsAnnounceaddrdns\x12\x12\n\nvalue_bool\x18\x01 \x01(\x08\x12\x0e\n\x06source\x18\x02 \x01(\t\"N\n(ListconfigsConfigsRequireconfirmedinputs\x12\x12\n\nvalue_bool\x18\x01 \x01(\x08\x12\x0e\n\x06source\x18\x02 \x01(\t\"@\n\x1bListconfigsConfigsCommitfee\x12\x11\n\tvalue_int\x18\x01 \x01(\x04\x12\x0e\n\x06source\x18\x02 \x01(\t\"J\n%ListconfigsConfigsCommitfeerateoffset\x12\x11\n\tvalue_int\x18\x01 \x01(\r\x12\x0e\n\x06source\x18\x02 \x01(\t\"M\n(ListconfigsConfigsAutoconnectseekerpeers\x12\x11\n\tvalue_int\x18\x01 \x01(\r\x12\x0e\n\x06source\x18\x02 \x01(\t\"\r\n\x0bStopRequest\"q\n\x0cStopResponse\x12\x31\n\x06result\x18\x01 \x01(\x0e\x32\x1c.cln.StopResponse.StopResultH\x00\x88\x01\x01\"#\n\nStopResult\x12\x15\n\x11SHUTDOWN_COMPLETE\x10\x00\x42\t\n\x07_result\"/\n\x0bHelpRequest\x12\x14\n\x07\x63ommand\x18\x01 \x01(\tH\x00\x88\x01\x01\x42\n\n\x08_command\"\x95\x01\n\x0cHelpResponse\x12\x1b\n\x04help\x18\x01 \x03(\x0b\x32\r.cln.HelpHelp\x12:\n\x0b\x66ormat_hint\x18\x02 \x01(\x0e\x32 .cln.HelpResponse.HelpFormathintH\x00\x88\x01\x01\"\x1c\n\x0eHelpFormathint\x12\n\n\x06SIMPLE\x10\x00\x42\x0e\n\x0c_format_hint\"\x1b\n\x08HelpHelp\x12\x0f\n\x07\x63ommand\x18\x01 \x01(\t\"g\n\x18PreapprovekeysendRequest\x12\x13\n\x0b\x64\x65stination\x18\x01 \x01(\x0c\x12\x14\n\x0cpayment_hash\x18\x02 \x01(\x0c\x12 \n\x0b\x61mount_msat\x18\x03 \x01(\x0b\x32\x0b.cln.Amount\"\x1b\n\x19PreapprovekeysendResponse\"*\n\x18PreapproveinvoiceRequest\x12\x0e\n\x06\x62olt11\x18\x01 \x01(\t\"\x1b\n\x19PreapproveinvoiceResponse\"\x15\n\x13StaticbackupRequest\"#\n\x14StaticbackupResponse\x12\x0b\n\x03scb\x18\x01 \x03(\x0c\"d\n\x16\x42kprchannelsapyRequest\x12\x17\n\nstart_time\x18\x01 \x01(\x04H\x00\x88\x01\x01\x12\x15\n\x08\x65nd_time\x18\x02 \x01(\x04H\x01\x88\x01\x01\x42\r\n\x0b_start_timeB\x0b\n\t_end_time\"P\n\x17\x42kprchannelsapyResponse\x12\x35\n\x0c\x63hannels_apy\x18\x01 \x03(\x0b\x32\x1f.cln.BkprchannelsapyChannelsApy\"\xf9\x06\n\x1a\x42kprchannelsapyChannelsApy\x12\x0f\n\x07\x61\x63\x63ount\x18\x01 \x01(\t\x12$\n\x0frouted_out_msat\x18\x02 \x01(\x0b\x32\x0b.cln.Amount\x12#\n\x0erouted_in_msat\x18\x03 \x01(\x0b\x32\x0b.cln.Amount\x12(\n\x13lease_fee_paid_msat\x18\x04 \x01(\x0b\x32\x0b.cln.Amount\x12*\n\x15lease_fee_earned_msat\x18\x05 \x01(\x0b\x32\x0b.cln.Amount\x12$\n\x0fpushed_out_msat\x18\x06 \x01(\x0b\x32\x0b.cln.Amount\x12#\n\x0epushed_in_msat\x18\x07 \x01(\x0b\x32\x0b.cln.Amount\x12+\n\x16our_start_balance_msat\x18\x08 \x01(\x0b\x32\x0b.cln.Amount\x12/\n\x1a\x63hannel_start_balance_msat\x18\t \x01(\x0b\x32\x0b.cln.Amount\x12\"\n\rfees_out_msat\x18\n \x01(\x0b\x32\x0b.cln.Amount\x12&\n\x0c\x66\x65\x65s_in_msat\x18\x0b \x01(\x0b\x32\x0b.cln.AmountH\x00\x88\x01\x01\x12\x17\n\x0futilization_out\x18\x0c \x01(\t\x12$\n\x17utilization_out_initial\x18\r \x01(\tH\x01\x88\x01\x01\x12\x16\n\x0eutilization_in\x18\x0e \x01(\t\x12#\n\x16utilization_in_initial\x18\x0f \x01(\tH\x02\x88\x01\x01\x12\x0f\n\x07\x61py_out\x18\x10 \x01(\t\x12\x1c\n\x0f\x61py_out_initial\x18\x11 \x01(\tH\x03\x88\x01\x01\x12\x0e\n\x06\x61py_in\x18\x12 \x01(\t\x12\x1b\n\x0e\x61py_in_initial\x18\x13 \x01(\tH\x04\x88\x01\x01\x12\x11\n\tapy_total\x18\x14 \x01(\t\x12\x1e\n\x11\x61py_total_initial\x18\x15 \x01(\tH\x05\x88\x01\x01\x12\x16\n\tapy_lease\x18\x16 \x01(\tH\x06\x88\x01\x01\x42\x0f\n\r_fees_in_msatB\x1a\n\x18_utilization_out_initialB\x19\n\x17_utilization_in_initialB\x12\n\x10_apy_out_initialB\x11\n\x0f_apy_in_initialB\x14\n\x12_apy_total_initialB\x0c\n\n_apy_lease\"\xd2\x01\n\x18\x42kprdumpincomecsvRequest\x12\x12\n\ncsv_format\x18\x01 \x01(\t\x12\x15\n\x08\x63sv_file\x18\x02 \x01(\tH\x00\x88\x01\x01\x12\x1d\n\x10\x63onsolidate_fees\x18\x03 \x01(\x08H\x01\x88\x01\x01\x12\x17\n\nstart_time\x18\x04 \x01(\x04H\x02\x88\x01\x01\x12\x15\n\x08\x65nd_time\x18\x05 \x01(\x04H\x03\x88\x01\x01\x42\x0b\n\t_csv_fileB\x13\n\x11_consolidate_feesB\r\n\x0b_start_timeB\x0b\n\t_end_time\"\xd4\x01\n\x19\x42kprdumpincomecsvResponse\x12\x10\n\x08\x63sv_file\x18\x01 \x01(\t\x12M\n\ncsv_format\x18\x02 \x01(\x0e\x32\x39.cln.BkprdumpincomecsvResponse.BkprdumpincomecsvCsvFormat\"V\n\x1a\x42kprdumpincomecsvCsvFormat\x12\x0f\n\x0b\x43OINTRACKER\x10\x00\x12\n\n\x06KOINLY\x10\x01\x12\x0b\n\x07HARMONY\x10\x02\x12\x0e\n\nQUICKBOOKS\x10\x03\"%\n\x12\x42kprinspectRequest\x12\x0f\n\x07\x61\x63\x63ount\x18\x01 \x01(\t\"7\n\x13\x42kprinspectResponse\x12 \n\x03txs\x18\x01 \x03(\x0b\x32\x13.cln.BkprinspectTxs\"\x9a\x01\n\x0e\x42kprinspectTxs\x12\x0c\n\x04txid\x18\x01 \x01(\x0c\x12\x18\n\x0b\x62lockheight\x18\x02 \x01(\rH\x00\x88\x01\x01\x12#\n\x0e\x66\x65\x65s_paid_msat\x18\x03 \x01(\x0b\x32\x0b.cln.Amount\x12+\n\x07outputs\x18\x04 \x03(\x0b\x32\x1a.cln.BkprinspectTxsOutputsB\x0e\n\x0c_blockheight\"\xbc\x03\n\x15\x42kprinspectTxsOutputs\x12\x0f\n\x07\x61\x63\x63ount\x18\x01 \x01(\t\x12\x0e\n\x06outnum\x18\x02 \x01(\r\x12&\n\x11output_value_msat\x18\x03 \x01(\x0b\x32\x0b.cln.Amount\x12\x10\n\x08\x63urrency\x18\x04 \x01(\t\x12%\n\x0b\x63redit_msat\x18\x05 \x01(\x0b\x32\x0b.cln.AmountH\x00\x88\x01\x01\x12$\n\ndebit_msat\x18\x06 \x01(\x0b\x32\x0b.cln.AmountH\x01\x88\x01\x01\x12 \n\x13originating_account\x18\x07 \x01(\tH\x02\x88\x01\x01\x12\x17\n\noutput_tag\x18\x08 \x01(\tH\x03\x88\x01\x01\x12\x16\n\tspend_tag\x18\t \x01(\tH\x04\x88\x01\x01\x12\x1a\n\rspending_txid\x18\n \x01(\x0cH\x05\x88\x01\x01\x12\x17\n\npayment_id\x18\x0b \x01(\x0cH\x06\x88\x01\x01\x42\x0e\n\x0c_credit_msatB\r\n\x0b_debit_msatB\x16\n\x14_originating_accountB\r\n\x0b_output_tagB\x0c\n\n_spend_tagB\x10\n\x0e_spending_txidB\r\n\x0b_payment_id\"h\n\x1c\x42kprlistaccounteventsRequest\x12\x14\n\x07\x61\x63\x63ount\x18\x01 \x01(\tH\x00\x88\x01\x01\x12\x17\n\npayment_id\x18\x02 \x01(\tH\x01\x88\x01\x01\x42\n\n\x08_accountB\r\n\x0b_payment_id\"Q\n\x1d\x42kprlistaccounteventsResponse\x12\x30\n\x06\x65vents\x18\x01 \x03(\x0b\x32 .cln.BkprlistaccounteventsEvents\"\xa1\x05\n\x1b\x42kprlistaccounteventsEvents\x12\x0f\n\x07\x61\x63\x63ount\x18\x01 \x01(\t\x12S\n\titem_type\x18\x02 \x01(\x0e\x32@.cln.BkprlistaccounteventsEvents.BkprlistaccounteventsEventsType\x12\x0b\n\x03tag\x18\x03 \x01(\t\x12 \n\x0b\x63redit_msat\x18\x04 \x01(\x0b\x32\x0b.cln.Amount\x12\x1f\n\ndebit_msat\x18\x05 \x01(\x0b\x32\x0b.cln.Amount\x12\x10\n\x08\x63urrency\x18\x06 \x01(\t\x12\x11\n\ttimestamp\x18\x07 \x01(\r\x12\x15\n\x08outpoint\x18\x08 \x01(\tH\x00\x88\x01\x01\x12\x18\n\x0b\x62lockheight\x18\t \x01(\rH\x01\x88\x01\x01\x12\x13\n\x06origin\x18\n \x01(\tH\x02\x88\x01\x01\x12\x17\n\npayment_id\x18\x0b \x01(\x0cH\x03\x88\x01\x01\x12\x11\n\x04txid\x18\x0c \x01(\x0cH\x04\x88\x01\x01\x12\x18\n\x0b\x64\x65scription\x18\r \x01(\tH\x05\x88\x01\x01\x12#\n\tfees_msat\x18\x0e \x01(\x0b\x32\x0b.cln.AmountH\x06\x88\x01\x01\x12\x19\n\x0cis_rebalance\x18\x0f \x01(\x08H\x07\x88\x01\x01\x12\x14\n\x07part_id\x18\x10 \x01(\rH\x08\x88\x01\x01\"J\n\x1f\x42kprlistaccounteventsEventsType\x12\x0f\n\x0bONCHAIN_FEE\x10\x00\x12\t\n\x05\x43HAIN\x10\x01\x12\x0b\n\x07\x43HANNEL\x10\x02\x42\x0b\n\t_outpointB\x0e\n\x0c_blockheightB\t\n\x07_originB\r\n\x0b_payment_idB\x07\n\x05_txidB\x0e\n\x0c_descriptionB\x0c\n\n_fees_msatB\x0f\n\r_is_rebalanceB\n\n\x08_part_id\"\x19\n\x17\x42kprlistbalancesRequest\"K\n\x18\x42kprlistbalancesResponse\x12/\n\x08\x61\x63\x63ounts\x18\x01 \x03(\x0b\x32\x1d.cln.BkprlistbalancesAccounts\"\xc6\x02\n\x18\x42kprlistbalancesAccounts\x12\x0f\n\x07\x61\x63\x63ount\x18\x01 \x01(\t\x12\x37\n\x08\x62\x61lances\x18\x02 \x03(\x0b\x32%.cln.BkprlistbalancesAccountsBalances\x12\x14\n\x07peer_id\x18\x03 \x01(\x0cH\x00\x88\x01\x01\x12\x16\n\twe_opened\x18\x04 \x01(\x08H\x01\x88\x01\x01\x12\x1b\n\x0e\x61\x63\x63ount_closed\x18\x05 \x01(\x08H\x02\x88\x01\x01\x12\x1d\n\x10\x61\x63\x63ount_resolved\x18\x06 \x01(\x08H\x03\x88\x01\x01\x12\x1e\n\x11resolved_at_block\x18\x07 \x01(\rH\x04\x88\x01\x01\x42\n\n\x08_peer_idB\x0c\n\n_we_openedB\x11\n\x0f_account_closedB\x13\n\x11_account_resolvedB\x14\n\x12_resolved_at_block\"X\n BkprlistbalancesAccountsBalances\x12!\n\x0c\x62\x61lance_msat\x18\x01 \x01(\x0b\x32\x0b.cln.Amount\x12\x11\n\tcoin_type\x18\x02 \x01(\t\"\x97\x01\n\x15\x42kprlistincomeRequest\x12\x1d\n\x10\x63onsolidate_fees\x18\x01 \x01(\x08H\x00\x88\x01\x01\x12\x17\n\nstart_time\x18\x02 \x01(\rH\x01\x88\x01\x01\x12\x15\n\x08\x65nd_time\x18\x03 \x01(\rH\x02\x88\x01\x01\x42\x13\n\x11_consolidate_feesB\r\n\x0b_start_timeB\x0b\n\t_end_time\"P\n\x16\x42kprlistincomeResponse\x12\x36\n\rincome_events\x18\x01 \x03(\x0b\x32\x1f.cln.BkprlistincomeIncomeEvents\"\xb4\x02\n\x1a\x42kprlistincomeIncomeEvents\x12\x0f\n\x07\x61\x63\x63ount\x18\x01 \x01(\t\x12\x0b\n\x03tag\x18\x02 \x01(\t\x12 \n\x0b\x63redit_msat\x18\x03 \x01(\x0b\x32\x0b.cln.Amount\x12\x1f\n\ndebit_msat\x18\x04 \x01(\x0b\x32\x0b.cln.Amount\x12\x10\n\x08\x63urrency\x18\x05 \x01(\t\x12\x11\n\ttimestamp\x18\x06 \x01(\r\x12\x18\n\x0b\x64\x65scription\x18\x07 \x01(\tH\x00\x88\x01\x01\x12\x15\n\x08outpoint\x18\x08 \x01(\tH\x01\x88\x01\x01\x12\x11\n\x04txid\x18\t \x01(\x0cH\x02\x88\x01\x01\x12\x17\n\npayment_id\x18\n \x01(\x0cH\x03\x88\x01\x01\x42\x0e\n\x0c_descriptionB\x0b\n\t_outpointB\x07\n\x05_txidB\r\n\x0b_payment_id\"P\n%BkpreditdescriptionbypaymentidRequest\x12\x12\n\npayment_id\x18\x01 \x01(\t\x12\x13\n\x0b\x64\x65scription\x18\x02 \x01(\t\"e\n&BkpreditdescriptionbypaymentidResponse\x12;\n\x07updated\x18\x01 \x03(\x0b\x32*.cln.BkpreditdescriptionbypaymentidUpdated\"\xa3\x05\n%BkpreditdescriptionbypaymentidUpdated\x12\x0f\n\x07\x61\x63\x63ount\x18\x01 \x01(\t\x12g\n\titem_type\x18\x02 \x01(\x0e\x32T.cln.BkpreditdescriptionbypaymentidUpdated.BkpreditdescriptionbypaymentidUpdatedType\x12\x0b\n\x03tag\x18\x03 \x01(\t\x12 \n\x0b\x63redit_msat\x18\x04 \x01(\x0b\x32\x0b.cln.Amount\x12\x1f\n\ndebit_msat\x18\x05 \x01(\x0b\x32\x0b.cln.Amount\x12\x10\n\x08\x63urrency\x18\x06 \x01(\t\x12\x11\n\ttimestamp\x18\x07 \x01(\r\x12\x13\n\x0b\x64\x65scription\x18\x08 \x01(\t\x12\x15\n\x08outpoint\x18\t \x01(\tH\x00\x88\x01\x01\x12\x18\n\x0b\x62lockheight\x18\n \x01(\rH\x01\x88\x01\x01\x12\x13\n\x06origin\x18\x0b \x01(\tH\x02\x88\x01\x01\x12\x17\n\npayment_id\x18\x0c \x01(\x0cH\x03\x88\x01\x01\x12\x11\n\x04txid\x18\r \x01(\x0cH\x04\x88\x01\x01\x12#\n\tfees_msat\x18\x0e \x01(\x0b\x32\x0b.cln.AmountH\x05\x88\x01\x01\x12\x19\n\x0cis_rebalance\x18\x0f \x01(\x08H\x06\x88\x01\x01\x12\x14\n\x07part_id\x18\x10 \x01(\rH\x07\x88\x01\x01\"C\n)BkpreditdescriptionbypaymentidUpdatedType\x12\t\n\x05\x43HAIN\x10\x00\x12\x0b\n\x07\x43HANNEL\x10\x01\x42\x0b\n\t_outpointB\x0e\n\x0c_blockheightB\t\n\x07_originB\r\n\x0b_payment_idB\x07\n\x05_txidB\x0c\n\n_fees_msatB\x0f\n\r_is_rebalanceB\n\n\x08_part_id\"M\n$BkpreditdescriptionbyoutpointRequest\x12\x10\n\x08outpoint\x18\x01 \x01(\t\x12\x13\n\x0b\x64\x65scription\x18\x02 \x01(\t\"c\n%BkpreditdescriptionbyoutpointResponse\x12:\n\x07updated\x18\x01 \x03(\x0b\x32).cln.BkpreditdescriptionbyoutpointUpdated\"\x9f\x05\n$BkpreditdescriptionbyoutpointUpdated\x12\x0f\n\x07\x61\x63\x63ount\x18\x01 \x01(\t\x12\x65\n\titem_type\x18\x02 \x01(\x0e\x32R.cln.BkpreditdescriptionbyoutpointUpdated.BkpreditdescriptionbyoutpointUpdatedType\x12\x0b\n\x03tag\x18\x03 \x01(\t\x12 \n\x0b\x63redit_msat\x18\x04 \x01(\x0b\x32\x0b.cln.Amount\x12\x1f\n\ndebit_msat\x18\x05 \x01(\x0b\x32\x0b.cln.Amount\x12\x10\n\x08\x63urrency\x18\x06 \x01(\t\x12\x11\n\ttimestamp\x18\x07 \x01(\r\x12\x13\n\x0b\x64\x65scription\x18\x08 \x01(\t\x12\x15\n\x08outpoint\x18\t \x01(\tH\x00\x88\x01\x01\x12\x18\n\x0b\x62lockheight\x18\n \x01(\rH\x01\x88\x01\x01\x12\x13\n\x06origin\x18\x0b \x01(\tH\x02\x88\x01\x01\x12\x17\n\npayment_id\x18\x0c \x01(\x0cH\x03\x88\x01\x01\x12\x11\n\x04txid\x18\r \x01(\x0cH\x04\x88\x01\x01\x12#\n\tfees_msat\x18\x0e \x01(\x0b\x32\x0b.cln.AmountH\x05\x88\x01\x01\x12\x19\n\x0cis_rebalance\x18\x0f \x01(\x08H\x06\x88\x01\x01\x12\x14\n\x07part_id\x18\x10 \x01(\rH\x07\x88\x01\x01\"B\n(BkpreditdescriptionbyoutpointUpdatedType\x12\t\n\x05\x43HAIN\x10\x00\x12\x0b\n\x07\x43HANNEL\x10\x01\x42\x0b\n\t_outpointB\x0e\n\x0c_blockheightB\t\n\x07_originB\r\n\x0b_payment_idB\x07\n\x05_txidB\x0c\n\n_fees_msatB\x0f\n\r_is_rebalanceB\n\n\x08_part_id\"n\n\x14\x42lacklistruneRequest\x12\x12\n\x05start\x18\x01 \x01(\x04H\x00\x88\x01\x01\x12\x10\n\x03\x65nd\x18\x02 \x01(\x04H\x01\x88\x01\x01\x12\x13\n\x06relist\x18\x03 \x01(\x08H\x02\x88\x01\x01\x42\x08\n\x06_startB\x06\n\x04_endB\t\n\x07_relist\"G\n\x15\x42lacklistruneResponse\x12.\n\tblacklist\x18\x01 \x03(\x0b\x32\x1b.cln.BlacklistruneBlacklist\"4\n\x16\x42lacklistruneBlacklist\x12\r\n\x05start\x18\x01 \x01(\x04\x12\x0b\n\x03\x65nd\x18\x02 \x01(\x04\"p\n\x10\x43heckruneRequest\x12\x0c\n\x04rune\x18\x01 \x01(\t\x12\x13\n\x06nodeid\x18\x02 \x01(\tH\x00\x88\x01\x01\x12\x13\n\x06method\x18\x03 \x01(\tH\x01\x88\x01\x01\x12\x0e\n\x06params\x18\x04 \x03(\tB\t\n\x07_nodeidB\t\n\x07_method\"\"\n\x11\x43heckruneResponse\x12\r\n\x05valid\x18\x01 \x01(\x08\"E\n\x11\x43reateruneRequest\x12\x11\n\x04rune\x18\x01 \x01(\tH\x00\x88\x01\x01\x12\x14\n\x0crestrictions\x18\x02 \x03(\tB\x07\n\x05_rune\"{\n\x12\x43reateruneResponse\x12\x0c\n\x04rune\x18\x01 \x01(\t\x12\x11\n\tunique_id\x18\x02 \x01(\t\x12&\n\x19warning_unrestricted_rune\x18\x03 \x01(\tH\x00\x88\x01\x01\x42\x1c\n\x1a_warning_unrestricted_rune\".\n\x10ShowrunesRequest\x12\x11\n\x04rune\x18\x01 \x01(\tH\x00\x88\x01\x01\x42\x07\n\x05_rune\"7\n\x11ShowrunesResponse\x12\"\n\x05runes\x18\x01 \x03(\x0b\x32\x13.cln.ShowrunesRunes\"\x9d\x02\n\x0eShowrunesRunes\x12\x0c\n\x04rune\x18\x01 \x01(\t\x12\x11\n\tunique_id\x18\x02 \x01(\t\x12\x35\n\x0crestrictions\x18\x03 \x03(\x0b\x32\x1f.cln.ShowrunesRunesRestrictions\x12\x1f\n\x17restrictions_as_english\x18\x04 \x01(\t\x12\x13\n\x06stored\x18\x05 \x01(\x08H\x00\x88\x01\x01\x12\x18\n\x0b\x62lacklisted\x18\x06 \x01(\x08H\x01\x88\x01\x01\x12\x16\n\tlast_used\x18\x07 \x01(\x01H\x02\x88\x01\x01\x12\x15\n\x08our_rune\x18\x08 \x01(\x08H\x03\x88\x01\x01\x42\t\n\x07_storedB\x0e\n\x0c_blacklistedB\x0c\n\n_last_usedB\x0b\n\t_our_rune\"p\n\x1aShowrunesRunesRestrictions\x12\x41\n\x0c\x61lternatives\x18\x01 \x03(\x0b\x32+.cln.ShowrunesRunesRestrictionsAlternatives\x12\x0f\n\x07\x65nglish\x18\x02 \x01(\t\"n\n&ShowrunesRunesRestrictionsAlternatives\x12\x11\n\tfieldname\x18\x01 \x01(\t\x12\r\n\x05value\x18\x02 \x01(\t\x12\x11\n\tcondition\x18\x03 \x01(\t\x12\x0f\n\x07\x65nglish\x18\x04 \x01(\t\"B\n\x17\x41skreneunreserveRequest\x12\'\n\x04path\x18\x01 \x03(\x0b\x32\x19.cln.AskreneunreservePath\"\x1a\n\x18\x41skreneunreserveResponse\"t\n\x14\x41skreneunreservePath\x12 \n\x0b\x61mount_msat\x18\x03 \x01(\x0b\x32\x0b.cln.Amount\x12!\n\x14short_channel_id_dir\x18\x04 \x01(\tH\x00\x88\x01\x01\x42\x17\n\x15_short_channel_id_dir\"8\n\x18\x41skrenelistlayersRequest\x12\x12\n\x05layer\x18\x01 \x01(\tH\x00\x88\x01\x01\x42\x08\n\x06_layer\"I\n\x19\x41skrenelistlayersResponse\x12,\n\x06layers\x18\x01 \x03(\x0b\x32\x1c.cln.AskrenelistlayersLayers\"\x81\x03\n\x17\x41skrenelistlayersLayers\x12\r\n\x05layer\x18\x01 \x01(\t\x12\x16\n\x0e\x64isabled_nodes\x18\x02 \x03(\x0c\x12\x45\n\x10\x63reated_channels\x18\x03 \x03(\x0b\x32+.cln.AskrenelistlayersLayersCreatedChannels\x12<\n\x0b\x63onstraints\x18\x04 \x03(\x0b\x32\'.cln.AskrenelistlayersLayersConstraints\x12\x17\n\npersistent\x18\x05 \x01(\x08H\x00\x88\x01\x01\x12\x19\n\x11\x64isabled_channels\x18\x06 \x03(\t\x12\x43\n\x0f\x63hannel_updates\x18\x07 \x03(\x0b\x32*.cln.AskrenelistlayersLayersChannelUpdates\x12\x32\n\x06\x62iases\x18\x08 \x03(\x0b\x32\".cln.AskrenelistlayersLayersBiasesB\r\n\x0b_persistent\"\x8b\x01\n&AskrenelistlayersLayersCreatedChannels\x12\x0e\n\x06source\x18\x01 \x01(\x0c\x12\x13\n\x0b\x64\x65stination\x18\x02 \x01(\x0c\x12\x18\n\x10short_channel_id\x18\x03 \x01(\t\x12\"\n\rcapacity_msat\x18\x04 \x01(\x0b\x32\x0b.cln.Amount\"\xa8\x03\n%AskrenelistlayersLayersChannelUpdates\x12\x1c\n\x14short_channel_id_dir\x18\x01 \x01(\t\x12\x14\n\x07\x65nabled\x18\x02 \x01(\x08H\x00\x88\x01\x01\x12+\n\x11htlc_minimum_msat\x18\x03 \x01(\x0b\x32\x0b.cln.AmountH\x01\x88\x01\x01\x12+\n\x11htlc_maximum_msat\x18\x04 \x01(\x0b\x32\x0b.cln.AmountH\x02\x88\x01\x01\x12\'\n\rfee_base_msat\x18\x05 \x01(\x0b\x32\x0b.cln.AmountH\x03\x88\x01\x01\x12(\n\x1b\x66\x65\x65_proportional_millionths\x18\x06 \x01(\rH\x04\x88\x01\x01\x12\x1e\n\x11\x63ltv_expiry_delta\x18\x07 \x01(\rH\x05\x88\x01\x01\x42\n\n\x08_enabledB\x14\n\x12_htlc_minimum_msatB\x14\n\x12_htlc_maximum_msatB\x10\n\x0e_fee_base_msatB\x1e\n\x1c_fee_proportional_millionthsB\x14\n\x12_cltv_expiry_delta\"\xf8\x01\n\"AskrenelistlayersLayersConstraints\x12&\n\x0cmaximum_msat\x18\x03 \x01(\x0b\x32\x0b.cln.AmountH\x00\x88\x01\x01\x12&\n\x0cminimum_msat\x18\x04 \x01(\x0b\x32\x0b.cln.AmountH\x01\x88\x01\x01\x12!\n\x14short_channel_id_dir\x18\x05 \x01(\tH\x02\x88\x01\x01\x12\x16\n\ttimestamp\x18\x06 \x01(\x04H\x03\x88\x01\x01\x42\x0f\n\r_maximum_msatB\x0f\n\r_minimum_msatB\x17\n\x15_short_channel_id_dirB\x0c\n\n_timestamp\"u\n\x1d\x41skrenelistlayersLayersBiases\x12\x1c\n\x14short_channel_id_dir\x18\x01 \x01(\t\x12\x0c\n\x04\x62ias\x18\x02 \x01(\x12\x12\x18\n\x0b\x64\x65scription\x18\x03 \x01(\tH\x00\x88\x01\x01\x42\x0e\n\x0c_description\"R\n\x19\x41skrenecreatelayerRequest\x12\r\n\x05layer\x18\x01 \x01(\t\x12\x17\n\npersistent\x18\x02 \x01(\x08H\x00\x88\x01\x01\x42\r\n\x0b_persistent\"K\n\x1a\x41skrenecreatelayerResponse\x12-\n\x06layers\x18\x01 \x03(\x0b\x32\x1d.cln.AskrenecreatelayerLayers\"\xf2\x02\n\x18\x41skrenecreatelayerLayers\x12\r\n\x05layer\x18\x01 \x01(\t\x12\x12\n\npersistent\x18\x02 \x01(\x08\x12\x16\n\x0e\x64isabled_nodes\x18\x03 \x03(\x0c\x12\x19\n\x11\x64isabled_channels\x18\x04 \x03(\t\x12\x46\n\x10\x63reated_channels\x18\x05 \x03(\x0b\x32,.cln.AskrenecreatelayerLayersCreatedChannels\x12\x44\n\x0f\x63hannel_updates\x18\x06 \x03(\x0b\x32+.cln.AskrenecreatelayerLayersChannelUpdates\x12=\n\x0b\x63onstraints\x18\x07 \x03(\x0b\x32(.cln.AskrenecreatelayerLayersConstraints\x12\x33\n\x06\x62iases\x18\x08 \x03(\x0b\x32#.cln.AskrenecreatelayerLayersBiases\"\x8c\x01\n\'AskrenecreatelayerLayersCreatedChannels\x12\x0e\n\x06source\x18\x01 \x01(\x0c\x12\x13\n\x0b\x64\x65stination\x18\x02 \x01(\x0c\x12\x18\n\x10short_channel_id\x18\x03 \x01(\t\x12\"\n\rcapacity_msat\x18\x04 \x01(\x0b\x32\x0b.cln.Amount\"\xd1\x02\n&AskrenecreatelayerLayersChannelUpdates\x12+\n\x11htlc_minimum_msat\x18\x01 \x01(\x0b\x32\x0b.cln.AmountH\x00\x88\x01\x01\x12+\n\x11htlc_maximum_msat\x18\x02 \x01(\x0b\x32\x0b.cln.AmountH\x01\x88\x01\x01\x12\'\n\rfee_base_msat\x18\x03 \x01(\x0b\x32\x0b.cln.AmountH\x02\x88\x01\x01\x12(\n\x1b\x66\x65\x65_proportional_millionths\x18\x04 \x01(\rH\x03\x88\x01\x01\x12\x12\n\x05\x64\x65lay\x18\x05 \x01(\rH\x04\x88\x01\x01\x42\x14\n\x12_htlc_minimum_msatB\x14\n\x12_htlc_maximum_msatB\x10\n\x0e_fee_base_msatB\x1e\n\x1c_fee_proportional_millionthsB\x08\n\x06_delay\"\xc4\x01\n#AskrenecreatelayerLayersConstraints\x12\x18\n\x10short_channel_id\x18\x01 \x01(\t\x12\x11\n\tdirection\x18\x02 \x01(\r\x12&\n\x0cmaximum_msat\x18\x03 \x01(\x0b\x32\x0b.cln.AmountH\x00\x88\x01\x01\x12&\n\x0cminimum_msat\x18\x04 \x01(\x0b\x32\x0b.cln.AmountH\x01\x88\x01\x01\x42\x0f\n\r_maximum_msatB\x0f\n\r_minimum_msat\"v\n\x1e\x41skrenecreatelayerLayersBiases\x12\x1c\n\x14short_channel_id_dir\x18\x01 \x01(\t\x12\x0c\n\x04\x62ias\x18\x02 \x01(\x12\x12\x18\n\x0b\x64\x65scription\x18\x03 \x01(\tH\x00\x88\x01\x01\x42\x0e\n\x0c_description\"*\n\x19\x41skreneremovelayerRequest\x12\r\n\x05layer\x18\x01 \x01(\t\"\x1c\n\x1a\x41skreneremovelayerResponse\">\n\x15\x41skrenereserveRequest\x12%\n\x04path\x18\x01 \x03(\x0b\x32\x17.cln.AskrenereservePath\"\x18\n\x16\x41skrenereserveResponse\"r\n\x12\x41skrenereservePath\x12 \n\x0b\x61mount_msat\x18\x03 \x01(\x0b\x32\x0b.cln.Amount\x12!\n\x14short_channel_id_dir\x18\x04 \x01(\tH\x00\x88\x01\x01\x42\x17\n\x15_short_channel_id_dir\"2\n\x11\x41skreneageRequest\x12\r\n\x05layer\x18\x01 \x01(\t\x12\x0e\n\x06\x63utoff\x18\x02 \x01(\x04\"8\n\x12\x41skreneageResponse\x12\r\n\x05layer\x18\x01 \x01(\t\x12\x13\n\x0bnum_removed\x18\x02 \x01(\x04\"\xfb\x01\n\x10GetroutesRequest\x12\x0e\n\x06source\x18\x01 \x01(\x0c\x12\x13\n\x0b\x64\x65stination\x18\x02 \x01(\x0c\x12 \n\x0b\x61mount_msat\x18\x03 \x01(\x0b\x32\x0b.cln.Amount\x12\x0e\n\x06layers\x18\x04 \x03(\t\x12 \n\x0bmaxfee_msat\x18\x05 \x01(\x0b\x32\x0b.cln.Amount\x12\x17\n\nfinal_cltv\x18\x07 \x01(\rH\x00\x88\x01\x01\x12\x15\n\x08maxdelay\x18\x08 \x01(\rH\x01\x88\x01\x01\x12\x15\n\x08maxparts\x18\t \x01(\rH\x02\x88\x01\x01\x42\r\n\x0b_final_cltvB\x0b\n\t_maxdelayB\x0b\n\t_maxparts\"R\n\x11GetroutesResponse\x12\x17\n\x0fprobability_ppm\x18\x01 \x01(\x04\x12$\n\x06routes\x18\x02 \x03(\x0b\x32\x14.cln.GetroutesRoutes\"\x9c\x01\n\x0fGetroutesRoutes\x12\x17\n\x0fprobability_ppm\x18\x01 \x01(\x04\x12 \n\x0b\x61mount_msat\x18\x02 \x01(\x0b\x32\x0b.cln.Amount\x12&\n\x04path\x18\x03 \x03(\x0b\x32\x18.cln.GetroutesRoutesPath\x12\x17\n\nfinal_cltv\x18\x04 \x01(\rH\x00\x88\x01\x01\x42\r\n\x0b_final_cltv\"\x98\x01\n\x13GetroutesRoutesPath\x12 \n\x0b\x61mount_msat\x18\x03 \x01(\x0b\x32\x0b.cln.Amount\x12\x14\n\x0cnext_node_id\x18\x04 \x01(\x0c\x12\r\n\x05\x64\x65lay\x18\x05 \x01(\r\x12!\n\x14short_channel_id_dir\x18\x06 \x01(\tH\x00\x88\x01\x01\x42\x17\n\x15_short_channel_id_dir\"8\n\x19\x41skrenedisablenodeRequest\x12\r\n\x05layer\x18\x01 \x01(\t\x12\x0c\n\x04node\x18\x02 \x01(\x0c\"\x1c\n\x1a\x41skrenedisablenodeResponse\"\xcd\x02\n\x1b\x41skreneinformchannelRequest\x12\r\n\x05layer\x18\x01 \x01(\t\x12!\n\x14short_channel_id_dir\x18\x06 \x01(\tH\x00\x88\x01\x01\x12%\n\x0b\x61mount_msat\x18\x07 \x01(\x0b\x32\x0b.cln.AmountH\x01\x88\x01\x01\x12P\n\x06inform\x18\x08 \x01(\x0e\x32;.cln.AskreneinformchannelRequest.AskreneinformchannelInformH\x02\x88\x01\x01\"O\n\x1a\x41skreneinformchannelInform\x12\x0f\n\x0b\x43ONSTRAINED\x10\x00\x12\x11\n\rUNCONSTRAINED\x10\x01\x12\r\n\tSUCCEEDED\x10\x02\x42\x17\n\x15_short_channel_id_dirB\x0e\n\x0c_amount_msatB\t\n\x07_inform\"Y\n\x1c\x41skreneinformchannelResponse\x12\x39\n\x0b\x63onstraints\x18\x02 \x03(\x0b\x32$.cln.AskreneinformchannelConstraints\"\xd3\x01\n\x1f\x41skreneinformchannelConstraints\x12\x1c\n\x14short_channel_id_dir\x18\x01 \x01(\t\x12\r\n\x05layer\x18\x02 \x01(\t\x12\x11\n\ttimestamp\x18\x03 \x01(\x04\x12&\n\x0cmaximum_msat\x18\x04 \x01(\x0b\x32\x0b.cln.AmountH\x00\x88\x01\x01\x12&\n\x0cminimum_msat\x18\x05 \x01(\x0b\x32\x0b.cln.AmountH\x01\x88\x01\x01\x42\x0f\n\r_maximum_msatB\x0f\n\r_minimum_msat\"\x8f\x01\n\x1b\x41skrenecreatechannelRequest\x12\r\n\x05layer\x18\x01 \x01(\t\x12\x0e\n\x06source\x18\x02 \x01(\x0c\x12\x13\n\x0b\x64\x65stination\x18\x03 \x01(\x0c\x12\x18\n\x10short_channel_id\x18\x04 \x01(\t\x12\"\n\rcapacity_msat\x18\x05 \x01(\x0b\x32\x0b.cln.Amount\"\x1e\n\x1c\x41skrenecreatechannelResponse\"\xad\x03\n\x1b\x41skreneupdatechannelRequest\x12\r\n\x05layer\x18\x01 \x01(\t\x12\x1c\n\x14short_channel_id_dir\x18\x02 \x01(\t\x12\x14\n\x07\x65nabled\x18\x03 \x01(\x08H\x00\x88\x01\x01\x12+\n\x11htlc_minimum_msat\x18\x04 \x01(\x0b\x32\x0b.cln.AmountH\x01\x88\x01\x01\x12+\n\x11htlc_maximum_msat\x18\x05 \x01(\x0b\x32\x0b.cln.AmountH\x02\x88\x01\x01\x12\'\n\rfee_base_msat\x18\x06 \x01(\x0b\x32\x0b.cln.AmountH\x03\x88\x01\x01\x12(\n\x1b\x66\x65\x65_proportional_millionths\x18\x07 \x01(\rH\x04\x88\x01\x01\x12\x1e\n\x11\x63ltv_expiry_delta\x18\x08 \x01(\rH\x05\x88\x01\x01\x42\n\n\x08_enabledB\x14\n\x12_htlc_minimum_msatB\x14\n\x12_htlc_maximum_msatB\x10\n\x0e_fee_base_msatB\x1e\n\x1c_fee_proportional_millionthsB\x14\n\x12_cltv_expiry_delta\"\x1e\n\x1c\x41skreneupdatechannelResponse\"\xa4\x01\n\x19\x41skrenebiaschannelRequest\x12\r\n\x05layer\x18\x01 \x01(\t\x12\x1c\n\x14short_channel_id_dir\x18\x02 \x01(\t\x12\x0c\n\x04\x62ias\x18\x03 \x01(\x12\x12\x18\n\x0b\x64\x65scription\x18\x04 \x01(\tH\x00\x88\x01\x01\x12\x15\n\x08relative\x18\x05 \x01(\x08H\x01\x88\x01\x01\x42\x0e\n\x0c_descriptionB\x0b\n\t_relative\"K\n\x1a\x41skrenebiaschannelResponse\x12-\n\x06\x62iases\x18\x01 \x03(\x0b\x32\x1d.cln.AskrenebiaschannelBiases\"\x7f\n\x18\x41skrenebiaschannelBiases\x12\r\n\x05layer\x18\x01 \x01(\t\x12\x1c\n\x14short_channel_id_dir\x18\x02 \x01(\t\x12\x0c\n\x04\x62ias\x18\x03 \x01(\x12\x12\x18\n\x0b\x64\x65scription\x18\x04 \x01(\tH\x00\x88\x01\x01\x42\x0e\n\x0c_description\" \n\x1e\x41skrenelistreservationsRequest\"a\n\x1f\x41skrenelistreservationsResponse\x12>\n\x0creservations\x18\x01 \x03(\x0b\x32(.cln.AskrenelistreservationsReservations\"\x91\x01\n#AskrenelistreservationsReservations\x12\x1c\n\x14short_channel_id_dir\x18\x01 \x01(\t\x12 \n\x0b\x61mount_msat\x18\x02 \x01(\x0b\x32\x0b.cln.Amount\x12\x16\n\x0e\x61ge_in_seconds\x18\x03 \x01(\x04\x12\x12\n\ncommand_id\x18\x04 \x01(\t\"\xcb\x02\n\x19InjectpaymentonionRequest\x12\r\n\x05onion\x18\x01 \x01(\x0c\x12\x14\n\x0cpayment_hash\x18\x02 \x01(\x0c\x12 \n\x0b\x61mount_msat\x18\x03 \x01(\x0b\x32\x0b.cln.Amount\x12\x13\n\x0b\x63ltv_expiry\x18\x04 \x01(\r\x12\x0e\n\x06partid\x18\x05 \x01(\x04\x12\x0f\n\x07groupid\x18\x06 \x01(\x04\x12\x12\n\x05label\x18\x07 \x01(\tH\x00\x88\x01\x01\x12\x16\n\tinvstring\x18\x08 \x01(\tH\x01\x88\x01\x01\x12\x1a\n\rlocalinvreqid\x18\t \x01(\x0cH\x02\x88\x01\x01\x12*\n\x10\x64\x65stination_msat\x18\n \x01(\x0b\x32\x0b.cln.AmountH\x03\x88\x01\x01\x42\x08\n\x06_labelB\x0c\n\n_invstringB\x10\n\x0e_localinvreqidB\x13\n\x11_destination_msat\"w\n\x1aInjectpaymentonionResponse\x12\x12\n\ncreated_at\x18\x01 \x01(\x04\x12\x14\n\x0c\x63ompleted_at\x18\x02 \x01(\x04\x12\x15\n\rcreated_index\x18\x03 \x01(\x04\x12\x18\n\x10payment_preimage\x18\x04 \x01(\x0c\">\n\x19InjectonionmessageRequest\x12\x10\n\x08path_key\x18\x01 \x01(\x0c\x12\x0f\n\x07message\x18\x02 \x01(\x0c\"\x1c\n\x1aInjectonionmessageResponse\"\x97\x02\n\x0bXpayRequest\x12\x11\n\tinvstring\x18\x01 \x01(\t\x12%\n\x0b\x61mount_msat\x18\x02 \x01(\x0b\x32\x0b.cln.AmountH\x00\x88\x01\x01\x12 \n\x06maxfee\x18\x03 \x01(\x0b\x32\x0b.cln.AmountH\x01\x88\x01\x01\x12\x0e\n\x06layers\x18\x04 \x03(\t\x12\x16\n\tretry_for\x18\x05 \x01(\rH\x02\x88\x01\x01\x12&\n\x0cpartial_msat\x18\x06 \x01(\x0b\x32\x0b.cln.AmountH\x03\x88\x01\x01\x12\x15\n\x08maxdelay\x18\x07 \x01(\rH\x04\x88\x01\x01\x42\x0e\n\x0c_amount_msatB\t\n\x07_maxfeeB\x0c\n\n_retry_forB\x0f\n\r_partial_msatB\x0b\n\t_maxdelay\"\xa1\x01\n\x0cXpayResponse\x12\x18\n\x10payment_preimage\x18\x01 \x01(\x0c\x12\x14\n\x0c\x66\x61iled_parts\x18\x02 \x01(\x04\x12\x18\n\x10successful_parts\x18\x03 \x01(\x04\x12 \n\x0b\x61mount_msat\x18\x04 \x01(\x0b\x32\x0b.cln.Amount\x12%\n\x10\x61mount_sent_msat\x18\x05 \x01(\x0b\x32\x0b.cln.Amount\"=\n\x19SignmessagewithkeyRequest\x12\x0f\n\x07message\x18\x01 \x01(\t\x12\x0f\n\x07\x61\x64\x64ress\x18\x02 \x01(\t\"`\n\x1aSignmessagewithkeyResponse\x12\x0f\n\x07\x61\x64\x64ress\x18\x01 \x01(\t\x12\x0e\n\x06pubkey\x18\x02 \x01(\x0c\x12\x11\n\tsignature\x18\x03 \x01(\x0c\x12\x0e\n\x06\x62\x61se64\x18\x04 \x01(\t\"\x19\n\x17StreamBlockAddedRequest\"6\n\x16\x42lockAddedNotification\x12\x0c\n\x04hash\x18\x01 \x01(\x0c\x12\x0e\n\x06height\x18\x02 \x01(\r\" \n\x1eStreamChannelOpenFailedRequest\"3\n\x1d\x43hannelOpenFailedNotification\x12\x12\n\nchannel_id\x18\x01 \x01(\x0c\"\x1c\n\x1aStreamChannelOpenedRequest\"w\n\x19\x43hannelOpenedNotification\x12\n\n\x02id\x18\x01 \x01(\x0c\x12!\n\x0c\x66unding_msat\x18\x02 \x01(\x0b\x32\x0b.cln.Amount\x12\x14\n\x0c\x66unding_txid\x18\x03 \x01(\x0c\x12\x15\n\rchannel_ready\x18\x04 \x01(\x08\"\x16\n\x14StreamConnectRequest\"\xbe\x01\n\x17PeerConnectNotification\x12\n\n\x02id\x18\x01 \x01(\x0c\x12\x44\n\tdirection\x18\x02 \x01(\x0e\x32\x31.cln.PeerConnectNotification.PeerConnectDirection\x12(\n\x07\x61\x64\x64ress\x18\x03 \x01(\x0b\x32\x17.cln.PeerConnectAddress\"\'\n\x14PeerConnectDirection\x12\x06\n\x02IN\x10\x00\x12\x07\n\x03OUT\x10\x01\"\x9a\x02\n\x12PeerConnectAddress\x12\x41\n\titem_type\x18\x01 \x01(\x0e\x32..cln.PeerConnectAddress.PeerConnectAddressType\x12\x13\n\x06socket\x18\x02 \x01(\tH\x00\x88\x01\x01\x12\x14\n\x07\x61\x64\x64ress\x18\x03 \x01(\tH\x01\x88\x01\x01\x12\x11\n\x04port\x18\x04 \x01(\rH\x02\x88\x01\x01\"c\n\x16PeerConnectAddressType\x12\x10\n\x0cLOCAL_SOCKET\x10\x00\x12\x08\n\x04IPV4\x10\x01\x12\x08\n\x04IPV6\x10\x02\x12\t\n\x05TORV2\x10\x03\x12\t\n\x05TORV3\x10\x04\x12\r\n\tWEBSOCKET\x10\x05\x42\t\n\x07_socketB\n\n\x08_addressB\x07\n\x05_port\"\x18\n\x16StreamCustomMsgRequest\"9\n\x15\x43ustomMsgNotification\x12\x0f\n\x07peer_id\x18\x01 \x01(\x0c\x12\x0f\n\x07payload\x18\x02 \x01(\x0c\"\"\n StreamChannelStateChangedRequest\"\xb0\x03\n\x1f\x43hannelStateChangedNotification\x12\x0f\n\x07peer_id\x18\x01 \x01(\x0c\x12\x12\n\nchannel_id\x18\x02 \x01(\x0c\x12\x1d\n\x10short_channel_id\x18\x03 \x01(\tH\x00\x88\x01\x01\x12\x11\n\ttimestamp\x18\x04 \x01(\t\x12)\n\told_state\x18\x05 \x01(\x0e\x32\x11.cln.ChannelStateH\x01\x88\x01\x01\x12$\n\tnew_state\x18\x06 \x01(\x0e\x32\x11.cln.ChannelState\x12L\n\x05\x63\x61use\x18\x07 \x01(\x0e\x32=.cln.ChannelStateChangedNotification.ChannelStateChangedCause\x12\x0f\n\x07message\x18\x08 \x01(\t\"c\n\x18\x43hannelStateChangedCause\x12\x0b\n\x07UNKNOWN\x10\x00\x12\t\n\x05LOCAL\x10\x01\x12\x08\n\x04USER\x10\x02\x12\n\n\x06REMOTE\x10\x03\x12\x0c\n\x08PROTOCOL\x10\x04\x12\x0b\n\x07ONCHAIN\x10\x05\x42\x13\n\x11_short_channel_idB\x0c\n\n_old_state2\xe9P\n\x04Node\x12\x36\n\x07Getinfo\x12\x13.cln.GetinfoRequest\x1a\x14.cln.GetinfoResponse\"\x00\x12<\n\tListPeers\x12\x15.cln.ListpeersRequest\x1a\x16.cln.ListpeersResponse\"\x00\x12<\n\tListFunds\x12\x15.cln.ListfundsRequest\x1a\x16.cln.ListfundsResponse\"\x00\x12\x36\n\x07SendPay\x12\x13.cln.SendpayRequest\x1a\x14.cln.SendpayResponse\"\x00\x12\x45\n\x0cListChannels\x12\x18.cln.ListchannelsRequest\x1a\x19.cln.ListchannelsResponse\"\x00\x12<\n\tAddGossip\x12\x15.cln.AddgossipRequest\x1a\x16.cln.AddgossipResponse\"\x00\x12H\n\rAddPsbtOutput\x12\x19.cln.AddpsbtoutputRequest\x1a\x1a.cln.AddpsbtoutputResponse\"\x00\x12H\n\rAutoCleanOnce\x12\x19.cln.AutocleanonceRequest\x1a\x1a.cln.AutocleanonceResponse\"\x00\x12N\n\x0f\x41utoCleanStatus\x12\x1b.cln.AutocleanstatusRequest\x1a\x1c.cln.AutocleanstatusResponse\"\x00\x12\x45\n\x0c\x43heckMessage\x12\x18.cln.CheckmessageRequest\x1a\x19.cln.CheckmessageResponse\"\x00\x12\x30\n\x05\x43lose\x12\x11.cln.CloseRequest\x1a\x12.cln.CloseResponse\"\x00\x12:\n\x0b\x43onnectPeer\x12\x13.cln.ConnectRequest\x1a\x14.cln.ConnectResponse\"\x00\x12H\n\rCreateInvoice\x12\x19.cln.CreateinvoiceRequest\x1a\x1a.cln.CreateinvoiceResponse\"\x00\x12<\n\tDatastore\x12\x15.cln.DatastoreRequest\x1a\x16.cln.DatastoreResponse\"\x00\x12K\n\x0e\x44\x61tastoreUsage\x12\x1a.cln.DatastoreusageRequest\x1a\x1b.cln.DatastoreusageResponse\"\x00\x12\x42\n\x0b\x43reateOnion\x12\x17.cln.CreateonionRequest\x1a\x18.cln.CreateonionResponse\"\x00\x12\x45\n\x0c\x44\x65lDatastore\x12\x18.cln.DeldatastoreRequest\x1a\x19.cln.DeldatastoreResponse\"\x00\x12?\n\nDelInvoice\x12\x16.cln.DelinvoiceRequest\x1a\x17.cln.DelinvoiceResponse\"\x00\x12Q\n\x10\x44\x65vForgetChannel\x12\x1c.cln.DevforgetchannelRequest\x1a\x1d.cln.DevforgetchannelResponse\"\x00\x12Q\n\x10\x45mergencyRecover\x12\x1c.cln.EmergencyrecoverRequest\x1a\x1d.cln.EmergencyrecoverResponse\"\x00\x12\x66\n\x17GetEmergencyRecoverData\x12#.cln.GetemergencyrecoverdataRequest\x1a$.cln.GetemergencyrecoverdataResponse\"\x00\x12\x45\n\x0c\x45xposeSecret\x12\x18.cln.ExposesecretRequest\x1a\x19.cln.ExposesecretResponse\"\x00\x12\x36\n\x07Recover\x12\x13.cln.RecoverRequest\x1a\x14.cln.RecoverResponse\"\x00\x12K\n\x0eRecoverChannel\x12\x1a.cln.RecoverchannelRequest\x1a\x1b.cln.RecoverchannelResponse\"\x00\x12\x36\n\x07Invoice\x12\x13.cln.InvoiceRequest\x1a\x14.cln.InvoiceResponse\"\x00\x12Q\n\x14\x43reateInvoiceRequest\x12\x1a.cln.InvoicerequestRequest\x1a\x1b.cln.InvoicerequestResponse\"\x00\x12`\n\x15\x44isableInvoiceRequest\x12!.cln.DisableinvoicerequestRequest\x1a\".cln.DisableinvoicerequestResponse\"\x00\x12Z\n\x13ListInvoiceRequests\x12\x1f.cln.ListinvoicerequestsRequest\x1a .cln.ListinvoicerequestsResponse\"\x00\x12H\n\rListDatastore\x12\x19.cln.ListdatastoreRequest\x1a\x1a.cln.ListdatastoreResponse\"\x00\x12\x45\n\x0cListInvoices\x12\x18.cln.ListinvoicesRequest\x1a\x19.cln.ListinvoicesResponse\"\x00\x12<\n\tSendOnion\x12\x15.cln.SendonionRequest\x1a\x16.cln.SendonionResponse\"\x00\x12\x45\n\x0cListSendPays\x12\x18.cln.ListsendpaysRequest\x1a\x19.cln.ListsendpaysResponse\"\x00\x12Q\n\x10ListTransactions\x12\x1c.cln.ListtransactionsRequest\x1a\x1d.cln.ListtransactionsResponse\"\x00\x12?\n\nMakeSecret\x12\x16.cln.MakesecretRequest\x1a\x17.cln.MakesecretResponse\"\x00\x12*\n\x03Pay\x12\x0f.cln.PayRequest\x1a\x10.cln.PayResponse\"\x00\x12<\n\tListNodes\x12\x15.cln.ListnodesRequest\x1a\x16.cln.ListnodesResponse\"\x00\x12K\n\x0eWaitAnyInvoice\x12\x1a.cln.WaitanyinvoiceRequest\x1a\x1b.cln.WaitanyinvoiceResponse\"\x00\x12\x42\n\x0bWaitInvoice\x12\x17.cln.WaitinvoiceRequest\x1a\x18.cln.WaitinvoiceResponse\"\x00\x12\x42\n\x0bWaitSendPay\x12\x17.cln.WaitsendpayRequest\x1a\x18.cln.WaitsendpayResponse\"\x00\x12\x36\n\x07NewAddr\x12\x13.cln.NewaddrRequest\x1a\x14.cln.NewaddrResponse\"\x00\x12\x39\n\x08Withdraw\x12\x14.cln.WithdrawRequest\x1a\x15.cln.WithdrawResponse\"\x00\x12\x36\n\x07KeySend\x12\x13.cln.KeysendRequest\x1a\x14.cln.KeysendResponse\"\x00\x12\x39\n\x08\x46undPsbt\x12\x14.cln.FundpsbtRequest\x1a\x15.cln.FundpsbtResponse\"\x00\x12\x39\n\x08SendPsbt\x12\x14.cln.SendpsbtRequest\x1a\x15.cln.SendpsbtResponse\"\x00\x12\x39\n\x08SignPsbt\x12\x14.cln.SignpsbtRequest\x1a\x15.cln.SignpsbtResponse\"\x00\x12\x39\n\x08UtxoPsbt\x12\x14.cln.UtxopsbtRequest\x1a\x15.cln.UtxopsbtResponse\"\x00\x12<\n\tTxDiscard\x12\x15.cln.TxdiscardRequest\x1a\x16.cln.TxdiscardResponse\"\x00\x12<\n\tTxPrepare\x12\x15.cln.TxprepareRequest\x1a\x16.cln.TxprepareResponse\"\x00\x12\x33\n\x06TxSend\x12\x12.cln.TxsendRequest\x1a\x13.cln.TxsendResponse\"\x00\x12Q\n\x10ListPeerChannels\x12\x1c.cln.ListpeerchannelsRequest\x1a\x1d.cln.ListpeerchannelsResponse\"\x00\x12W\n\x12ListClosedChannels\x12\x1e.cln.ListclosedchannelsRequest\x1a\x1f.cln.ListclosedchannelsResponse\"\x00\x12<\n\tDecodePay\x12\x15.cln.DecodepayRequest\x1a\x16.cln.DecodepayResponse\"\x00\x12\x33\n\x06\x44\x65\x63ode\x12\x12.cln.DecodeRequest\x1a\x13.cln.DecodeResponse\"\x00\x12\x33\n\x06\x44\x65lPay\x12\x12.cln.DelpayRequest\x1a\x13.cln.DelpayResponse\"\x00\x12?\n\nDelForward\x12\x16.cln.DelforwardRequest\x1a\x17.cln.DelforwardResponse\"\x00\x12\x45\n\x0c\x44isableOffer\x12\x18.cln.DisableofferRequest\x1a\x19.cln.DisableofferResponse\"\x00\x12\x42\n\x0b\x45nableOffer\x12\x17.cln.EnableofferRequest\x1a\x18.cln.EnableofferResponse\"\x00\x12?\n\nDisconnect\x12\x16.cln.DisconnectRequest\x1a\x17.cln.DisconnectResponse\"\x00\x12\x39\n\x08\x46\x65\x65rates\x12\x14.cln.FeeratesRequest\x1a\x15.cln.FeeratesResponse\"\x00\x12\x42\n\x0b\x46\x65tchBip353\x12\x17.cln.Fetchbip353Request\x1a\x18.cln.Fetchbip353Response\"\x00\x12\x45\n\x0c\x46\x65tchInvoice\x12\x18.cln.FetchinvoiceRequest\x1a\x19.cln.FetchinvoiceResponse\"\x00\x12T\n\x11\x46undChannelCancel\x12\x1d.cln.FundchannelCancelRequest\x1a\x1e.cln.FundchannelCancelResponse\"\x00\x12Z\n\x13\x46undChannelComplete\x12\x1f.cln.FundchannelCompleteRequest\x1a .cln.FundchannelCompleteResponse\"\x00\x12\x42\n\x0b\x46undChannel\x12\x17.cln.FundchannelRequest\x1a\x18.cln.FundchannelResponse\"\x00\x12Q\n\x10\x46undChannelStart\x12\x1c.cln.FundchannelStartRequest\x1a\x1d.cln.FundchannelStartResponse\"\x00\x12\x33\n\x06GetLog\x12\x12.cln.GetlogRequest\x1a\x13.cln.GetlogResponse\"\x00\x12\x45\n\x0c\x46underUpdate\x12\x18.cln.FunderupdateRequest\x1a\x19.cln.FunderupdateResponse\"\x00\x12\x39\n\x08GetRoute\x12\x14.cln.GetrouteRequest\x1a\x15.cln.GetrouteResponse\"\x00\x12H\n\rListAddresses\x12\x19.cln.ListaddressesRequest\x1a\x1a.cln.ListaddressesResponse\"\x00\x12\x45\n\x0cListForwards\x12\x18.cln.ListforwardsRequest\x1a\x19.cln.ListforwardsResponse\"\x00\x12?\n\nListOffers\x12\x16.cln.ListoffersRequest\x1a\x17.cln.ListoffersResponse\"\x00\x12\x39\n\x08ListPays\x12\x14.cln.ListpaysRequest\x1a\x15.cln.ListpaysResponse\"\x00\x12<\n\tListHtlcs\x12\x15.cln.ListhtlcsRequest\x1a\x16.cln.ListhtlcsResponse\"\x00\x12Q\n\x10MultiFundChannel\x12\x1c.cln.MultifundchannelRequest\x1a\x1d.cln.MultifundchannelResponse\"\x00\x12H\n\rMultiWithdraw\x12\x19.cln.MultiwithdrawRequest\x1a\x1a.cln.MultiwithdrawResponse\"\x00\x12\x30\n\x05Offer\x12\x11.cln.OfferRequest\x1a\x12.cln.OfferResponse\"\x00\x12Q\n\x10OpenChannelAbort\x12\x1c.cln.OpenchannelAbortRequest\x1a\x1d.cln.OpenchannelAbortResponse\"\x00\x12N\n\x0fOpenChannelBump\x12\x1b.cln.OpenchannelBumpRequest\x1a\x1c.cln.OpenchannelBumpResponse\"\x00\x12N\n\x0fOpenChannelInit\x12\x1b.cln.OpenchannelInitRequest\x1a\x1c.cln.OpenchannelInitResponse\"\x00\x12T\n\x11OpenChannelSigned\x12\x1d.cln.OpenchannelSignedRequest\x1a\x1e.cln.OpenchannelSignedResponse\"\x00\x12T\n\x11OpenChannelUpdate\x12\x1d.cln.OpenchannelUpdateRequest\x1a\x1e.cln.OpenchannelUpdateResponse\"\x00\x12-\n\x04Ping\x12\x10.cln.PingRequest\x1a\x11.cln.PingResponse\"\x00\x12\x33\n\x06Plugin\x12\x12.cln.PluginRequest\x1a\x13.cln.PluginResponse\"\x00\x12H\n\rRenePayStatus\x12\x19.cln.RenepaystatusRequest\x1a\x1a.cln.RenepaystatusResponse\"\x00\x12\x36\n\x07RenePay\x12\x13.cln.RenepayRequest\x1a\x14.cln.RenepayResponse\"\x00\x12H\n\rReserveInputs\x12\x19.cln.ReserveinputsRequest\x1a\x1a.cln.ReserveinputsResponse\"\x00\x12H\n\rSendCustomMsg\x12\x19.cln.SendcustommsgRequest\x1a\x1a.cln.SendcustommsgResponse\"\x00\x12\x42\n\x0bSendInvoice\x12\x17.cln.SendinvoiceRequest\x1a\x18.cln.SendinvoiceResponse\"\x00\x12?\n\nSetChannel\x12\x16.cln.SetchannelRequest\x1a\x17.cln.SetchannelResponse\"\x00\x12<\n\tSetConfig\x12\x15.cln.SetconfigRequest\x1a\x16.cln.SetconfigResponse\"\x00\x12K\n\x0eSetPsbtVersion\x12\x1a.cln.SetpsbtversionRequest\x1a\x1b.cln.SetpsbtversionResponse\"\x00\x12\x42\n\x0bSignInvoice\x12\x17.cln.SigninvoiceRequest\x1a\x18.cln.SigninvoiceResponse\"\x00\x12\x42\n\x0bSignMessage\x12\x17.cln.SignmessageRequest\x1a\x18.cln.SignmessageResponse\"\x00\x12?\n\nSpliceInit\x12\x16.cln.SpliceInitRequest\x1a\x17.cln.SpliceInitResponse\"\x00\x12\x45\n\x0cSpliceSigned\x12\x18.cln.SpliceSignedRequest\x1a\x19.cln.SpliceSignedResponse\"\x00\x12\x45\n\x0cSpliceUpdate\x12\x18.cln.SpliceUpdateRequest\x1a\x19.cln.SpliceUpdateResponse\"\x00\x12<\n\tDevSplice\x12\x15.cln.DevspliceRequest\x1a\x16.cln.DevspliceResponse\"\x00\x12N\n\x0fUnreserveInputs\x12\x1b.cln.UnreserveinputsRequest\x1a\x1c.cln.UnreserveinputsResponse\"\x00\x12H\n\rUpgradeWallet\x12\x19.cln.UpgradewalletRequest\x1a\x1a.cln.UpgradewalletResponse\"\x00\x12N\n\x0fWaitBlockHeight\x12\x1b.cln.WaitblockheightRequest\x1a\x1c.cln.WaitblockheightResponse\"\x00\x12-\n\x04Wait\x12\x10.cln.WaitRequest\x1a\x11.cln.WaitResponse\"\x00\x12\x42\n\x0bListConfigs\x12\x17.cln.ListconfigsRequest\x1a\x18.cln.ListconfigsResponse\"\x00\x12-\n\x04Stop\x12\x10.cln.StopRequest\x1a\x11.cln.StopResponse\"\x00\x12-\n\x04Help\x12\x10.cln.HelpRequest\x1a\x11.cln.HelpResponse\"\x00\x12T\n\x11PreApproveKeysend\x12\x1d.cln.PreapprovekeysendRequest\x1a\x1e.cln.PreapprovekeysendResponse\"\x00\x12T\n\x11PreApproveInvoice\x12\x1d.cln.PreapproveinvoiceRequest\x1a\x1e.cln.PreapproveinvoiceResponse\"\x00\x12\x45\n\x0cStaticBackup\x12\x18.cln.StaticbackupRequest\x1a\x19.cln.StaticbackupResponse\"\x00\x12N\n\x0f\x42kprChannelsApy\x12\x1b.cln.BkprchannelsapyRequest\x1a\x1c.cln.BkprchannelsapyResponse\"\x00\x12T\n\x11\x42kprDumpIncomeCsv\x12\x1d.cln.BkprdumpincomecsvRequest\x1a\x1e.cln.BkprdumpincomecsvResponse\"\x00\x12\x42\n\x0b\x42kprInspect\x12\x17.cln.BkprinspectRequest\x1a\x18.cln.BkprinspectResponse\"\x00\x12`\n\x15\x42kprListAccountEvents\x12!.cln.BkprlistaccounteventsRequest\x1a\".cln.BkprlistaccounteventsResponse\"\x00\x12Q\n\x10\x42kprListBalances\x12\x1c.cln.BkprlistbalancesRequest\x1a\x1d.cln.BkprlistbalancesResponse\"\x00\x12K\n\x0e\x42kprListIncome\x12\x1a.cln.BkprlistincomeRequest\x1a\x1b.cln.BkprlistincomeResponse\"\x00\x12{\n\x1e\x42kprEditDescriptionByPaymentId\x12*.cln.BkpreditdescriptionbypaymentidRequest\x1a+.cln.BkpreditdescriptionbypaymentidResponse\"\x00\x12x\n\x1d\x42kprEditDescriptionByOutpoint\x12).cln.BkpreditdescriptionbyoutpointRequest\x1a*.cln.BkpreditdescriptionbyoutpointResponse\"\x00\x12H\n\rBlacklistRune\x12\x19.cln.BlacklistruneRequest\x1a\x1a.cln.BlacklistruneResponse\"\x00\x12<\n\tCheckRune\x12\x15.cln.CheckruneRequest\x1a\x16.cln.CheckruneResponse\"\x00\x12?\n\nCreateRune\x12\x16.cln.CreateruneRequest\x1a\x17.cln.CreateruneResponse\"\x00\x12<\n\tShowRunes\x12\x15.cln.ShowrunesRequest\x1a\x16.cln.ShowrunesResponse\"\x00\x12Q\n\x10\x41skReneUnreserve\x12\x1c.cln.AskreneunreserveRequest\x1a\x1d.cln.AskreneunreserveResponse\"\x00\x12T\n\x11\x41skReneListLayers\x12\x1d.cln.AskrenelistlayersRequest\x1a\x1e.cln.AskrenelistlayersResponse\"\x00\x12W\n\x12\x41skReneCreateLayer\x12\x1e.cln.AskrenecreatelayerRequest\x1a\x1f.cln.AskrenecreatelayerResponse\"\x00\x12W\n\x12\x41skReneRemoveLayer\x12\x1e.cln.AskreneremovelayerRequest\x1a\x1f.cln.AskreneremovelayerResponse\"\x00\x12K\n\x0e\x41skReneReserve\x12\x1a.cln.AskrenereserveRequest\x1a\x1b.cln.AskrenereserveResponse\"\x00\x12?\n\nAskReneAge\x12\x16.cln.AskreneageRequest\x1a\x17.cln.AskreneageResponse\"\x00\x12<\n\tGetRoutes\x12\x15.cln.GetroutesRequest\x1a\x16.cln.GetroutesResponse\"\x00\x12W\n\x12\x41skReneDisableNode\x12\x1e.cln.AskrenedisablenodeRequest\x1a\x1f.cln.AskrenedisablenodeResponse\"\x00\x12]\n\x14\x41skReneInformChannel\x12 .cln.AskreneinformchannelRequest\x1a!.cln.AskreneinformchannelResponse\"\x00\x12]\n\x14\x41skReneCreateChannel\x12 .cln.AskrenecreatechannelRequest\x1a!.cln.AskrenecreatechannelResponse\"\x00\x12]\n\x14\x41skReneUpdateChannel\x12 .cln.AskreneupdatechannelRequest\x1a!.cln.AskreneupdatechannelResponse\"\x00\x12W\n\x12\x41skReneBiasChannel\x12\x1e.cln.AskrenebiaschannelRequest\x1a\x1f.cln.AskrenebiaschannelResponse\"\x00\x12\x66\n\x17\x41skReneListReservations\x12#.cln.AskrenelistreservationsRequest\x1a$.cln.AskrenelistreservationsResponse\"\x00\x12W\n\x12InjectPaymentOnion\x12\x1e.cln.InjectpaymentonionRequest\x1a\x1f.cln.InjectpaymentonionResponse\"\x00\x12W\n\x12InjectOnionMessage\x12\x1e.cln.InjectonionmessageRequest\x1a\x1f.cln.InjectonionmessageResponse\"\x00\x12-\n\x04Xpay\x12\x10.cln.XpayRequest\x1a\x11.cln.XpayResponse\"\x00\x12W\n\x12SignMessageWithKey\x12\x1e.cln.SignmessagewithkeyRequest\x1a\x1f.cln.SignmessagewithkeyResponse\"\x00\x12T\n\x13SubscribeBlockAdded\x12\x1c.cln.StreamBlockAddedRequest\x1a\x1b.cln.BlockAddedNotification\"\x00\x30\x01\x12i\n\x1aSubscribeChannelOpenFailed\x12#.cln.StreamChannelOpenFailedRequest\x1a\".cln.ChannelOpenFailedNotification\"\x00\x30\x01\x12]\n\x16SubscribeChannelOpened\x12\x1f.cln.StreamChannelOpenedRequest\x1a\x1e.cln.ChannelOpenedNotification\"\x00\x30\x01\x12O\n\x10SubscribeConnect\x12\x19.cln.StreamConnectRequest\x1a\x1c.cln.PeerConnectNotification\"\x00\x30\x01\x12Q\n\x12SubscribeCustomMsg\x12\x1b.cln.StreamCustomMsgRequest\x1a\x1a.cln.CustomMsgNotification\"\x00\x30\x01\x12o\n\x1cSubscribeChannelStateChanged\x12%.cln.StreamChannelStateChangedRequest\x1a$.cln.ChannelStateChangedNotification\"\x00\x30\x01\x62\x06proto3') +DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\nnode.proto\x12\x03\x63ln\x1a\x10primitives.proto\"\x10\n\x0eGetinfoRequest\"\xc0\x04\n\x0fGetinfoResponse\x12\n\n\x02id\x18\x01 \x01(\x0c\x12\x12\n\x05\x61lias\x18\x02 \x01(\tH\x00\x88\x01\x01\x12\r\n\x05\x63olor\x18\x03 \x01(\x0c\x12\x11\n\tnum_peers\x18\x04 \x01(\r\x12\x1c\n\x14num_pending_channels\x18\x05 \x01(\r\x12\x1b\n\x13num_active_channels\x18\x06 \x01(\r\x12\x1d\n\x15num_inactive_channels\x18\x07 \x01(\r\x12\x0f\n\x07version\x18\x08 \x01(\t\x12\x15\n\rlightning_dir\x18\t \x01(\t\x12\x32\n\x0cour_features\x18\n \x01(\x0b\x32\x17.cln.GetinfoOurFeaturesH\x01\x88\x01\x01\x12\x13\n\x0b\x62lockheight\x18\x0b \x01(\r\x12\x0f\n\x07network\x18\x0c \x01(\t\x12(\n\x13\x66\x65\x65s_collected_msat\x18\r \x01(\x0b\x32\x0b.cln.Amount\x12$\n\x07\x61\x64\x64ress\x18\x0e \x03(\x0b\x32\x13.cln.GetinfoAddress\x12$\n\x07\x62inding\x18\x0f \x03(\x0b\x32\x13.cln.GetinfoBinding\x12\"\n\x15warning_bitcoind_sync\x18\x10 \x01(\tH\x02\x88\x01\x01\x12$\n\x17warning_lightningd_sync\x18\x11 \x01(\tH\x03\x88\x01\x01\x42\x08\n\x06_aliasB\x0f\n\r_our_featuresB\x18\n\x16_warning_bitcoind_syncB\x1a\n\x18_warning_lightningd_sync\"R\n\x12GetinfoOurFeatures\x12\x0c\n\x04init\x18\x01 \x01(\x0c\x12\x0c\n\x04node\x18\x02 \x01(\x0c\x12\x0f\n\x07\x63hannel\x18\x03 \x01(\x0c\x12\x0f\n\x07invoice\x18\x04 \x01(\x0c\"\xc4\x01\n\x0eGetinfoAddress\x12\x39\n\titem_type\x18\x01 \x01(\x0e\x32&.cln.GetinfoAddress.GetinfoAddressType\x12\x0c\n\x04port\x18\x02 \x01(\r\x12\x14\n\x07\x61\x64\x64ress\x18\x03 \x01(\tH\x00\x88\x01\x01\"G\n\x12GetinfoAddressType\x12\x07\n\x03\x44NS\x10\x00\x12\x08\n\x04IPV4\x10\x01\x12\x08\n\x04IPV6\x10\x02\x12\t\n\x05TORV2\x10\x03\x12\t\n\x05TORV3\x10\x04\x42\n\n\x08_address\"\xac\x02\n\x0eGetinfoBinding\x12\x39\n\titem_type\x18\x01 \x01(\x0e\x32&.cln.GetinfoBinding.GetinfoBindingType\x12\x14\n\x07\x61\x64\x64ress\x18\x02 \x01(\tH\x00\x88\x01\x01\x12\x11\n\x04port\x18\x03 \x01(\rH\x01\x88\x01\x01\x12\x13\n\x06socket\x18\x04 \x01(\tH\x02\x88\x01\x01\x12\x14\n\x07subtype\x18\x05 \x01(\tH\x03\x88\x01\x01\"_\n\x12GetinfoBindingType\x12\x10\n\x0cLOCAL_SOCKET\x10\x00\x12\x08\n\x04IPV4\x10\x01\x12\x08\n\x04IPV6\x10\x02\x12\t\n\x05TORV2\x10\x03\x12\t\n\x05TORV3\x10\x04\x12\r\n\tWEBSOCKET\x10\x05\x42\n\n\x08_addressB\x07\n\x05_portB\t\n\x07_socketB\n\n\x08_subtype\"\xb5\x01\n\x10ListpeersRequest\x12\x0f\n\x02id\x18\x01 \x01(\x0cH\x00\x88\x01\x01\x12\x38\n\x05level\x18\x02 \x01(\x0e\x32$.cln.ListpeersRequest.ListpeersLevelH\x01\x88\x01\x01\"E\n\x0eListpeersLevel\x12\x06\n\x02IO\x10\x00\x12\t\n\x05\x44\x45\x42UG\x10\x01\x12\x08\n\x04INFO\x10\x02\x12\x0b\n\x07UNUSUAL\x10\x03\x12\t\n\x05TRACE\x10\x04\x42\x05\n\x03_idB\x08\n\x06_level\"7\n\x11ListpeersResponse\x12\"\n\x05peers\x18\x01 \x03(\x0b\x32\x13.cln.ListpeersPeers\"\xdf\x01\n\x0eListpeersPeers\x12\n\n\x02id\x18\x01 \x01(\x0c\x12\x11\n\tconnected\x18\x02 \x01(\x08\x12#\n\x03log\x18\x03 \x03(\x0b\x32\x16.cln.ListpeersPeersLog\x12\x0f\n\x07netaddr\x18\x05 \x03(\t\x12\x15\n\x08\x66\x65\x61tures\x18\x06 \x01(\x0cH\x00\x88\x01\x01\x12\x18\n\x0bremote_addr\x18\x07 \x01(\tH\x01\x88\x01\x01\x12\x19\n\x0cnum_channels\x18\x08 \x01(\rH\x02\x88\x01\x01\x42\x0b\n\t_featuresB\x0e\n\x0c_remote_addrB\x0f\n\r_num_channels\"\x88\x03\n\x11ListpeersPeersLog\x12?\n\titem_type\x18\x01 \x01(\x0e\x32,.cln.ListpeersPeersLog.ListpeersPeersLogType\x12\x18\n\x0bnum_skipped\x18\x02 \x01(\rH\x00\x88\x01\x01\x12\x11\n\x04time\x18\x03 \x01(\tH\x01\x88\x01\x01\x12\x13\n\x06source\x18\x04 \x01(\tH\x02\x88\x01\x01\x12\x10\n\x03log\x18\x05 \x01(\tH\x03\x88\x01\x01\x12\x14\n\x07node_id\x18\x06 \x01(\x0cH\x04\x88\x01\x01\x12\x11\n\x04\x64\x61ta\x18\x07 \x01(\x0cH\x05\x88\x01\x01\"t\n\x15ListpeersPeersLogType\x12\x0b\n\x07SKIPPED\x10\x00\x12\n\n\x06\x42ROKEN\x10\x01\x12\x0b\n\x07UNUSUAL\x10\x02\x12\x08\n\x04INFO\x10\x03\x12\t\n\x05\x44\x45\x42UG\x10\x04\x12\t\n\x05IO_IN\x10\x05\x12\n\n\x06IO_OUT\x10\x06\x12\t\n\x05TRACE\x10\x07\x42\x0e\n\x0c_num_skippedB\x07\n\x05_timeB\t\n\x07_sourceB\x06\n\x04_logB\n\n\x08_node_idB\x07\n\x05_data\"0\n\x10ListfundsRequest\x12\x12\n\x05spent\x18\x01 \x01(\x08H\x00\x88\x01\x01\x42\x08\n\x06_spent\"e\n\x11ListfundsResponse\x12&\n\x07outputs\x18\x01 \x03(\x0b\x32\x15.cln.ListfundsOutputs\x12(\n\x08\x63hannels\x18\x02 \x03(\x0b\x32\x16.cln.ListfundsChannels\"\xb9\x03\n\x10ListfundsOutputs\x12\x0c\n\x04txid\x18\x01 \x01(\x0c\x12\x0e\n\x06output\x18\x02 \x01(\r\x12 \n\x0b\x61mount_msat\x18\x03 \x01(\x0b\x32\x0b.cln.Amount\x12\x14\n\x0cscriptpubkey\x18\x04 \x01(\x0c\x12\x14\n\x07\x61\x64\x64ress\x18\x05 \x01(\tH\x00\x88\x01\x01\x12\x19\n\x0credeemscript\x18\x06 \x01(\x0cH\x01\x88\x01\x01\x12<\n\x06status\x18\x07 \x01(\x0e\x32,.cln.ListfundsOutputs.ListfundsOutputsStatus\x12\x18\n\x0b\x62lockheight\x18\x08 \x01(\rH\x02\x88\x01\x01\x12\x10\n\x08reserved\x18\t \x01(\x08\x12\x1e\n\x11reserved_to_block\x18\n \x01(\rH\x03\x88\x01\x01\"Q\n\x16ListfundsOutputsStatus\x12\x0f\n\x0bUNCONFIRMED\x10\x00\x12\r\n\tCONFIRMED\x10\x01\x12\t\n\x05SPENT\x10\x02\x12\x0c\n\x08IMMATURE\x10\x03\x42\n\n\x08_addressB\x0f\n\r_redeemscriptB\x0e\n\x0c_blockheightB\x14\n\x12_reserved_to_block\"\xab\x02\n\x11ListfundsChannels\x12\x0f\n\x07peer_id\x18\x01 \x01(\x0c\x12$\n\x0four_amount_msat\x18\x02 \x01(\x0b\x32\x0b.cln.Amount\x12 \n\x0b\x61mount_msat\x18\x03 \x01(\x0b\x32\x0b.cln.Amount\x12\x14\n\x0c\x66unding_txid\x18\x04 \x01(\x0c\x12\x16\n\x0e\x66unding_output\x18\x05 \x01(\r\x12\x11\n\tconnected\x18\x06 \x01(\x08\x12 \n\x05state\x18\x07 \x01(\x0e\x32\x11.cln.ChannelState\x12\x1d\n\x10short_channel_id\x18\x08 \x01(\tH\x00\x88\x01\x01\x12\x17\n\nchannel_id\x18\t \x01(\x0cH\x01\x88\x01\x01\x42\x13\n\x11_short_channel_idB\r\n\x0b_channel_id\"\xbb\x03\n\x0eSendpayRequest\x12 \n\x05route\x18\x01 \x03(\x0b\x32\x11.cln.SendpayRoute\x12\x14\n\x0cpayment_hash\x18\x02 \x01(\x0c\x12\x12\n\x05label\x18\x03 \x01(\tH\x00\x88\x01\x01\x12\x13\n\x06\x62olt11\x18\x05 \x01(\tH\x01\x88\x01\x01\x12\x1b\n\x0epayment_secret\x18\x06 \x01(\x0cH\x02\x88\x01\x01\x12\x13\n\x06partid\x18\x07 \x01(\x04H\x03\x88\x01\x01\x12\x14\n\x07groupid\x18\t \x01(\x04H\x04\x88\x01\x01\x12%\n\x0b\x61mount_msat\x18\n \x01(\x0b\x32\x0b.cln.AmountH\x05\x88\x01\x01\x12\x1a\n\rlocalinvreqid\x18\x0b \x01(\x0cH\x06\x88\x01\x01\x12\x1d\n\x10payment_metadata\x18\x0c \x01(\x0cH\x07\x88\x01\x01\x12\x18\n\x0b\x64\x65scription\x18\r \x01(\tH\x08\x88\x01\x01\x42\x08\n\x06_labelB\t\n\x07_bolt11B\x11\n\x0f_payment_secretB\t\n\x07_partidB\n\n\x08_groupidB\x0e\n\x0c_amount_msatB\x10\n\x0e_localinvreqidB\x13\n\x11_payment_metadataB\x0e\n\x0c_description\"\xad\x05\n\x0fSendpayResponse\x12\n\n\x02id\x18\x01 \x01(\x04\x12\x14\n\x07groupid\x18\x02 \x01(\x04H\x00\x88\x01\x01\x12\x14\n\x0cpayment_hash\x18\x03 \x01(\x0c\x12\x32\n\x06status\x18\x04 \x01(\x0e\x32\".cln.SendpayResponse.SendpayStatus\x12%\n\x0b\x61mount_msat\x18\x05 \x01(\x0b\x32\x0b.cln.AmountH\x01\x88\x01\x01\x12\x18\n\x0b\x64\x65stination\x18\x06 \x01(\x0cH\x02\x88\x01\x01\x12\x12\n\ncreated_at\x18\x07 \x01(\x04\x12%\n\x10\x61mount_sent_msat\x18\x08 \x01(\x0b\x32\x0b.cln.Amount\x12\x12\n\x05label\x18\t \x01(\tH\x03\x88\x01\x01\x12\x13\n\x06partid\x18\n \x01(\x04H\x04\x88\x01\x01\x12\x13\n\x06\x62olt11\x18\x0b \x01(\tH\x05\x88\x01\x01\x12\x13\n\x06\x62olt12\x18\x0c \x01(\tH\x06\x88\x01\x01\x12\x1d\n\x10payment_preimage\x18\r \x01(\x0cH\x07\x88\x01\x01\x12\x14\n\x07message\x18\x0e \x01(\tH\x08\x88\x01\x01\x12\x19\n\x0c\x63ompleted_at\x18\x0f \x01(\x04H\t\x88\x01\x01\x12\x1a\n\rcreated_index\x18\x10 \x01(\x04H\n\x88\x01\x01\x12\x1a\n\rupdated_index\x18\x11 \x01(\x04H\x0b\x88\x01\x01\"*\n\rSendpayStatus\x12\x0b\n\x07PENDING\x10\x00\x12\x0c\n\x08\x43OMPLETE\x10\x01\x42\n\n\x08_groupidB\x0e\n\x0c_amount_msatB\x0e\n\x0c_destinationB\x08\n\x06_labelB\t\n\x07_partidB\t\n\x07_bolt11B\t\n\x07_bolt12B\x13\n\x11_payment_preimageB\n\n\x08_messageB\x0f\n\r_completed_atB\x10\n\x0e_created_indexB\x10\n\x0e_updated_index\"\\\n\x0cSendpayRoute\x12\n\n\x02id\x18\x02 \x01(\x0c\x12\r\n\x05\x64\x65lay\x18\x03 \x01(\r\x12\x0f\n\x07\x63hannel\x18\x04 \x01(\t\x12 \n\x0b\x61mount_msat\x18\x05 \x01(\x0b\x32\x0b.cln.Amount\"\x93\x01\n\x13ListchannelsRequest\x12\x1d\n\x10short_channel_id\x18\x01 \x01(\tH\x00\x88\x01\x01\x12\x13\n\x06source\x18\x02 \x01(\x0cH\x01\x88\x01\x01\x12\x18\n\x0b\x64\x65stination\x18\x03 \x01(\x0cH\x02\x88\x01\x01\x42\x13\n\x11_short_channel_idB\t\n\x07_sourceB\x0e\n\x0c_destination\"C\n\x14ListchannelsResponse\x12+\n\x08\x63hannels\x18\x01 \x03(\x0b\x32\x19.cln.ListchannelsChannels\"\xb3\x03\n\x14ListchannelsChannels\x12\x0e\n\x06source\x18\x01 \x01(\x0c\x12\x13\n\x0b\x64\x65stination\x18\x02 \x01(\x0c\x12\x18\n\x10short_channel_id\x18\x03 \x01(\t\x12\x0e\n\x06public\x18\x04 \x01(\x08\x12 \n\x0b\x61mount_msat\x18\x05 \x01(\x0b\x32\x0b.cln.Amount\x12\x15\n\rmessage_flags\x18\x06 \x01(\r\x12\x15\n\rchannel_flags\x18\x07 \x01(\r\x12\x0e\n\x06\x61\x63tive\x18\x08 \x01(\x08\x12\x13\n\x0blast_update\x18\t \x01(\r\x12\x1d\n\x15\x62\x61se_fee_millisatoshi\x18\n \x01(\r\x12\x19\n\x11\x66\x65\x65_per_millionth\x18\x0b \x01(\r\x12\r\n\x05\x64\x65lay\x18\x0c \x01(\r\x12&\n\x11htlc_minimum_msat\x18\r \x01(\x0b\x32\x0b.cln.Amount\x12+\n\x11htlc_maximum_msat\x18\x0e \x01(\x0b\x32\x0b.cln.AmountH\x00\x88\x01\x01\x12\x10\n\x08\x66\x65\x61tures\x18\x0f \x01(\x0c\x12\x11\n\tdirection\x18\x10 \x01(\rB\x14\n\x12_htlc_maximum_msat\"#\n\x10\x41\x64\x64gossipRequest\x12\x0f\n\x07message\x18\x01 \x01(\x0c\"\x13\n\x11\x41\x64\x64gossipResponse\"\xac\x01\n\x14\x41\x64\x64psbtoutputRequest\x12\x1c\n\x07satoshi\x18\x01 \x01(\x0b\x32\x0b.cln.Amount\x12\x15\n\x08locktime\x18\x02 \x01(\rH\x00\x88\x01\x01\x12\x18\n\x0binitialpsbt\x18\x03 \x01(\tH\x01\x88\x01\x01\x12\x18\n\x0b\x64\x65stination\x18\x04 \x01(\tH\x02\x88\x01\x01\x42\x0b\n\t_locktimeB\x0e\n\x0c_initialpsbtB\x0e\n\x0c_destination\"U\n\x15\x41\x64\x64psbtoutputResponse\x12\x0c\n\x04psbt\x18\x01 \x01(\t\x12\x1e\n\x16\x65stimated_added_weight\x18\x02 \x01(\r\x12\x0e\n\x06outnum\x18\x03 \x01(\r\"O\n\x14\x41utocleanonceRequest\x12*\n\tsubsystem\x18\x01 \x01(\x0e\x32\x17.cln.AutocleanSubsystem\x12\x0b\n\x03\x61ge\x18\x02 \x01(\x04\"G\n\x15\x41utocleanonceResponse\x12.\n\tautoclean\x18\x01 \x01(\x0b\x32\x1b.cln.AutocleanonceAutoclean\"\xb1\x04\n\x16\x41utocleanonceAutoclean\x12L\n\x11succeededforwards\x18\x01 \x01(\x0b\x32,.cln.AutocleanonceAutocleanSucceededforwardsH\x00\x88\x01\x01\x12\x46\n\x0e\x66\x61iledforwards\x18\x02 \x01(\x0b\x32).cln.AutocleanonceAutocleanFailedforwardsH\x01\x88\x01\x01\x12\x44\n\rsucceededpays\x18\x03 \x01(\x0b\x32(.cln.AutocleanonceAutocleanSucceededpaysH\x02\x88\x01\x01\x12>\n\nfailedpays\x18\x04 \x01(\x0b\x32%.cln.AutocleanonceAutocleanFailedpaysH\x03\x88\x01\x01\x12\x42\n\x0cpaidinvoices\x18\x05 \x01(\x0b\x32\'.cln.AutocleanonceAutocleanPaidinvoicesH\x04\x88\x01\x01\x12H\n\x0f\x65xpiredinvoices\x18\x06 \x01(\x0b\x32*.cln.AutocleanonceAutocleanExpiredinvoicesH\x05\x88\x01\x01\x42\x14\n\x12_succeededforwardsB\x11\n\x0f_failedforwardsB\x10\n\x0e_succeededpaysB\r\n\x0b_failedpaysB\x0f\n\r_paidinvoicesB\x12\n\x10_expiredinvoices\"M\n\'AutocleanonceAutocleanSucceededforwards\x12\x0f\n\x07\x63leaned\x18\x01 \x01(\x04\x12\x11\n\tuncleaned\x18\x02 \x01(\x04\"J\n$AutocleanonceAutocleanFailedforwards\x12\x0f\n\x07\x63leaned\x18\x01 \x01(\x04\x12\x11\n\tuncleaned\x18\x02 \x01(\x04\"I\n#AutocleanonceAutocleanSucceededpays\x12\x0f\n\x07\x63leaned\x18\x01 \x01(\x04\x12\x11\n\tuncleaned\x18\x02 \x01(\x04\"F\n AutocleanonceAutocleanFailedpays\x12\x0f\n\x07\x63leaned\x18\x01 \x01(\x04\x12\x11\n\tuncleaned\x18\x02 \x01(\x04\"H\n\"AutocleanonceAutocleanPaidinvoices\x12\x0f\n\x07\x63leaned\x18\x01 \x01(\x04\x12\x11\n\tuncleaned\x18\x02 \x01(\x04\"K\n%AutocleanonceAutocleanExpiredinvoices\x12\x0f\n\x07\x63leaned\x18\x01 \x01(\x04\x12\x11\n\tuncleaned\x18\x02 \x01(\x04\"W\n\x16\x41utocleanstatusRequest\x12/\n\tsubsystem\x18\x01 \x01(\x0e\x32\x17.cln.AutocleanSubsystemH\x00\x88\x01\x01\x42\x0c\n\n_subsystem\"K\n\x17\x41utocleanstatusResponse\x12\x30\n\tautoclean\x18\x01 \x01(\x0b\x32\x1d.cln.AutocleanstatusAutoclean\"\xbf\x04\n\x18\x41utocleanstatusAutoclean\x12N\n\x11succeededforwards\x18\x01 \x01(\x0b\x32..cln.AutocleanstatusAutocleanSucceededforwardsH\x00\x88\x01\x01\x12H\n\x0e\x66\x61iledforwards\x18\x02 \x01(\x0b\x32+.cln.AutocleanstatusAutocleanFailedforwardsH\x01\x88\x01\x01\x12\x46\n\rsucceededpays\x18\x03 \x01(\x0b\x32*.cln.AutocleanstatusAutocleanSucceededpaysH\x02\x88\x01\x01\x12@\n\nfailedpays\x18\x04 \x01(\x0b\x32\'.cln.AutocleanstatusAutocleanFailedpaysH\x03\x88\x01\x01\x12\x44\n\x0cpaidinvoices\x18\x05 \x01(\x0b\x32).cln.AutocleanstatusAutocleanPaidinvoicesH\x04\x88\x01\x01\x12J\n\x0f\x65xpiredinvoices\x18\x06 \x01(\x0b\x32,.cln.AutocleanstatusAutocleanExpiredinvoicesH\x05\x88\x01\x01\x42\x14\n\x12_succeededforwardsB\x11\n\x0f_failedforwardsB\x10\n\x0e_succeededpaysB\r\n\x0b_failedpaysB\x0f\n\r_paidinvoicesB\x12\n\x10_expiredinvoices\"g\n)AutocleanstatusAutocleanSucceededforwards\x12\x0f\n\x07\x65nabled\x18\x01 \x01(\x08\x12\x0f\n\x07\x63leaned\x18\x02 \x01(\x04\x12\x10\n\x03\x61ge\x18\x03 \x01(\x04H\x00\x88\x01\x01\x42\x06\n\x04_age\"d\n&AutocleanstatusAutocleanFailedforwards\x12\x0f\n\x07\x65nabled\x18\x01 \x01(\x08\x12\x0f\n\x07\x63leaned\x18\x02 \x01(\x04\x12\x10\n\x03\x61ge\x18\x03 \x01(\x04H\x00\x88\x01\x01\x42\x06\n\x04_age\"c\n%AutocleanstatusAutocleanSucceededpays\x12\x0f\n\x07\x65nabled\x18\x01 \x01(\x08\x12\x0f\n\x07\x63leaned\x18\x02 \x01(\x04\x12\x10\n\x03\x61ge\x18\x03 \x01(\x04H\x00\x88\x01\x01\x42\x06\n\x04_age\"`\n\"AutocleanstatusAutocleanFailedpays\x12\x0f\n\x07\x65nabled\x18\x01 \x01(\x08\x12\x0f\n\x07\x63leaned\x18\x02 \x01(\x04\x12\x10\n\x03\x61ge\x18\x03 \x01(\x04H\x00\x88\x01\x01\x42\x06\n\x04_age\"b\n$AutocleanstatusAutocleanPaidinvoices\x12\x0f\n\x07\x65nabled\x18\x01 \x01(\x08\x12\x0f\n\x07\x63leaned\x18\x02 \x01(\x04\x12\x10\n\x03\x61ge\x18\x03 \x01(\x04H\x00\x88\x01\x01\x42\x06\n\x04_age\"e\n\'AutocleanstatusAutocleanExpiredinvoices\x12\x0f\n\x07\x65nabled\x18\x01 \x01(\x08\x12\x0f\n\x07\x63leaned\x18\x02 \x01(\x04\x12\x10\n\x03\x61ge\x18\x03 \x01(\x04H\x00\x88\x01\x01\x42\x06\n\x04_age\"U\n\x13\x43heckmessageRequest\x12\x0f\n\x07message\x18\x01 \x01(\t\x12\r\n\x05zbase\x18\x02 \x01(\t\x12\x13\n\x06pubkey\x18\x03 \x01(\x0cH\x00\x88\x01\x01\x42\t\n\x07_pubkey\"8\n\x14\x43heckmessageResponse\x12\x10\n\x08verified\x18\x01 \x01(\x08\x12\x0e\n\x06pubkey\x18\x02 \x01(\x0c\"\xcb\x02\n\x0c\x43loseRequest\x12\n\n\x02id\x18\x01 \x01(\t\x12\x1e\n\x11unilateraltimeout\x18\x02 \x01(\rH\x00\x88\x01\x01\x12\x18\n\x0b\x64\x65stination\x18\x03 \x01(\tH\x01\x88\x01\x01\x12!\n\x14\x66\x65\x65_negotiation_step\x18\x04 \x01(\tH\x02\x88\x01\x01\x12)\n\rwrong_funding\x18\x05 \x01(\x0b\x32\r.cln.OutpointH\x03\x88\x01\x01\x12\x1f\n\x12\x66orce_lease_closed\x18\x06 \x01(\x08H\x04\x88\x01\x01\x12\x1e\n\x08\x66\x65\x65range\x18\x07 \x03(\x0b\x32\x0c.cln.FeerateB\x14\n\x12_unilateraltimeoutB\x0e\n\x0c_destinationB\x17\n\x15_fee_negotiation_stepB\x10\n\x0e_wrong_fundingB\x15\n\x13_force_lease_closed\"\xc7\x01\n\rCloseResponse\x12/\n\titem_type\x18\x01 \x01(\x0e\x32\x1c.cln.CloseResponse.CloseType\x12\x0f\n\x02tx\x18\x02 \x01(\x0cH\x00\x88\x01\x01\x12\x11\n\x04txid\x18\x03 \x01(\x0cH\x01\x88\x01\x01\x12\x0b\n\x03txs\x18\x04 \x03(\x0c\x12\r\n\x05txids\x18\x05 \x03(\x0c\"5\n\tCloseType\x12\n\n\x06MUTUAL\x10\x00\x12\x0e\n\nUNILATERAL\x10\x01\x12\x0c\n\x08UNOPENED\x10\x02\x42\x05\n\x03_txB\x07\n\x05_txid\"T\n\x0e\x43onnectRequest\x12\n\n\x02id\x18\x01 \x01(\t\x12\x11\n\x04host\x18\x02 \x01(\tH\x00\x88\x01\x01\x12\x11\n\x04port\x18\x03 \x01(\rH\x01\x88\x01\x01\x42\x07\n\x05_hostB\x07\n\x05_port\"\xb4\x01\n\x0f\x43onnectResponse\x12\n\n\x02id\x18\x01 \x01(\x0c\x12\x10\n\x08\x66\x65\x61tures\x18\x02 \x01(\x0c\x12\x38\n\tdirection\x18\x03 \x01(\x0e\x32%.cln.ConnectResponse.ConnectDirection\x12$\n\x07\x61\x64\x64ress\x18\x04 \x01(\x0b\x32\x13.cln.ConnectAddress\"#\n\x10\x43onnectDirection\x12\x06\n\x02IN\x10\x00\x12\x07\n\x03OUT\x10\x01\"\xfb\x01\n\x0e\x43onnectAddress\x12\x39\n\titem_type\x18\x01 \x01(\x0e\x32&.cln.ConnectAddress.ConnectAddressType\x12\x13\n\x06socket\x18\x02 \x01(\tH\x00\x88\x01\x01\x12\x14\n\x07\x61\x64\x64ress\x18\x03 \x01(\tH\x01\x88\x01\x01\x12\x11\n\x04port\x18\x04 \x01(\rH\x02\x88\x01\x01\"P\n\x12\x43onnectAddressType\x12\x10\n\x0cLOCAL_SOCKET\x10\x00\x12\x08\n\x04IPV4\x10\x01\x12\x08\n\x04IPV6\x10\x02\x12\t\n\x05TORV2\x10\x03\x12\t\n\x05TORV3\x10\x04\x42\t\n\x07_socketB\n\n\x08_addressB\x07\n\x05_port\"J\n\x14\x43reateinvoiceRequest\x12\x11\n\tinvstring\x18\x01 \x01(\t\x12\r\n\x05label\x18\x02 \x01(\t\x12\x10\n\x08preimage\x18\x03 \x01(\x0c\"\xfd\x05\n\x15\x43reateinvoiceResponse\x12\r\n\x05label\x18\x01 \x01(\t\x12\x13\n\x06\x62olt11\x18\x02 \x01(\tH\x00\x88\x01\x01\x12\x13\n\x06\x62olt12\x18\x03 \x01(\tH\x01\x88\x01\x01\x12\x14\n\x0cpayment_hash\x18\x04 \x01(\x0c\x12%\n\x0b\x61mount_msat\x18\x05 \x01(\x0b\x32\x0b.cln.AmountH\x02\x88\x01\x01\x12>\n\x06status\x18\x06 \x01(\x0e\x32..cln.CreateinvoiceResponse.CreateinvoiceStatus\x12\x13\n\x0b\x64\x65scription\x18\x07 \x01(\t\x12\x12\n\nexpires_at\x18\x08 \x01(\x04\x12\x16\n\tpay_index\x18\t \x01(\x04H\x03\x88\x01\x01\x12.\n\x14\x61mount_received_msat\x18\n \x01(\x0b\x32\x0b.cln.AmountH\x04\x88\x01\x01\x12\x14\n\x07paid_at\x18\x0b \x01(\x04H\x05\x88\x01\x01\x12\x1d\n\x10payment_preimage\x18\x0c \x01(\x0cH\x06\x88\x01\x01\x12\x1b\n\x0elocal_offer_id\x18\r \x01(\x0cH\x07\x88\x01\x01\x12\x1e\n\x11invreq_payer_note\x18\x0f \x01(\tH\x08\x88\x01\x01\x12\x1a\n\rcreated_index\x18\x10 \x01(\x04H\t\x88\x01\x01\x12:\n\rpaid_outpoint\x18\x11 \x01(\x0b\x32\x1e.cln.CreateinvoicePaidOutpointH\n\x88\x01\x01\"8\n\x13\x43reateinvoiceStatus\x12\x08\n\x04PAID\x10\x00\x12\x0b\n\x07\x45XPIRED\x10\x01\x12\n\n\x06UNPAID\x10\x02\x42\t\n\x07_bolt11B\t\n\x07_bolt12B\x0e\n\x0c_amount_msatB\x0c\n\n_pay_indexB\x17\n\x15_amount_received_msatB\n\n\x08_paid_atB\x13\n\x11_payment_preimageB\x11\n\x0f_local_offer_idB\x14\n\x12_invreq_payer_noteB\x10\n\x0e_created_indexB\x10\n\x0e_paid_outpoint\"9\n\x19\x43reateinvoicePaidOutpoint\x12\x0c\n\x04txid\x18\x01 \x01(\x0c\x12\x0e\n\x06outnum\x18\x02 \x01(\r\"\xb4\x02\n\x10\x44\x61tastoreRequest\x12\x10\n\x03hex\x18\x02 \x01(\x0cH\x00\x88\x01\x01\x12\x36\n\x04mode\x18\x03 \x01(\x0e\x32#.cln.DatastoreRequest.DatastoreModeH\x01\x88\x01\x01\x12\x17\n\ngeneration\x18\x04 \x01(\x04H\x02\x88\x01\x01\x12\x0b\n\x03key\x18\x05 \x03(\t\x12\x13\n\x06string\x18\x06 \x01(\tH\x03\x88\x01\x01\"p\n\rDatastoreMode\x12\x0f\n\x0bMUST_CREATE\x10\x00\x12\x10\n\x0cMUST_REPLACE\x10\x01\x12\x15\n\x11\x43REATE_OR_REPLACE\x10\x02\x12\x0f\n\x0bMUST_APPEND\x10\x03\x12\x14\n\x10\x43REATE_OR_APPEND\x10\x04\x42\x06\n\x04_hexB\x07\n\x05_modeB\r\n\x0b_generationB\t\n\x07_string\"\x82\x01\n\x11\x44\x61tastoreResponse\x12\x17\n\ngeneration\x18\x02 \x01(\x04H\x00\x88\x01\x01\x12\x10\n\x03hex\x18\x03 \x01(\x0cH\x01\x88\x01\x01\x12\x13\n\x06string\x18\x04 \x01(\tH\x02\x88\x01\x01\x12\x0b\n\x03key\x18\x05 \x03(\tB\r\n\x0b_generationB\x06\n\x04_hexB\t\n\x07_string\"$\n\x15\x44\x61tastoreusageRequest\x12\x0b\n\x03key\x18\x01 \x03(\t\"S\n\x16\x44\x61tastoreusageResponse\x12\x39\n\x0e\x64\x61tastoreusage\x18\x01 \x01(\x0b\x32!.cln.DatastoreusageDatastoreusage\"@\n\x1c\x44\x61tastoreusageDatastoreusage\x12\x0b\n\x03key\x18\x01 \x01(\t\x12\x13\n\x0btotal_bytes\x18\x02 \x01(\x04\"\x9d\x01\n\x12\x43reateonionRequest\x12\"\n\x04hops\x18\x01 \x03(\x0b\x32\x14.cln.CreateonionHops\x12\x11\n\tassocdata\x18\x02 \x01(\x0c\x12\x18\n\x0bsession_key\x18\x03 \x01(\x0cH\x00\x88\x01\x01\x12\x17\n\nonion_size\x18\x04 \x01(\rH\x01\x88\x01\x01\x42\x0e\n\x0c_session_keyB\r\n\x0b_onion_size\"<\n\x13\x43reateonionResponse\x12\r\n\x05onion\x18\x01 \x01(\x0c\x12\x16\n\x0eshared_secrets\x18\x02 \x03(\x0c\"2\n\x0f\x43reateonionHops\x12\x0e\n\x06pubkey\x18\x01 \x01(\x0c\x12\x0f\n\x07payload\x18\x02 \x01(\x0c\"J\n\x13\x44\x65ldatastoreRequest\x12\x17\n\ngeneration\x18\x02 \x01(\x04H\x00\x88\x01\x01\x12\x0b\n\x03key\x18\x03 \x03(\tB\r\n\x0b_generation\"\x85\x01\n\x14\x44\x65ldatastoreResponse\x12\x17\n\ngeneration\x18\x02 \x01(\x04H\x00\x88\x01\x01\x12\x10\n\x03hex\x18\x03 \x01(\x0cH\x01\x88\x01\x01\x12\x13\n\x06string\x18\x04 \x01(\tH\x02\x88\x01\x01\x12\x0b\n\x03key\x18\x05 \x03(\tB\r\n\x0b_generationB\x06\n\x04_hexB\t\n\x07_string\"\xb6\x01\n\x11\x44\x65linvoiceRequest\x12\r\n\x05label\x18\x01 \x01(\t\x12\x37\n\x06status\x18\x02 \x01(\x0e\x32\'.cln.DelinvoiceRequest.DelinvoiceStatus\x12\x15\n\x08\x64\x65sconly\x18\x03 \x01(\x08H\x00\x88\x01\x01\"5\n\x10\x44\x65linvoiceStatus\x12\x08\n\x04PAID\x10\x00\x12\x0b\n\x07\x45XPIRED\x10\x01\x12\n\n\x06UNPAID\x10\x02\x42\x0b\n\t_desconly\"\xe6\x05\n\x12\x44\x65linvoiceResponse\x12\r\n\x05label\x18\x01 \x01(\t\x12\x13\n\x06\x62olt11\x18\x02 \x01(\tH\x00\x88\x01\x01\x12\x13\n\x06\x62olt12\x18\x03 \x01(\tH\x01\x88\x01\x01\x12%\n\x0b\x61mount_msat\x18\x04 \x01(\x0b\x32\x0b.cln.AmountH\x02\x88\x01\x01\x12\x18\n\x0b\x64\x65scription\x18\x05 \x01(\tH\x03\x88\x01\x01\x12\x14\n\x0cpayment_hash\x18\x06 \x01(\x0c\x12\x38\n\x06status\x18\x07 \x01(\x0e\x32(.cln.DelinvoiceResponse.DelinvoiceStatus\x12\x12\n\nexpires_at\x18\x08 \x01(\x04\x12\x1b\n\x0elocal_offer_id\x18\t \x01(\x0cH\x04\x88\x01\x01\x12\x1e\n\x11invreq_payer_note\x18\x0b \x01(\tH\x05\x88\x01\x01\x12\x1a\n\rcreated_index\x18\x0c \x01(\x04H\x06\x88\x01\x01\x12\x1a\n\rupdated_index\x18\r \x01(\x04H\x07\x88\x01\x01\x12\x16\n\tpay_index\x18\x0e \x01(\x04H\x08\x88\x01\x01\x12.\n\x14\x61mount_received_msat\x18\x0f \x01(\x0b\x32\x0b.cln.AmountH\t\x88\x01\x01\x12\x14\n\x07paid_at\x18\x10 \x01(\x04H\n\x88\x01\x01\x12\x1d\n\x10payment_preimage\x18\x11 \x01(\x0cH\x0b\x88\x01\x01\"5\n\x10\x44\x65linvoiceStatus\x12\x08\n\x04PAID\x10\x00\x12\x0b\n\x07\x45XPIRED\x10\x01\x12\n\n\x06UNPAID\x10\x02\x42\t\n\x07_bolt11B\t\n\x07_bolt12B\x0e\n\x0c_amount_msatB\x0e\n\x0c_descriptionB\x11\n\x0f_local_offer_idB\x14\n\x12_invreq_payer_noteB\x10\n\x0e_created_indexB\x10\n\x0e_updated_indexB\x0c\n\n_pay_indexB\x17\n\x15_amount_received_msatB\n\n\x08_paid_atB\x13\n\x11_payment_preimage\"\x9f\x01\n\x17\x44\x65vforgetchannelRequest\x12\n\n\x02id\x18\x01 \x01(\x0c\x12\x1d\n\x10short_channel_id\x18\x02 \x01(\tH\x00\x88\x01\x01\x12\x17\n\nchannel_id\x18\x03 \x01(\x0cH\x01\x88\x01\x01\x12\x12\n\x05\x66orce\x18\x04 \x01(\x08H\x02\x88\x01\x01\x42\x13\n\x11_short_channel_idB\r\n\x0b_channel_idB\x08\n\x06_force\"Y\n\x18\x44\x65vforgetchannelResponse\x12\x0e\n\x06\x66orced\x18\x01 \x01(\x08\x12\x17\n\x0f\x66unding_unspent\x18\x02 \x01(\x08\x12\x14\n\x0c\x66unding_txid\x18\x03 \x01(\x0c\"\x19\n\x17\x45mergencyrecoverRequest\")\n\x18\x45mergencyrecoverResponse\x12\r\n\x05stubs\x18\x01 \x03(\x0c\" \n\x1eGetemergencyrecoverdataRequest\"3\n\x1fGetemergencyrecoverdataResponse\x12\x10\n\x08\x66iledata\x18\x01 \x01(\x0c\"Q\n\x13\x45xposesecretRequest\x12\x12\n\npassphrase\x18\x01 \x01(\t\x12\x17\n\nidentifier\x18\x02 \x01(\tH\x00\x88\x01\x01\x42\r\n\x0b_identifier\";\n\x14\x45xposesecretResponse\x12\x12\n\nidentifier\x18\x01 \x01(\t\x12\x0f\n\x07\x63odex32\x18\x02 \x01(\t\"#\n\x0eRecoverRequest\x12\x11\n\thsmsecret\x18\x01 \x01(\t\"\x88\x01\n\x0fRecoverResponse\x12\x37\n\x06result\x18\x01 \x01(\x0e\x32\".cln.RecoverResponse.RecoverResultH\x00\x88\x01\x01\"1\n\rRecoverResult\x12 \n\x1cRECOVERY_RESTART_IN_PROGRESS\x10\x00\x42\t\n\x07_result\"$\n\x15RecoverchannelRequest\x12\x0b\n\x03scb\x18\x01 \x03(\x0c\"\'\n\x16RecoverchannelResponse\x12\r\n\x05stubs\x18\x01 \x03(\t\"\x99\x02\n\x0eInvoiceRequest\x12\x13\n\x0b\x64\x65scription\x18\x02 \x01(\t\x12\r\n\x05label\x18\x03 \x01(\t\x12\x11\n\tfallbacks\x18\x04 \x03(\t\x12\x15\n\x08preimage\x18\x05 \x01(\x0cH\x00\x88\x01\x01\x12\x11\n\x04\x63ltv\x18\x06 \x01(\rH\x01\x88\x01\x01\x12\x13\n\x06\x65xpiry\x18\x07 \x01(\x04H\x02\x88\x01\x01\x12\x1d\n\x15\x65xposeprivatechannels\x18\x08 \x03(\t\x12\x19\n\x0c\x64\x65schashonly\x18\t \x01(\x08H\x03\x88\x01\x01\x12%\n\x0b\x61mount_msat\x18\n \x01(\x0b\x32\x10.cln.AmountOrAnyB\x0b\n\t_preimageB\x07\n\x05_cltvB\t\n\x07_expiryB\x0f\n\r_deschashonly\"\x95\x03\n\x0fInvoiceResponse\x12\x0e\n\x06\x62olt11\x18\x01 \x01(\t\x12\x14\n\x0cpayment_hash\x18\x02 \x01(\x0c\x12\x16\n\x0epayment_secret\x18\x03 \x01(\x0c\x12\x12\n\nexpires_at\x18\x04 \x01(\x04\x12\x1d\n\x10warning_capacity\x18\x05 \x01(\tH\x00\x88\x01\x01\x12\x1c\n\x0fwarning_offline\x18\x06 \x01(\tH\x01\x88\x01\x01\x12\x1d\n\x10warning_deadends\x18\x07 \x01(\tH\x02\x88\x01\x01\x12#\n\x16warning_private_unused\x18\x08 \x01(\tH\x03\x88\x01\x01\x12\x18\n\x0bwarning_mpp\x18\t \x01(\tH\x04\x88\x01\x01\x12\x1a\n\rcreated_index\x18\n \x01(\x04H\x05\x88\x01\x01\x42\x13\n\x11_warning_capacityB\x12\n\x10_warning_offlineB\x13\n\x11_warning_deadendsB\x19\n\x17_warning_private_unusedB\x0e\n\x0c_warning_mppB\x10\n\x0e_created_index\"\xe1\x01\n\x15InvoicerequestRequest\x12\x1b\n\x06\x61mount\x18\x01 \x01(\x0b\x32\x0b.cln.Amount\x12\x13\n\x0b\x64\x65scription\x18\x02 \x01(\t\x12\x13\n\x06issuer\x18\x03 \x01(\tH\x00\x88\x01\x01\x12\x12\n\x05label\x18\x04 \x01(\tH\x01\x88\x01\x01\x12\x1c\n\x0f\x61\x62solute_expiry\x18\x05 \x01(\x04H\x02\x88\x01\x01\x12\x17\n\nsingle_use\x18\x06 \x01(\x08H\x03\x88\x01\x01\x42\t\n\x07_issuerB\x08\n\x06_labelB\x12\n\x10_absolute_expiryB\r\n\x0b_single_use\"\x8b\x01\n\x16InvoicerequestResponse\x12\x11\n\tinvreq_id\x18\x01 \x01(\x0c\x12\x0e\n\x06\x61\x63tive\x18\x02 \x01(\x08\x12\x12\n\nsingle_use\x18\x03 \x01(\x08\x12\x0e\n\x06\x62olt12\x18\x04 \x01(\t\x12\x0c\n\x04used\x18\x05 \x01(\x08\x12\x12\n\x05label\x18\x06 \x01(\tH\x00\x88\x01\x01\x42\x08\n\x06_label\"1\n\x1c\x44isableinvoicerequestRequest\x12\x11\n\tinvreq_id\x18\x01 \x01(\t\"\x92\x01\n\x1d\x44isableinvoicerequestResponse\x12\x11\n\tinvreq_id\x18\x01 \x01(\x0c\x12\x0e\n\x06\x61\x63tive\x18\x02 \x01(\x08\x12\x12\n\nsingle_use\x18\x03 \x01(\x08\x12\x0e\n\x06\x62olt12\x18\x04 \x01(\t\x12\x0c\n\x04used\x18\x05 \x01(\x08\x12\x12\n\x05label\x18\x06 \x01(\tH\x00\x88\x01\x01\x42\x08\n\x06_label\"l\n\x1aListinvoicerequestsRequest\x12\x16\n\tinvreq_id\x18\x01 \x01(\tH\x00\x88\x01\x01\x12\x18\n\x0b\x61\x63tive_only\x18\x02 \x01(\x08H\x01\x88\x01\x01\x42\x0c\n\n_invreq_idB\x0e\n\x0c_active_only\"_\n\x1bListinvoicerequestsResponse\x12@\n\x0finvoicerequests\x18\x01 \x03(\x0b\x32\'.cln.ListinvoicerequestsInvoicerequests\"\x97\x01\n\"ListinvoicerequestsInvoicerequests\x12\x11\n\tinvreq_id\x18\x01 \x01(\x0c\x12\x0e\n\x06\x61\x63tive\x18\x02 \x01(\x08\x12\x12\n\nsingle_use\x18\x03 \x01(\x08\x12\x0e\n\x06\x62olt12\x18\x04 \x01(\t\x12\x0c\n\x04used\x18\x05 \x01(\x08\x12\x12\n\x05label\x18\x06 \x01(\tH\x00\x88\x01\x01\x42\x08\n\x06_label\"#\n\x14ListdatastoreRequest\x12\x0b\n\x03key\x18\x02 \x03(\t\"G\n\x15ListdatastoreResponse\x12.\n\tdatastore\x18\x01 \x03(\x0b\x32\x1b.cln.ListdatastoreDatastore\"\x87\x01\n\x16ListdatastoreDatastore\x12\x0b\n\x03key\x18\x01 \x03(\t\x12\x17\n\ngeneration\x18\x02 \x01(\x04H\x00\x88\x01\x01\x12\x10\n\x03hex\x18\x03 \x01(\x0cH\x01\x88\x01\x01\x12\x13\n\x06string\x18\x04 \x01(\tH\x02\x88\x01\x01\x42\r\n\x0b_generationB\x06\n\x04_hexB\t\n\x07_string\"\xde\x02\n\x13ListinvoicesRequest\x12\x12\n\x05label\x18\x01 \x01(\tH\x00\x88\x01\x01\x12\x16\n\tinvstring\x18\x02 \x01(\tH\x01\x88\x01\x01\x12\x19\n\x0cpayment_hash\x18\x03 \x01(\x0cH\x02\x88\x01\x01\x12\x15\n\x08offer_id\x18\x04 \x01(\tH\x03\x88\x01\x01\x12>\n\x05index\x18\x05 \x01(\x0e\x32*.cln.ListinvoicesRequest.ListinvoicesIndexH\x04\x88\x01\x01\x12\x12\n\x05start\x18\x06 \x01(\x04H\x05\x88\x01\x01\x12\x12\n\x05limit\x18\x07 \x01(\rH\x06\x88\x01\x01\"-\n\x11ListinvoicesIndex\x12\x0b\n\x07\x43REATED\x10\x00\x12\x0b\n\x07UPDATED\x10\x01\x42\x08\n\x06_labelB\x0c\n\n_invstringB\x0f\n\r_payment_hashB\x0b\n\t_offer_idB\x08\n\x06_indexB\x08\n\x06_startB\x08\n\x06_limit\"C\n\x14ListinvoicesResponse\x12+\n\x08invoices\x18\x01 \x03(\x0b\x32\x19.cln.ListinvoicesInvoices\"\xd3\x06\n\x14ListinvoicesInvoices\x12\r\n\x05label\x18\x01 \x01(\t\x12\x18\n\x0b\x64\x65scription\x18\x02 \x01(\tH\x00\x88\x01\x01\x12\x14\n\x0cpayment_hash\x18\x03 \x01(\x0c\x12\x44\n\x06status\x18\x04 \x01(\x0e\x32\x34.cln.ListinvoicesInvoices.ListinvoicesInvoicesStatus\x12\x12\n\nexpires_at\x18\x05 \x01(\x04\x12%\n\x0b\x61mount_msat\x18\x06 \x01(\x0b\x32\x0b.cln.AmountH\x01\x88\x01\x01\x12\x13\n\x06\x62olt11\x18\x07 \x01(\tH\x02\x88\x01\x01\x12\x13\n\x06\x62olt12\x18\x08 \x01(\tH\x03\x88\x01\x01\x12\x1b\n\x0elocal_offer_id\x18\t \x01(\x0cH\x04\x88\x01\x01\x12\x16\n\tpay_index\x18\x0b \x01(\x04H\x05\x88\x01\x01\x12.\n\x14\x61mount_received_msat\x18\x0c \x01(\x0b\x32\x0b.cln.AmountH\x06\x88\x01\x01\x12\x14\n\x07paid_at\x18\r \x01(\x04H\x07\x88\x01\x01\x12\x1d\n\x10payment_preimage\x18\x0e \x01(\x0cH\x08\x88\x01\x01\x12\x1e\n\x11invreq_payer_note\x18\x0f \x01(\tH\t\x88\x01\x01\x12\x1a\n\rcreated_index\x18\x10 \x01(\x04H\n\x88\x01\x01\x12\x1a\n\rupdated_index\x18\x11 \x01(\x04H\x0b\x88\x01\x01\x12\x41\n\rpaid_outpoint\x18\x12 \x01(\x0b\x32%.cln.ListinvoicesInvoicesPaidOutpointH\x0c\x88\x01\x01\"?\n\x1aListinvoicesInvoicesStatus\x12\n\n\x06UNPAID\x10\x00\x12\x08\n\x04PAID\x10\x01\x12\x0b\n\x07\x45XPIRED\x10\x02\x42\x0e\n\x0c_descriptionB\x0e\n\x0c_amount_msatB\t\n\x07_bolt11B\t\n\x07_bolt12B\x11\n\x0f_local_offer_idB\x0c\n\n_pay_indexB\x17\n\x15_amount_received_msatB\n\n\x08_paid_atB\x13\n\x11_payment_preimageB\x14\n\x12_invreq_payer_noteB\x10\n\x0e_created_indexB\x10\n\x0e_updated_indexB\x10\n\x0e_paid_outpoint\"@\n ListinvoicesInvoicesPaidOutpoint\x12\x0c\n\x04txid\x18\x01 \x01(\x0c\x12\x0e\n\x06outnum\x18\x02 \x01(\r\"\xf6\x03\n\x10SendonionRequest\x12\r\n\x05onion\x18\x01 \x01(\x0c\x12)\n\tfirst_hop\x18\x02 \x01(\x0b\x32\x16.cln.SendonionFirstHop\x12\x14\n\x0cpayment_hash\x18\x03 \x01(\x0c\x12\x12\n\x05label\x18\x04 \x01(\tH\x00\x88\x01\x01\x12\x16\n\x0eshared_secrets\x18\x05 \x03(\x0c\x12\x13\n\x06partid\x18\x06 \x01(\rH\x01\x88\x01\x01\x12\x13\n\x06\x62olt11\x18\x07 \x01(\tH\x02\x88\x01\x01\x12\x18\n\x0b\x64\x65stination\x18\t \x01(\x0cH\x03\x88\x01\x01\x12\x14\n\x07groupid\x18\x0b \x01(\x04H\x04\x88\x01\x01\x12%\n\x0b\x61mount_msat\x18\x0c \x01(\x0b\x32\x0b.cln.AmountH\x05\x88\x01\x01\x12\x1a\n\rlocalinvreqid\x18\r \x01(\x0cH\x06\x88\x01\x01\x12\x18\n\x0b\x64\x65scription\x18\x0e \x01(\tH\x07\x88\x01\x01\x12+\n\x11total_amount_msat\x18\x0f \x01(\x0b\x32\x0b.cln.AmountH\x08\x88\x01\x01\x42\x08\n\x06_labelB\t\n\x07_partidB\t\n\x07_bolt11B\x0e\n\x0c_destinationB\n\n\x08_groupidB\x0e\n\x0c_amount_msatB\x10\n\x0e_localinvreqidB\x0e\n\x0c_descriptionB\x14\n\x12_total_amount_msat\"\xe7\x04\n\x11SendonionResponse\x12\n\n\x02id\x18\x01 \x01(\x04\x12\x14\n\x0cpayment_hash\x18\x02 \x01(\x0c\x12\x36\n\x06status\x18\x03 \x01(\x0e\x32&.cln.SendonionResponse.SendonionStatus\x12%\n\x0b\x61mount_msat\x18\x04 \x01(\x0b\x32\x0b.cln.AmountH\x00\x88\x01\x01\x12\x18\n\x0b\x64\x65stination\x18\x05 \x01(\x0cH\x01\x88\x01\x01\x12\x12\n\ncreated_at\x18\x06 \x01(\x04\x12%\n\x10\x61mount_sent_msat\x18\x07 \x01(\x0b\x32\x0b.cln.Amount\x12\x12\n\x05label\x18\x08 \x01(\tH\x02\x88\x01\x01\x12\x13\n\x06\x62olt11\x18\t \x01(\tH\x03\x88\x01\x01\x12\x13\n\x06\x62olt12\x18\n \x01(\tH\x04\x88\x01\x01\x12\x1d\n\x10payment_preimage\x18\x0b \x01(\x0cH\x05\x88\x01\x01\x12\x14\n\x07message\x18\x0c \x01(\tH\x06\x88\x01\x01\x12\x13\n\x06partid\x18\r \x01(\x04H\x07\x88\x01\x01\x12\x1a\n\rcreated_index\x18\x0e \x01(\x04H\x08\x88\x01\x01\x12\x1a\n\rupdated_index\x18\x0f \x01(\x04H\t\x88\x01\x01\",\n\x0fSendonionStatus\x12\x0b\n\x07PENDING\x10\x00\x12\x0c\n\x08\x43OMPLETE\x10\x01\x42\x0e\n\x0c_amount_msatB\x0e\n\x0c_destinationB\x08\n\x06_labelB\t\n\x07_bolt11B\t\n\x07_bolt12B\x13\n\x11_payment_preimageB\n\n\x08_messageB\t\n\x07_partidB\x10\n\x0e_created_indexB\x10\n\x0e_updated_index\"P\n\x11SendonionFirstHop\x12\n\n\x02id\x18\x01 \x01(\x0c\x12 \n\x0b\x61mount_msat\x18\x02 \x01(\x0b\x32\x0b.cln.Amount\x12\r\n\x05\x64\x65lay\x18\x03 \x01(\r\"\xa0\x03\n\x13ListsendpaysRequest\x12\x13\n\x06\x62olt11\x18\x01 \x01(\tH\x00\x88\x01\x01\x12\x19\n\x0cpayment_hash\x18\x02 \x01(\x0cH\x01\x88\x01\x01\x12@\n\x06status\x18\x03 \x01(\x0e\x32+.cln.ListsendpaysRequest.ListsendpaysStatusH\x02\x88\x01\x01\x12>\n\x05index\x18\x04 \x01(\x0e\x32*.cln.ListsendpaysRequest.ListsendpaysIndexH\x03\x88\x01\x01\x12\x12\n\x05start\x18\x05 \x01(\x04H\x04\x88\x01\x01\x12\x12\n\x05limit\x18\x06 \x01(\rH\x05\x88\x01\x01\";\n\x12ListsendpaysStatus\x12\x0b\n\x07PENDING\x10\x00\x12\x0c\n\x08\x43OMPLETE\x10\x01\x12\n\n\x06\x46\x41ILED\x10\x02\"-\n\x11ListsendpaysIndex\x12\x0b\n\x07\x43REATED\x10\x00\x12\x0b\n\x07UPDATED\x10\x01\x42\t\n\x07_bolt11B\x0f\n\r_payment_hashB\t\n\x07_statusB\x08\n\x06_indexB\x08\n\x06_startB\x08\n\x06_limit\"C\n\x14ListsendpaysResponse\x12+\n\x08payments\x18\x01 \x03(\x0b\x32\x19.cln.ListsendpaysPayments\"\xfc\x05\n\x14ListsendpaysPayments\x12\n\n\x02id\x18\x01 \x01(\x04\x12\x0f\n\x07groupid\x18\x02 \x01(\x04\x12\x14\n\x0cpayment_hash\x18\x03 \x01(\x0c\x12\x44\n\x06status\x18\x04 \x01(\x0e\x32\x34.cln.ListsendpaysPayments.ListsendpaysPaymentsStatus\x12%\n\x0b\x61mount_msat\x18\x05 \x01(\x0b\x32\x0b.cln.AmountH\x00\x88\x01\x01\x12\x18\n\x0b\x64\x65stination\x18\x06 \x01(\x0cH\x01\x88\x01\x01\x12\x12\n\ncreated_at\x18\x07 \x01(\x04\x12%\n\x10\x61mount_sent_msat\x18\x08 \x01(\x0b\x32\x0b.cln.Amount\x12\x12\n\x05label\x18\t \x01(\tH\x02\x88\x01\x01\x12\x13\n\x06\x62olt11\x18\n \x01(\tH\x03\x88\x01\x01\x12\x13\n\x06\x62olt12\x18\x0b \x01(\tH\x04\x88\x01\x01\x12\x1d\n\x10payment_preimage\x18\x0c \x01(\x0cH\x05\x88\x01\x01\x12\x17\n\nerroronion\x18\r \x01(\x0cH\x06\x88\x01\x01\x12\x18\n\x0b\x64\x65scription\x18\x0e \x01(\tH\x07\x88\x01\x01\x12\x13\n\x06partid\x18\x0f \x01(\x04H\x08\x88\x01\x01\x12\x1a\n\rcreated_index\x18\x10 \x01(\x04H\t\x88\x01\x01\x12\x1a\n\rupdated_index\x18\x11 \x01(\x04H\n\x88\x01\x01\x12\x19\n\x0c\x63ompleted_at\x18\x12 \x01(\x04H\x0b\x88\x01\x01\"C\n\x1aListsendpaysPaymentsStatus\x12\x0b\n\x07PENDING\x10\x00\x12\n\n\x06\x46\x41ILED\x10\x01\x12\x0c\n\x08\x43OMPLETE\x10\x02\x42\x0e\n\x0c_amount_msatB\x0e\n\x0c_destinationB\x08\n\x06_labelB\t\n\x07_bolt11B\t\n\x07_bolt12B\x13\n\x11_payment_preimageB\r\n\x0b_erroronionB\x0e\n\x0c_descriptionB\t\n\x07_partidB\x10\n\x0e_created_indexB\x10\n\x0e_updated_indexB\x0f\n\r_completed_at\"\x19\n\x17ListtransactionsRequest\"S\n\x18ListtransactionsResponse\x12\x37\n\x0ctransactions\x18\x01 \x03(\x0b\x32!.cln.ListtransactionsTransactions\"\xf8\x01\n\x1cListtransactionsTransactions\x12\x0c\n\x04hash\x18\x01 \x01(\x0c\x12\r\n\x05rawtx\x18\x02 \x01(\x0c\x12\x13\n\x0b\x62lockheight\x18\x03 \x01(\r\x12\x0f\n\x07txindex\x18\x04 \x01(\r\x12\x10\n\x08locktime\x18\x07 \x01(\r\x12\x0f\n\x07version\x18\x08 \x01(\r\x12\x37\n\x06inputs\x18\t \x03(\x0b\x32\'.cln.ListtransactionsTransactionsInputs\x12\x39\n\x07outputs\x18\n \x03(\x0b\x32(.cln.ListtransactionsTransactionsOutputs\"S\n\"ListtransactionsTransactionsInputs\x12\x0c\n\x04txid\x18\x01 \x01(\x0c\x12\r\n\x05index\x18\x02 \x01(\r\x12\x10\n\x08sequence\x18\x03 \x01(\r\"l\n#ListtransactionsTransactionsOutputs\x12\r\n\x05index\x18\x01 \x01(\r\x12\x14\n\x0cscriptPubKey\x18\x03 \x01(\x0c\x12 \n\x0b\x61mount_msat\x18\x06 \x01(\x0b\x32\x0b.cln.Amount\"M\n\x11MakesecretRequest\x12\x10\n\x03hex\x18\x01 \x01(\x0cH\x00\x88\x01\x01\x12\x13\n\x06string\x18\x02 \x01(\tH\x01\x88\x01\x01\x42\x06\n\x04_hexB\t\n\x07_string\"$\n\x12MakesecretResponse\x12\x0e\n\x06secret\x18\x01 \x01(\x0c\"\x93\x04\n\nPayRequest\x12\x0e\n\x06\x62olt11\x18\x01 \x01(\t\x12\x12\n\x05label\x18\x03 \x01(\tH\x00\x88\x01\x01\x12\x1a\n\rmaxfeepercent\x18\x04 \x01(\x01H\x01\x88\x01\x01\x12\x16\n\tretry_for\x18\x05 \x01(\rH\x02\x88\x01\x01\x12\x15\n\x08maxdelay\x18\x06 \x01(\rH\x03\x88\x01\x01\x12#\n\texemptfee\x18\x07 \x01(\x0b\x32\x0b.cln.AmountH\x04\x88\x01\x01\x12\x17\n\nriskfactor\x18\x08 \x01(\x01H\x05\x88\x01\x01\x12\x0f\n\x07\x65xclude\x18\n \x03(\t\x12 \n\x06maxfee\x18\x0b \x01(\x0b\x32\x0b.cln.AmountH\x06\x88\x01\x01\x12\x18\n\x0b\x64\x65scription\x18\x0c \x01(\tH\x07\x88\x01\x01\x12%\n\x0b\x61mount_msat\x18\r \x01(\x0b\x32\x0b.cln.AmountH\x08\x88\x01\x01\x12\x1a\n\rlocalinvreqid\x18\x0e \x01(\x0cH\t\x88\x01\x01\x12&\n\x0cpartial_msat\x18\x0f \x01(\x0b\x32\x0b.cln.AmountH\n\x88\x01\x01\x42\x08\n\x06_labelB\x10\n\x0e_maxfeepercentB\x0c\n\n_retry_forB\x0b\n\t_maxdelayB\x0c\n\n_exemptfeeB\r\n\x0b_riskfactorB\t\n\x07_maxfeeB\x0e\n\x0c_descriptionB\x0e\n\x0c_amount_msatB\x10\n\x0e_localinvreqidB\x0f\n\r_partial_msat\"\xfb\x02\n\x0bPayResponse\x12\x18\n\x10payment_preimage\x18\x01 \x01(\x0c\x12\x18\n\x0b\x64\x65stination\x18\x02 \x01(\x0cH\x00\x88\x01\x01\x12\x14\n\x0cpayment_hash\x18\x03 \x01(\x0c\x12\x12\n\ncreated_at\x18\x04 \x01(\x01\x12\r\n\x05parts\x18\x05 \x01(\r\x12 \n\x0b\x61mount_msat\x18\x06 \x01(\x0b\x32\x0b.cln.Amount\x12%\n\x10\x61mount_sent_msat\x18\x07 \x01(\x0b\x32\x0b.cln.Amount\x12\'\n\x1awarning_partial_completion\x18\x08 \x01(\tH\x01\x88\x01\x01\x12*\n\x06status\x18\t \x01(\x0e\x32\x1a.cln.PayResponse.PayStatus\"2\n\tPayStatus\x12\x0c\n\x08\x43OMPLETE\x10\x00\x12\x0b\n\x07PENDING\x10\x01\x12\n\n\x06\x46\x41ILED\x10\x02\x42\x0e\n\x0c_destinationB\x1d\n\x1b_warning_partial_completion\"*\n\x10ListnodesRequest\x12\x0f\n\x02id\x18\x01 \x01(\x0cH\x00\x88\x01\x01\x42\x05\n\x03_id\"7\n\x11ListnodesResponse\x12\"\n\x05nodes\x18\x01 \x03(\x0b\x32\x13.cln.ListnodesNodes\"\xb8\x02\n\x0eListnodesNodes\x12\x0e\n\x06nodeid\x18\x01 \x01(\x0c\x12\x1b\n\x0elast_timestamp\x18\x02 \x01(\rH\x00\x88\x01\x01\x12\x12\n\x05\x61lias\x18\x03 \x01(\tH\x01\x88\x01\x01\x12\x12\n\x05\x63olor\x18\x04 \x01(\x0cH\x02\x88\x01\x01\x12\x15\n\x08\x66\x65\x61tures\x18\x05 \x01(\x0cH\x03\x88\x01\x01\x12/\n\taddresses\x18\x06 \x03(\x0b\x32\x1c.cln.ListnodesNodesAddresses\x12@\n\x10option_will_fund\x18\x07 \x01(\x0b\x32!.cln.ListnodesNodesOptionWillFundH\x04\x88\x01\x01\x42\x11\n\x0f_last_timestampB\x08\n\x06_aliasB\x08\n\x06_colorB\x0b\n\t_featuresB\x13\n\x11_option_will_fund\"\xf2\x01\n\x1cListnodesNodesOptionWillFund\x12(\n\x13lease_fee_base_msat\x18\x01 \x01(\x0b\x32\x0b.cln.Amount\x12\x17\n\x0flease_fee_basis\x18\x02 \x01(\r\x12\x16\n\x0e\x66unding_weight\x18\x03 \x01(\r\x12.\n\x19\x63hannel_fee_max_base_msat\x18\x04 \x01(\x0b\x32\x0b.cln.Amount\x12\x30\n(channel_fee_max_proportional_thousandths\x18\x05 \x01(\r\x12\x15\n\rcompact_lease\x18\x06 \x01(\x0c\"\xe8\x01\n\x17ListnodesNodesAddresses\x12K\n\titem_type\x18\x01 \x01(\x0e\x32\x38.cln.ListnodesNodesAddresses.ListnodesNodesAddressesType\x12\x0c\n\x04port\x18\x02 \x01(\r\x12\x14\n\x07\x61\x64\x64ress\x18\x03 \x01(\tH\x00\x88\x01\x01\"P\n\x1bListnodesNodesAddressesType\x12\x07\n\x03\x44NS\x10\x00\x12\x08\n\x04IPV4\x10\x01\x12\x08\n\x04IPV6\x10\x02\x12\t\n\x05TORV2\x10\x03\x12\t\n\x05TORV3\x10\x04\x42\n\n\x08_address\"g\n\x15WaitanyinvoiceRequest\x12\x1a\n\rlastpay_index\x18\x01 \x01(\x04H\x00\x88\x01\x01\x12\x14\n\x07timeout\x18\x02 \x01(\x04H\x01\x88\x01\x01\x42\x10\n\x0e_lastpay_indexB\n\n\x08_timeout\"\xd3\x05\n\x16WaitanyinvoiceResponse\x12\r\n\x05label\x18\x01 \x01(\t\x12\x18\n\x0b\x64\x65scription\x18\x02 \x01(\tH\x00\x88\x01\x01\x12\x14\n\x0cpayment_hash\x18\x03 \x01(\x0c\x12@\n\x06status\x18\x04 \x01(\x0e\x32\x30.cln.WaitanyinvoiceResponse.WaitanyinvoiceStatus\x12\x12\n\nexpires_at\x18\x05 \x01(\x04\x12%\n\x0b\x61mount_msat\x18\x06 \x01(\x0b\x32\x0b.cln.AmountH\x01\x88\x01\x01\x12\x13\n\x06\x62olt11\x18\x07 \x01(\tH\x02\x88\x01\x01\x12\x13\n\x06\x62olt12\x18\x08 \x01(\tH\x03\x88\x01\x01\x12\x16\n\tpay_index\x18\t \x01(\x04H\x04\x88\x01\x01\x12.\n\x14\x61mount_received_msat\x18\n \x01(\x0b\x32\x0b.cln.AmountH\x05\x88\x01\x01\x12\x14\n\x07paid_at\x18\x0b \x01(\x04H\x06\x88\x01\x01\x12\x1d\n\x10payment_preimage\x18\x0c \x01(\x0cH\x07\x88\x01\x01\x12\x1a\n\rcreated_index\x18\r \x01(\x04H\x08\x88\x01\x01\x12\x1a\n\rupdated_index\x18\x0e \x01(\x04H\t\x88\x01\x01\x12;\n\rpaid_outpoint\x18\x0f \x01(\x0b\x32\x1f.cln.WaitanyinvoicePaidOutpointH\n\x88\x01\x01\"-\n\x14WaitanyinvoiceStatus\x12\x08\n\x04PAID\x10\x00\x12\x0b\n\x07\x45XPIRED\x10\x01\x42\x0e\n\x0c_descriptionB\x0e\n\x0c_amount_msatB\t\n\x07_bolt11B\t\n\x07_bolt12B\x0c\n\n_pay_indexB\x17\n\x15_amount_received_msatB\n\n\x08_paid_atB\x13\n\x11_payment_preimageB\x10\n\x0e_created_indexB\x10\n\x0e_updated_indexB\x10\n\x0e_paid_outpoint\":\n\x1aWaitanyinvoicePaidOutpoint\x12\x0c\n\x04txid\x18\x01 \x01(\x0c\x12\x0e\n\x06outnum\x18\x02 \x01(\r\"#\n\x12WaitinvoiceRequest\x12\r\n\x05label\x18\x01 \x01(\t\"\xc4\x05\n\x13WaitinvoiceResponse\x12\r\n\x05label\x18\x01 \x01(\t\x12\x18\n\x0b\x64\x65scription\x18\x02 \x01(\tH\x00\x88\x01\x01\x12\x14\n\x0cpayment_hash\x18\x03 \x01(\x0c\x12:\n\x06status\x18\x04 \x01(\x0e\x32*.cln.WaitinvoiceResponse.WaitinvoiceStatus\x12\x12\n\nexpires_at\x18\x05 \x01(\x04\x12%\n\x0b\x61mount_msat\x18\x06 \x01(\x0b\x32\x0b.cln.AmountH\x01\x88\x01\x01\x12\x13\n\x06\x62olt11\x18\x07 \x01(\tH\x02\x88\x01\x01\x12\x13\n\x06\x62olt12\x18\x08 \x01(\tH\x03\x88\x01\x01\x12\x16\n\tpay_index\x18\t \x01(\x04H\x04\x88\x01\x01\x12.\n\x14\x61mount_received_msat\x18\n \x01(\x0b\x32\x0b.cln.AmountH\x05\x88\x01\x01\x12\x14\n\x07paid_at\x18\x0b \x01(\x04H\x06\x88\x01\x01\x12\x1d\n\x10payment_preimage\x18\x0c \x01(\x0cH\x07\x88\x01\x01\x12\x1a\n\rcreated_index\x18\r \x01(\x04H\x08\x88\x01\x01\x12\x1a\n\rupdated_index\x18\x0e \x01(\x04H\t\x88\x01\x01\x12\x38\n\rpaid_outpoint\x18\x0f \x01(\x0b\x32\x1c.cln.WaitinvoicePaidOutpointH\n\x88\x01\x01\"*\n\x11WaitinvoiceStatus\x12\x08\n\x04PAID\x10\x00\x12\x0b\n\x07\x45XPIRED\x10\x01\x42\x0e\n\x0c_descriptionB\x0e\n\x0c_amount_msatB\t\n\x07_bolt11B\t\n\x07_bolt12B\x0c\n\n_pay_indexB\x17\n\x15_amount_received_msatB\n\n\x08_paid_atB\x13\n\x11_payment_preimageB\x10\n\x0e_created_indexB\x10\n\x0e_updated_indexB\x10\n\x0e_paid_outpoint\"7\n\x17WaitinvoicePaidOutpoint\x12\x0c\n\x04txid\x18\x01 \x01(\x0c\x12\x0e\n\x06outnum\x18\x02 \x01(\r\"\x8e\x01\n\x12WaitsendpayRequest\x12\x14\n\x0cpayment_hash\x18\x01 \x01(\x0c\x12\x13\n\x06partid\x18\x02 \x01(\x04H\x00\x88\x01\x01\x12\x14\n\x07timeout\x18\x03 \x01(\rH\x01\x88\x01\x01\x12\x14\n\x07groupid\x18\x04 \x01(\x04H\x02\x88\x01\x01\x42\t\n\x07_partidB\n\n\x08_timeoutB\n\n\x08_groupid\"\x8e\x05\n\x13WaitsendpayResponse\x12\n\n\x02id\x18\x01 \x01(\x04\x12\x14\n\x07groupid\x18\x02 \x01(\x04H\x00\x88\x01\x01\x12\x14\n\x0cpayment_hash\x18\x03 \x01(\x0c\x12:\n\x06status\x18\x04 \x01(\x0e\x32*.cln.WaitsendpayResponse.WaitsendpayStatus\x12%\n\x0b\x61mount_msat\x18\x05 \x01(\x0b\x32\x0b.cln.AmountH\x01\x88\x01\x01\x12\x18\n\x0b\x64\x65stination\x18\x06 \x01(\x0cH\x02\x88\x01\x01\x12\x12\n\ncreated_at\x18\x07 \x01(\x04\x12%\n\x10\x61mount_sent_msat\x18\x08 \x01(\x0b\x32\x0b.cln.Amount\x12\x12\n\x05label\x18\t \x01(\tH\x03\x88\x01\x01\x12\x13\n\x06partid\x18\n \x01(\x04H\x04\x88\x01\x01\x12\x13\n\x06\x62olt11\x18\x0b \x01(\tH\x05\x88\x01\x01\x12\x13\n\x06\x62olt12\x18\x0c \x01(\tH\x06\x88\x01\x01\x12\x1d\n\x10payment_preimage\x18\r \x01(\x0cH\x07\x88\x01\x01\x12\x19\n\x0c\x63ompleted_at\x18\x0e \x01(\x01H\x08\x88\x01\x01\x12\x1a\n\rcreated_index\x18\x0f \x01(\x04H\t\x88\x01\x01\x12\x1a\n\rupdated_index\x18\x10 \x01(\x04H\n\x88\x01\x01\"!\n\x11WaitsendpayStatus\x12\x0c\n\x08\x43OMPLETE\x10\x00\x42\n\n\x08_groupidB\x0e\n\x0c_amount_msatB\x0e\n\x0c_destinationB\x08\n\x06_labelB\t\n\x07_partidB\t\n\x07_bolt11B\t\n\x07_bolt12B\x13\n\x11_payment_preimageB\x0f\n\r_completed_atB\x10\n\x0e_created_indexB\x10\n\x0e_updated_index\"\x97\x01\n\x0eNewaddrRequest\x12@\n\x0b\x61\x64\x64resstype\x18\x01 \x01(\x0e\x32&.cln.NewaddrRequest.NewaddrAddresstypeH\x00\x88\x01\x01\"3\n\x12NewaddrAddresstype\x12\n\n\x06\x42\x45\x43H32\x10\x00\x12\x07\n\x03\x41LL\x10\x02\x12\x08\n\x04P2TR\x10\x03\x42\x0e\n\x0c_addresstype\"M\n\x0fNewaddrResponse\x12\x13\n\x06\x62\x65\x63h32\x18\x01 \x01(\tH\x00\x88\x01\x01\x12\x11\n\x04p2tr\x18\x03 \x01(\tH\x01\x88\x01\x01\x42\t\n\x07_bech32B\x07\n\x05_p2tr\"\xb9\x01\n\x0fWithdrawRequest\x12\x13\n\x0b\x64\x65stination\x18\x01 \x01(\t\x12!\n\x07satoshi\x18\x02 \x01(\x0b\x32\x10.cln.AmountOrAll\x12\x14\n\x07minconf\x18\x03 \x01(\rH\x00\x88\x01\x01\x12\x1c\n\x05utxos\x18\x04 \x03(\x0b\x32\r.cln.Outpoint\x12\"\n\x07\x66\x65\x65rate\x18\x05 \x01(\x0b\x32\x0c.cln.FeerateH\x01\x88\x01\x01\x42\n\n\x08_minconfB\n\n\x08_feerate\":\n\x10WithdrawResponse\x12\n\n\x02tx\x18\x01 \x01(\x0c\x12\x0c\n\x04txid\x18\x02 \x01(\x0c\x12\x0c\n\x04psbt\x18\x03 \x01(\t\"\xaf\x03\n\x0eKeysendRequest\x12\x13\n\x0b\x64\x65stination\x18\x01 \x01(\x0c\x12\x12\n\x05label\x18\x03 \x01(\tH\x00\x88\x01\x01\x12\x1a\n\rmaxfeepercent\x18\x04 \x01(\x01H\x01\x88\x01\x01\x12\x16\n\tretry_for\x18\x05 \x01(\rH\x02\x88\x01\x01\x12\x15\n\x08maxdelay\x18\x06 \x01(\rH\x03\x88\x01\x01\x12#\n\texemptfee\x18\x07 \x01(\x0b\x32\x0b.cln.AmountH\x04\x88\x01\x01\x12+\n\nroutehints\x18\x08 \x01(\x0b\x32\x12.cln.RoutehintListH\x05\x88\x01\x01\x12&\n\textratlvs\x18\t \x01(\x0b\x32\x0e.cln.TlvStreamH\x06\x88\x01\x01\x12 \n\x0b\x61mount_msat\x18\n \x01(\x0b\x32\x0b.cln.Amount\x12 \n\x06maxfee\x18\x0b \x01(\x0b\x32\x0b.cln.AmountH\x07\x88\x01\x01\x42\x08\n\x06_labelB\x10\n\x0e_maxfeepercentB\x0c\n\n_retry_forB\x0b\n\t_maxdelayB\x0c\n\n_exemptfeeB\r\n\x0b_routehintsB\x0c\n\n_extratlvsB\t\n\x07_maxfee\"\xf2\x02\n\x0fKeysendResponse\x12\x18\n\x10payment_preimage\x18\x01 \x01(\x0c\x12\x18\n\x0b\x64\x65stination\x18\x02 \x01(\x0cH\x00\x88\x01\x01\x12\x14\n\x0cpayment_hash\x18\x03 \x01(\x0c\x12\x12\n\ncreated_at\x18\x04 \x01(\x01\x12\r\n\x05parts\x18\x05 \x01(\r\x12 \n\x0b\x61mount_msat\x18\x06 \x01(\x0b\x32\x0b.cln.Amount\x12%\n\x10\x61mount_sent_msat\x18\x07 \x01(\x0b\x32\x0b.cln.Amount\x12\'\n\x1awarning_partial_completion\x18\x08 \x01(\tH\x01\x88\x01\x01\x12\x32\n\x06status\x18\t \x01(\x0e\x32\".cln.KeysendResponse.KeysendStatus\"\x1d\n\rKeysendStatus\x12\x0c\n\x08\x43OMPLETE\x10\x00\x42\x0e\n\x0c_destinationB\x1d\n\x1b_warning_partial_completion\"\xa4\x03\n\x0f\x46undpsbtRequest\x12!\n\x07satoshi\x18\x01 \x01(\x0b\x32\x10.cln.AmountOrAll\x12\x1d\n\x07\x66\x65\x65rate\x18\x02 \x01(\x0b\x32\x0c.cln.Feerate\x12\x13\n\x0bstartweight\x18\x03 \x01(\r\x12\x14\n\x07minconf\x18\x04 \x01(\rH\x00\x88\x01\x01\x12\x14\n\x07reserve\x18\x05 \x01(\rH\x01\x88\x01\x01\x12\x15\n\x08locktime\x18\x06 \x01(\rH\x02\x88\x01\x01\x12\x1f\n\x12min_witness_weight\x18\x07 \x01(\rH\x03\x88\x01\x01\x12\x1d\n\x10\x65xcess_as_change\x18\x08 \x01(\x08H\x04\x88\x01\x01\x12\x17\n\nnonwrapped\x18\t \x01(\x08H\x05\x88\x01\x01\x12#\n\x16opening_anchor_channel\x18\n \x01(\x08H\x06\x88\x01\x01\x42\n\n\x08_minconfB\n\n\x08_reserveB\x0b\n\t_locktimeB\x15\n\x13_min_witness_weightB\x13\n\x11_excess_as_changeB\r\n\x0b_nonwrappedB\x19\n\x17_opening_anchor_channel\"\xd9\x01\n\x10\x46undpsbtResponse\x12\x0c\n\x04psbt\x18\x01 \x01(\t\x12\x16\n\x0e\x66\x65\x65rate_per_kw\x18\x02 \x01(\r\x12\x1e\n\x16\x65stimated_final_weight\x18\x03 \x01(\r\x12 \n\x0b\x65xcess_msat\x18\x04 \x01(\x0b\x32\x0b.cln.Amount\x12\x1a\n\rchange_outnum\x18\x05 \x01(\rH\x00\x88\x01\x01\x12/\n\x0creservations\x18\x06 \x03(\x0b\x32\x19.cln.FundpsbtReservationsB\x10\n\x0e_change_outnum\"u\n\x14\x46undpsbtReservations\x12\x0c\n\x04txid\x18\x01 \x01(\x0c\x12\x0c\n\x04vout\x18\x02 \x01(\r\x12\x14\n\x0cwas_reserved\x18\x03 \x01(\x08\x12\x10\n\x08reserved\x18\x04 \x01(\x08\x12\x19\n\x11reserved_to_block\x18\x05 \x01(\r\"A\n\x0fSendpsbtRequest\x12\x0c\n\x04psbt\x18\x01 \x01(\t\x12\x14\n\x07reserve\x18\x02 \x01(\rH\x00\x88\x01\x01\x42\n\n\x08_reserve\",\n\x10SendpsbtResponse\x12\n\n\x02tx\x18\x01 \x01(\x0c\x12\x0c\n\x04txid\x18\x02 \x01(\x0c\"1\n\x0fSignpsbtRequest\x12\x0c\n\x04psbt\x18\x01 \x01(\t\x12\x10\n\x08signonly\x18\x02 \x03(\r\"\'\n\x10SignpsbtResponse\x12\x13\n\x0bsigned_psbt\x18\x01 \x01(\t\"\xa0\x03\n\x0fUtxopsbtRequest\x12!\n\x07satoshi\x18\x01 \x01(\x0b\x32\x10.cln.AmountOrAll\x12\x1d\n\x07\x66\x65\x65rate\x18\x02 \x01(\x0b\x32\x0c.cln.Feerate\x12\x13\n\x0bstartweight\x18\x03 \x01(\r\x12\x1c\n\x05utxos\x18\x04 \x03(\x0b\x32\r.cln.Outpoint\x12\x14\n\x07reserve\x18\x05 \x01(\rH\x00\x88\x01\x01\x12\x15\n\x08locktime\x18\x06 \x01(\rH\x01\x88\x01\x01\x12\x1f\n\x12min_witness_weight\x18\x07 \x01(\rH\x02\x88\x01\x01\x12\x17\n\nreservedok\x18\x08 \x01(\x08H\x03\x88\x01\x01\x12\x1d\n\x10\x65xcess_as_change\x18\t \x01(\x08H\x04\x88\x01\x01\x12#\n\x16opening_anchor_channel\x18\n \x01(\x08H\x05\x88\x01\x01\x42\n\n\x08_reserveB\x0b\n\t_locktimeB\x15\n\x13_min_witness_weightB\r\n\x0b_reservedokB\x13\n\x11_excess_as_changeB\x19\n\x17_opening_anchor_channel\"\xd9\x01\n\x10UtxopsbtResponse\x12\x0c\n\x04psbt\x18\x01 \x01(\t\x12\x16\n\x0e\x66\x65\x65rate_per_kw\x18\x02 \x01(\r\x12\x1e\n\x16\x65stimated_final_weight\x18\x03 \x01(\r\x12 \n\x0b\x65xcess_msat\x18\x04 \x01(\x0b\x32\x0b.cln.Amount\x12\x1a\n\rchange_outnum\x18\x05 \x01(\rH\x00\x88\x01\x01\x12/\n\x0creservations\x18\x06 \x03(\x0b\x32\x19.cln.UtxopsbtReservationsB\x10\n\x0e_change_outnum\"u\n\x14UtxopsbtReservations\x12\x0c\n\x04txid\x18\x01 \x01(\x0c\x12\x0c\n\x04vout\x18\x02 \x01(\r\x12\x14\n\x0cwas_reserved\x18\x03 \x01(\x08\x12\x10\n\x08reserved\x18\x04 \x01(\x08\x12\x19\n\x11reserved_to_block\x18\x05 \x01(\r\" \n\x10TxdiscardRequest\x12\x0c\n\x04txid\x18\x01 \x01(\x0c\"6\n\x11TxdiscardResponse\x12\x13\n\x0bunsigned_tx\x18\x01 \x01(\x0c\x12\x0c\n\x04txid\x18\x02 \x01(\x0c\"\xa4\x01\n\x10TxprepareRequest\x12\"\n\x07\x66\x65\x65rate\x18\x02 \x01(\x0b\x32\x0c.cln.FeerateH\x00\x88\x01\x01\x12\x14\n\x07minconf\x18\x03 \x01(\rH\x01\x88\x01\x01\x12\x1c\n\x05utxos\x18\x04 \x03(\x0b\x32\r.cln.Outpoint\x12 \n\x07outputs\x18\x05 \x03(\x0b\x32\x0f.cln.OutputDescB\n\n\x08_feerateB\n\n\x08_minconf\"D\n\x11TxprepareResponse\x12\x0c\n\x04psbt\x18\x01 \x01(\t\x12\x13\n\x0bunsigned_tx\x18\x02 \x01(\x0c\x12\x0c\n\x04txid\x18\x03 \x01(\x0c\"\x1d\n\rTxsendRequest\x12\x0c\n\x04txid\x18\x01 \x01(\x0c\"8\n\x0eTxsendResponse\x12\x0c\n\x04psbt\x18\x01 \x01(\t\x12\n\n\x02tx\x18\x02 \x01(\x0c\x12\x0c\n\x04txid\x18\x03 \x01(\x0c\"e\n\x17ListpeerchannelsRequest\x12\x0f\n\x02id\x18\x01 \x01(\x0cH\x00\x88\x01\x01\x12\x1d\n\x10short_channel_id\x18\x02 \x01(\tH\x01\x88\x01\x01\x42\x05\n\x03_idB\x13\n\x11_short_channel_id\"K\n\x18ListpeerchannelsResponse\x12/\n\x08\x63hannels\x18\x01 \x03(\x0b\x32\x1d.cln.ListpeerchannelsChannels\"\xf4\x19\n\x18ListpeerchannelsChannels\x12\x0f\n\x07peer_id\x18\x01 \x01(\x0c\x12\x16\n\x0epeer_connected\x18\x02 \x01(\x08\x12 \n\x05state\x18\x03 \x01(\x0e\x32\x11.cln.ChannelState\x12\x19\n\x0cscratch_txid\x18\x04 \x01(\x0cH\x00\x88\x01\x01\x12:\n\x07\x66\x65\x65rate\x18\x06 \x01(\x0b\x32$.cln.ListpeerchannelsChannelsFeerateH\x01\x88\x01\x01\x12\x12\n\x05owner\x18\x07 \x01(\tH\x02\x88\x01\x01\x12\x1d\n\x10short_channel_id\x18\x08 \x01(\tH\x03\x88\x01\x01\x12\x17\n\nchannel_id\x18\t \x01(\x0cH\x04\x88\x01\x01\x12\x19\n\x0c\x66unding_txid\x18\n \x01(\x0cH\x05\x88\x01\x01\x12\x1b\n\x0e\x66unding_outnum\x18\x0b \x01(\rH\x06\x88\x01\x01\x12\x1c\n\x0finitial_feerate\x18\x0c \x01(\tH\x07\x88\x01\x01\x12\x19\n\x0clast_feerate\x18\r \x01(\tH\x08\x88\x01\x01\x12\x19\n\x0cnext_feerate\x18\x0e \x01(\tH\t\x88\x01\x01\x12\x1a\n\rnext_fee_step\x18\x0f \x01(\rH\n\x88\x01\x01\x12\x37\n\x08inflight\x18\x10 \x03(\x0b\x32%.cln.ListpeerchannelsChannelsInflight\x12\x15\n\x08\x63lose_to\x18\x11 \x01(\x0cH\x0b\x88\x01\x01\x12\x14\n\x07private\x18\x12 \x01(\x08H\x0c\x88\x01\x01\x12 \n\x06opener\x18\x13 \x01(\x0e\x32\x10.cln.ChannelSide\x12%\n\x06\x63loser\x18\x14 \x01(\x0e\x32\x10.cln.ChannelSideH\r\x88\x01\x01\x12:\n\x07\x66unding\x18\x16 \x01(\x0b\x32$.cln.ListpeerchannelsChannelsFundingH\x0e\x88\x01\x01\x12$\n\nto_us_msat\x18\x17 \x01(\x0b\x32\x0b.cln.AmountH\x0f\x88\x01\x01\x12(\n\x0emin_to_us_msat\x18\x18 \x01(\x0b\x32\x0b.cln.AmountH\x10\x88\x01\x01\x12(\n\x0emax_to_us_msat\x18\x19 \x01(\x0b\x32\x0b.cln.AmountH\x11\x88\x01\x01\x12$\n\ntotal_msat\x18\x1a \x01(\x0b\x32\x0b.cln.AmountH\x12\x88\x01\x01\x12\'\n\rfee_base_msat\x18\x1b \x01(\x0b\x32\x0b.cln.AmountH\x13\x88\x01\x01\x12(\n\x1b\x66\x65\x65_proportional_millionths\x18\x1c \x01(\rH\x14\x88\x01\x01\x12)\n\x0f\x64ust_limit_msat\x18\x1d \x01(\x0b\x32\x0b.cln.AmountH\x15\x88\x01\x01\x12\x30\n\x16max_total_htlc_in_msat\x18\x1e \x01(\x0b\x32\x0b.cln.AmountH\x16\x88\x01\x01\x12,\n\x12their_reserve_msat\x18\x1f \x01(\x0b\x32\x0b.cln.AmountH\x17\x88\x01\x01\x12*\n\x10our_reserve_msat\x18 \x01(\x0b\x32\x0b.cln.AmountH\x18\x88\x01\x01\x12(\n\x0espendable_msat\x18! \x01(\x0b\x32\x0b.cln.AmountH\x19\x88\x01\x01\x12)\n\x0freceivable_msat\x18\" \x01(\x0b\x32\x0b.cln.AmountH\x1a\x88\x01\x01\x12.\n\x14minimum_htlc_in_msat\x18# \x01(\x0b\x32\x0b.cln.AmountH\x1b\x88\x01\x01\x12/\n\x15minimum_htlc_out_msat\x18$ \x01(\x0b\x32\x0b.cln.AmountH\x1c\x88\x01\x01\x12/\n\x15maximum_htlc_out_msat\x18% \x01(\x0b\x32\x0b.cln.AmountH\x1d\x88\x01\x01\x12 \n\x13their_to_self_delay\x18& \x01(\rH\x1e\x88\x01\x01\x12\x1e\n\x11our_to_self_delay\x18\' \x01(\rH\x1f\x88\x01\x01\x12\x1f\n\x12max_accepted_htlcs\x18( \x01(\rH \x88\x01\x01\x12\x36\n\x05\x61lias\x18) \x01(\x0b\x32\".cln.ListpeerchannelsChannelsAliasH!\x88\x01\x01\x12\x0e\n\x06status\x18+ \x03(\t\x12 \n\x13in_payments_offered\x18, \x01(\x04H\"\x88\x01\x01\x12)\n\x0fin_offered_msat\x18- \x01(\x0b\x32\x0b.cln.AmountH#\x88\x01\x01\x12\"\n\x15in_payments_fulfilled\x18. \x01(\x04H$\x88\x01\x01\x12+\n\x11in_fulfilled_msat\x18/ \x01(\x0b\x32\x0b.cln.AmountH%\x88\x01\x01\x12!\n\x14out_payments_offered\x18\x30 \x01(\x04H&\x88\x01\x01\x12*\n\x10out_offered_msat\x18\x31 \x01(\x0b\x32\x0b.cln.AmountH\'\x88\x01\x01\x12#\n\x16out_payments_fulfilled\x18\x32 \x01(\x04H(\x88\x01\x01\x12,\n\x12out_fulfilled_msat\x18\x33 \x01(\x0b\x32\x0b.cln.AmountH)\x88\x01\x01\x12\x31\n\x05htlcs\x18\x34 \x03(\x0b\x32\".cln.ListpeerchannelsChannelsHtlcs\x12\x1a\n\rclose_to_addr\x18\x35 \x01(\tH*\x88\x01\x01\x12\x1e\n\x11ignore_fee_limits\x18\x36 \x01(\x08H+\x88\x01\x01\x12:\n\x07updates\x18\x37 \x01(\x0b\x32$.cln.ListpeerchannelsChannelsUpdatesH,\x88\x01\x01\x12#\n\x16last_stable_connection\x18\x38 \x01(\x04H-\x88\x01\x01\x12\x17\n\nlost_state\x18\x39 \x01(\x08H.\x88\x01\x01\x12\x1a\n\rreestablished\x18: \x01(\x08H/\x88\x01\x01\x12*\n\x10last_tx_fee_msat\x18; \x01(\x0b\x32\x0b.cln.AmountH0\x88\x01\x01\x12\x16\n\tdirection\x18< \x01(\rH1\x88\x01\x01\x12=\n#their_max_htlc_value_in_flight_msat\x18= \x01(\x0b\x32\x0b.cln.AmountH2\x88\x01\x01\x12;\n!our_max_htlc_value_in_flight_msat\x18> \x01(\x0b\x32\x0b.cln.AmountH3\x88\x01\x01\x42\x0f\n\r_scratch_txidB\n\n\x08_feerateB\x08\n\x06_ownerB\x13\n\x11_short_channel_idB\r\n\x0b_channel_idB\x0f\n\r_funding_txidB\x11\n\x0f_funding_outnumB\x12\n\x10_initial_feerateB\x0f\n\r_last_feerateB\x0f\n\r_next_feerateB\x10\n\x0e_next_fee_stepB\x0b\n\t_close_toB\n\n\x08_privateB\t\n\x07_closerB\n\n\x08_fundingB\r\n\x0b_to_us_msatB\x11\n\x0f_min_to_us_msatB\x11\n\x0f_max_to_us_msatB\r\n\x0b_total_msatB\x10\n\x0e_fee_base_msatB\x1e\n\x1c_fee_proportional_millionthsB\x12\n\x10_dust_limit_msatB\x19\n\x17_max_total_htlc_in_msatB\x15\n\x13_their_reserve_msatB\x13\n\x11_our_reserve_msatB\x11\n\x0f_spendable_msatB\x12\n\x10_receivable_msatB\x17\n\x15_minimum_htlc_in_msatB\x18\n\x16_minimum_htlc_out_msatB\x18\n\x16_maximum_htlc_out_msatB\x16\n\x14_their_to_self_delayB\x14\n\x12_our_to_self_delayB\x15\n\x13_max_accepted_htlcsB\x08\n\x06_aliasB\x16\n\x14_in_payments_offeredB\x12\n\x10_in_offered_msatB\x18\n\x16_in_payments_fulfilledB\x14\n\x12_in_fulfilled_msatB\x17\n\x15_out_payments_offeredB\x13\n\x11_out_offered_msatB\x19\n\x17_out_payments_fulfilledB\x15\n\x13_out_fulfilled_msatB\x10\n\x0e_close_to_addrB\x14\n\x12_ignore_fee_limitsB\n\n\x08_updatesB\x19\n\x17_last_stable_connectionB\r\n\x0b_lost_stateB\x10\n\x0e_reestablishedB\x13\n\x11_last_tx_fee_msatB\x0c\n\n_directionB&\n$_their_max_htlc_value_in_flight_msatB$\n\"_our_max_htlc_value_in_flight_msat\"\xa7\x01\n\x1fListpeerchannelsChannelsUpdates\x12\x38\n\x05local\x18\x01 \x01(\x0b\x32).cln.ListpeerchannelsChannelsUpdatesLocal\x12?\n\x06remote\x18\x02 \x01(\x0b\x32*.cln.ListpeerchannelsChannelsUpdatesRemoteH\x00\x88\x01\x01\x42\t\n\x07_remote\"\xda\x01\n$ListpeerchannelsChannelsUpdatesLocal\x12&\n\x11htlc_minimum_msat\x18\x01 \x01(\x0b\x32\x0b.cln.Amount\x12&\n\x11htlc_maximum_msat\x18\x02 \x01(\x0b\x32\x0b.cln.Amount\x12\x19\n\x11\x63ltv_expiry_delta\x18\x03 \x01(\r\x12\"\n\rfee_base_msat\x18\x04 \x01(\x0b\x32\x0b.cln.Amount\x12#\n\x1b\x66\x65\x65_proportional_millionths\x18\x05 \x01(\r\"\xdb\x01\n%ListpeerchannelsChannelsUpdatesRemote\x12&\n\x11htlc_minimum_msat\x18\x01 \x01(\x0b\x32\x0b.cln.Amount\x12&\n\x11htlc_maximum_msat\x18\x02 \x01(\x0b\x32\x0b.cln.Amount\x12\x19\n\x11\x63ltv_expiry_delta\x18\x03 \x01(\r\x12\"\n\rfee_base_msat\x18\x04 \x01(\x0b\x32\x0b.cln.Amount\x12#\n\x1b\x66\x65\x65_proportional_millionths\x18\x05 \x01(\r\"?\n\x1fListpeerchannelsChannelsFeerate\x12\r\n\x05perkw\x18\x01 \x01(\r\x12\r\n\x05perkb\x18\x02 \x01(\r\"\x8b\x02\n ListpeerchannelsChannelsInflight\x12\x14\n\x0c\x66unding_txid\x18\x01 \x01(\x0c\x12\x16\n\x0e\x66unding_outnum\x18\x02 \x01(\r\x12\x0f\n\x07\x66\x65\x65rate\x18\x03 \x01(\t\x12\'\n\x12total_funding_msat\x18\x04 \x01(\x0b\x32\x0b.cln.Amount\x12%\n\x10our_funding_msat\x18\x05 \x01(\x0b\x32\x0b.cln.Amount\x12\x19\n\x0cscratch_txid\x18\x06 \x01(\x0cH\x00\x88\x01\x01\x12\x1a\n\rsplice_amount\x18\x07 \x01(\x12H\x01\x88\x01\x01\x42\x0f\n\r_scratch_txidB\x10\n\x0e_splice_amount\"\x9d\x02\n\x1fListpeerchannelsChannelsFunding\x12%\n\x0bpushed_msat\x18\x01 \x01(\x0b\x32\x0b.cln.AmountH\x00\x88\x01\x01\x12%\n\x10local_funds_msat\x18\x02 \x01(\x0b\x32\x0b.cln.Amount\x12&\n\x11remote_funds_msat\x18\x03 \x01(\x0b\x32\x0b.cln.Amount\x12\'\n\rfee_paid_msat\x18\x04 \x01(\x0b\x32\x0b.cln.AmountH\x01\x88\x01\x01\x12\'\n\rfee_rcvd_msat\x18\x05 \x01(\x0b\x32\x0b.cln.AmountH\x02\x88\x01\x01\x42\x0e\n\x0c_pushed_msatB\x10\n\x0e_fee_paid_msatB\x10\n\x0e_fee_rcvd_msat\"]\n\x1dListpeerchannelsChannelsAlias\x12\x12\n\x05local\x18\x01 \x01(\tH\x00\x88\x01\x01\x12\x13\n\x06remote\x18\x02 \x01(\tH\x01\x88\x01\x01\x42\x08\n\x06_localB\t\n\x07_remote\"\xf9\x02\n\x1dListpeerchannelsChannelsHtlcs\x12\\\n\tdirection\x18\x01 \x01(\x0e\x32I.cln.ListpeerchannelsChannelsHtlcs.ListpeerchannelsChannelsHtlcsDirection\x12\n\n\x02id\x18\x02 \x01(\x04\x12 \n\x0b\x61mount_msat\x18\x03 \x01(\x0b\x32\x0b.cln.Amount\x12\x0e\n\x06\x65xpiry\x18\x04 \x01(\r\x12\x14\n\x0cpayment_hash\x18\x05 \x01(\x0c\x12\x1a\n\rlocal_trimmed\x18\x06 \x01(\x08H\x00\x88\x01\x01\x12\x13\n\x06status\x18\x07 \x01(\tH\x01\x88\x01\x01\x12\x1d\n\x05state\x18\x08 \x01(\x0e\x32\x0e.cln.HtlcState\"9\n&ListpeerchannelsChannelsHtlcsDirection\x12\x06\n\x02IN\x10\x00\x12\x07\n\x03OUT\x10\x01\x42\x10\n\x0e_local_trimmedB\t\n\x07_status\"3\n\x19ListclosedchannelsRequest\x12\x0f\n\x02id\x18\x01 \x01(\x0cH\x00\x88\x01\x01\x42\x05\n\x03_id\"[\n\x1aListclosedchannelsResponse\x12=\n\x0e\x63losedchannels\x18\x01 \x03(\x0b\x32%.cln.ListclosedchannelsClosedchannels\"\xf0\t\n ListclosedchannelsClosedchannels\x12\x14\n\x07peer_id\x18\x01 \x01(\x0cH\x00\x88\x01\x01\x12\x12\n\nchannel_id\x18\x02 \x01(\x0c\x12\x1d\n\x10short_channel_id\x18\x03 \x01(\tH\x01\x88\x01\x01\x12>\n\x05\x61lias\x18\x04 \x01(\x0b\x32*.cln.ListclosedchannelsClosedchannelsAliasH\x02\x88\x01\x01\x12 \n\x06opener\x18\x05 \x01(\x0e\x32\x10.cln.ChannelSide\x12%\n\x06\x63loser\x18\x06 \x01(\x0e\x32\x10.cln.ChannelSideH\x03\x88\x01\x01\x12\x0f\n\x07private\x18\x07 \x01(\x08\x12\x1f\n\x17total_local_commitments\x18\t \x01(\x04\x12 \n\x18total_remote_commitments\x18\n \x01(\x04\x12\x18\n\x10total_htlcs_sent\x18\x0b \x01(\x04\x12\x14\n\x0c\x66unding_txid\x18\x0c \x01(\x0c\x12\x16\n\x0e\x66unding_outnum\x18\r \x01(\r\x12\x0e\n\x06leased\x18\x0e \x01(\x08\x12/\n\x15\x66unding_fee_paid_msat\x18\x0f \x01(\x0b\x32\x0b.cln.AmountH\x04\x88\x01\x01\x12/\n\x15\x66unding_fee_rcvd_msat\x18\x10 \x01(\x0b\x32\x0b.cln.AmountH\x05\x88\x01\x01\x12-\n\x13\x66unding_pushed_msat\x18\x11 \x01(\x0b\x32\x0b.cln.AmountH\x06\x88\x01\x01\x12\x1f\n\ntotal_msat\x18\x12 \x01(\x0b\x32\x0b.cln.Amount\x12%\n\x10\x66inal_to_us_msat\x18\x13 \x01(\x0b\x32\x0b.cln.Amount\x12#\n\x0emin_to_us_msat\x18\x14 \x01(\x0b\x32\x0b.cln.Amount\x12#\n\x0emax_to_us_msat\x18\x15 \x01(\x0b\x32\x0b.cln.Amount\x12!\n\x14last_commitment_txid\x18\x16 \x01(\x0cH\x07\x88\x01\x01\x12\x32\n\x18last_commitment_fee_msat\x18\x17 \x01(\x0b\x32\x0b.cln.AmountH\x08\x88\x01\x01\x12\x65\n\x0b\x63lose_cause\x18\x18 \x01(\x0e\x32P.cln.ListclosedchannelsClosedchannels.ListclosedchannelsClosedchannelsCloseCause\x12#\n\x16last_stable_connection\x18\x19 \x01(\x04H\t\x88\x01\x01\"u\n*ListclosedchannelsClosedchannelsCloseCause\x12\x0b\n\x07UNKNOWN\x10\x00\x12\t\n\x05LOCAL\x10\x01\x12\x08\n\x04USER\x10\x02\x12\n\n\x06REMOTE\x10\x03\x12\x0c\n\x08PROTOCOL\x10\x04\x12\x0b\n\x07ONCHAIN\x10\x05\x42\n\n\x08_peer_idB\x13\n\x11_short_channel_idB\x08\n\x06_aliasB\t\n\x07_closerB\x18\n\x16_funding_fee_paid_msatB\x18\n\x16_funding_fee_rcvd_msatB\x16\n\x14_funding_pushed_msatB\x17\n\x15_last_commitment_txidB\x1b\n\x19_last_commitment_fee_msatB\x19\n\x17_last_stable_connection\"e\n%ListclosedchannelsClosedchannelsAlias\x12\x12\n\x05local\x18\x01 \x01(\tH\x00\x88\x01\x01\x12\x13\n\x06remote\x18\x02 \x01(\tH\x01\x88\x01\x01\x42\x08\n\x06_localB\t\n\x07_remote\"L\n\x10\x44\x65\x63odepayRequest\x12\x0e\n\x06\x62olt11\x18\x01 \x01(\t\x12\x18\n\x0b\x64\x65scription\x18\x02 \x01(\tH\x00\x88\x01\x01\x42\x0e\n\x0c_description\"\xc7\x04\n\x11\x44\x65\x63odepayResponse\x12\x10\n\x08\x63urrency\x18\x01 \x01(\t\x12\x12\n\ncreated_at\x18\x02 \x01(\x04\x12\x0e\n\x06\x65xpiry\x18\x03 \x01(\x04\x12\r\n\x05payee\x18\x04 \x01(\x0c\x12%\n\x0b\x61mount_msat\x18\x05 \x01(\x0b\x32\x0b.cln.AmountH\x00\x88\x01\x01\x12\x14\n\x0cpayment_hash\x18\x06 \x01(\x0c\x12\x11\n\tsignature\x18\x07 \x01(\t\x12\x18\n\x0b\x64\x65scription\x18\x08 \x01(\tH\x01\x88\x01\x01\x12\x1d\n\x10\x64\x65scription_hash\x18\t \x01(\x0cH\x02\x88\x01\x01\x12\x1d\n\x15min_final_cltv_expiry\x18\n \x01(\r\x12\x1b\n\x0epayment_secret\x18\x0b \x01(\x0cH\x03\x88\x01\x01\x12\x15\n\x08\x66\x65\x61tures\x18\x0c \x01(\x0cH\x04\x88\x01\x01\x12\x1d\n\x10payment_metadata\x18\r \x01(\x0cH\x05\x88\x01\x01\x12*\n\tfallbacks\x18\x0e \x03(\x0b\x32\x17.cln.DecodepayFallbacks\x12\"\n\x05\x65xtra\x18\x10 \x03(\x0b\x32\x13.cln.DecodepayExtra\x12-\n\x06routes\x18\x11 \x01(\x0b\x32\x18.cln.DecodeRoutehintListH\x06\x88\x01\x01\x42\x0e\n\x0c_amount_msatB\x0e\n\x0c_descriptionB\x13\n\x11_description_hashB\x11\n\x0f_payment_secretB\x0b\n\t_featuresB\x13\n\x11_payment_metadataB\t\n\x07_routes\"\xd0\x01\n\x12\x44\x65\x63odepayFallbacks\x12\x41\n\titem_type\x18\x01 \x01(\x0e\x32..cln.DecodepayFallbacks.DecodepayFallbacksType\x12\x11\n\x04\x61\x64\x64r\x18\x02 \x01(\tH\x00\x88\x01\x01\x12\x0b\n\x03hex\x18\x03 \x01(\x0c\"N\n\x16\x44\x65\x63odepayFallbacksType\x12\t\n\x05P2PKH\x10\x00\x12\x08\n\x04P2SH\x10\x01\x12\n\n\x06P2WPKH\x10\x02\x12\t\n\x05P2WSH\x10\x03\x12\x08\n\x04P2TR\x10\x04\x42\x07\n\x05_addr\"+\n\x0e\x44\x65\x63odepayExtra\x12\x0b\n\x03tag\x18\x01 \x01(\t\x12\x0c\n\x04\x64\x61ta\x18\x02 \x01(\t\"\x1f\n\rDecodeRequest\x12\x0e\n\x06string\x18\x01 \x01(\t\"\x8c(\n\x0e\x44\x65\x63odeResponse\x12\x31\n\titem_type\x18\x01 \x01(\x0e\x32\x1e.cln.DecodeResponse.DecodeType\x12\r\n\x05valid\x18\x02 \x01(\x08\x12\x15\n\x08offer_id\x18\x03 \x01(\x0cH\x00\x88\x01\x01\x12\x14\n\x0coffer_chains\x18\x04 \x03(\x0c\x12\x1b\n\x0eoffer_metadata\x18\x05 \x01(\x0cH\x01\x88\x01\x01\x12\x1b\n\x0eoffer_currency\x18\x06 \x01(\tH\x02\x88\x01\x01\x12+\n\x1ewarning_unknown_offer_currency\x18\x07 \x01(\tH\x03\x88\x01\x01\x12 \n\x13\x63urrency_minor_unit\x18\x08 \x01(\rH\x04\x88\x01\x01\x12\x19\n\x0coffer_amount\x18\t \x01(\x04H\x05\x88\x01\x01\x12+\n\x11offer_amount_msat\x18\n \x01(\x0b\x32\x0b.cln.AmountH\x06\x88\x01\x01\x12\x1e\n\x11offer_description\x18\x0b \x01(\tH\x07\x88\x01\x01\x12\x19\n\x0coffer_issuer\x18\x0c \x01(\tH\x08\x88\x01\x01\x12\x1b\n\x0eoffer_features\x18\r \x01(\x0cH\t\x88\x01\x01\x12\"\n\x15offer_absolute_expiry\x18\x0e \x01(\x04H\n\x88\x01\x01\x12\x1f\n\x12offer_quantity_max\x18\x0f \x01(\x04H\x0b\x88\x01\x01\x12*\n\x0boffer_paths\x18\x10 \x03(\x0b\x32\x15.cln.DecodeOfferPaths\x12\x1a\n\roffer_node_id\x18\x11 \x01(\x0cH\x0c\x88\x01\x01\x12*\n\x1dwarning_missing_offer_node_id\x18\x14 \x01(\tH\r\x88\x01\x01\x12.\n!warning_invalid_offer_description\x18\x15 \x01(\tH\x0e\x88\x01\x01\x12.\n!warning_missing_offer_description\x18\x16 \x01(\tH\x0f\x88\x01\x01\x12+\n\x1ewarning_invalid_offer_currency\x18\x17 \x01(\tH\x10\x88\x01\x01\x12)\n\x1cwarning_invalid_offer_issuer\x18\x18 \x01(\tH\x11\x88\x01\x01\x12\x1c\n\x0finvreq_metadata\x18\x19 \x01(\x0cH\x12\x88\x01\x01\x12\x1c\n\x0finvreq_payer_id\x18\x1a \x01(\x0cH\x13\x88\x01\x01\x12\x19\n\x0cinvreq_chain\x18\x1b \x01(\x0cH\x14\x88\x01\x01\x12,\n\x12invreq_amount_msat\x18\x1c \x01(\x0b\x32\x0b.cln.AmountH\x15\x88\x01\x01\x12\x1c\n\x0finvreq_features\x18\x1d \x01(\x0cH\x16\x88\x01\x01\x12\x1c\n\x0finvreq_quantity\x18\x1e \x01(\x04H\x17\x88\x01\x01\x12\x1e\n\x11invreq_payer_note\x18\x1f \x01(\tH\x18\x88\x01\x01\x12&\n\x19invreq_recurrence_counter\x18 \x01(\rH\x19\x88\x01\x01\x12$\n\x17invreq_recurrence_start\x18! \x01(\rH\x1a\x88\x01\x01\x12,\n\x1fwarning_missing_invreq_metadata\x18# \x01(\tH\x1b\x88\x01\x01\x12,\n\x1fwarning_missing_invreq_payer_id\x18$ \x01(\tH\x1c\x88\x01\x01\x12.\n!warning_invalid_invreq_payer_note\x18% \x01(\tH\x1d\x88\x01\x01\x12\x36\n)warning_missing_invoice_request_signature\x18& \x01(\tH\x1e\x88\x01\x01\x12\x36\n)warning_invalid_invoice_request_signature\x18\' \x01(\tH\x1f\x88\x01\x01\x12\x1f\n\x12invoice_created_at\x18) \x01(\x04H \x88\x01\x01\x12$\n\x17invoice_relative_expiry\x18* \x01(\rH!\x88\x01\x01\x12!\n\x14invoice_payment_hash\x18+ \x01(\x0cH\"\x88\x01\x01\x12-\n\x13invoice_amount_msat\x18, \x01(\x0b\x32\x0b.cln.AmountH#\x88\x01\x01\x12\x36\n\x11invoice_fallbacks\x18- \x03(\x0b\x32\x1b.cln.DecodeInvoiceFallbacks\x12\x1d\n\x10invoice_features\x18. \x01(\x0cH$\x88\x01\x01\x12\x1c\n\x0finvoice_node_id\x18/ \x01(\x0cH%\x88\x01\x01\x12(\n\x1binvoice_recurrence_basetime\x18\x30 \x01(\x04H&\x88\x01\x01\x12*\n\x1dwarning_missing_invoice_paths\x18\x32 \x01(\tH\'\x88\x01\x01\x12/\n\"warning_missing_invoice_blindedpay\x18\x33 \x01(\tH(\x88\x01\x01\x12/\n\"warning_missing_invoice_created_at\x18\x34 \x01(\tH)\x88\x01\x01\x12\x31\n$warning_missing_invoice_payment_hash\x18\x35 \x01(\tH*\x88\x01\x01\x12+\n\x1ewarning_missing_invoice_amount\x18\x36 \x01(\tH+\x88\x01\x01\x12\x38\n+warning_missing_invoice_recurrence_basetime\x18\x37 \x01(\tH,\x88\x01\x01\x12,\n\x1fwarning_missing_invoice_node_id\x18\x38 \x01(\tH-\x88\x01\x01\x12.\n!warning_missing_invoice_signature\x18\x39 \x01(\tH.\x88\x01\x01\x12.\n!warning_invalid_invoice_signature\x18: \x01(\tH/\x88\x01\x01\x12\'\n\tfallbacks\x18; \x03(\x0b\x32\x14.cln.DecodeFallbacks\x12\x17\n\ncreated_at\x18< \x01(\x04H0\x88\x01\x01\x12\x13\n\x06\x65xpiry\x18= \x01(\x04H1\x88\x01\x01\x12\x12\n\x05payee\x18> \x01(\x0cH2\x88\x01\x01\x12\x19\n\x0cpayment_hash\x18? \x01(\x0cH3\x88\x01\x01\x12\x1d\n\x10\x64\x65scription_hash\x18@ \x01(\x0cH4\x88\x01\x01\x12\"\n\x15min_final_cltv_expiry\x18\x41 \x01(\rH5\x88\x01\x01\x12\x1b\n\x0epayment_secret\x18\x42 \x01(\x0cH6\x88\x01\x01\x12\x1d\n\x10payment_metadata\x18\x43 \x01(\x0cH7\x88\x01\x01\x12\x1f\n\x05\x65xtra\x18\x45 \x03(\x0b\x32\x10.cln.DecodeExtra\x12\x16\n\tunique_id\x18\x46 \x01(\tH8\x88\x01\x01\x12\x14\n\x07version\x18G \x01(\tH9\x88\x01\x01\x12\x13\n\x06string\x18H \x01(\tH:\x88\x01\x01\x12-\n\x0crestrictions\x18I \x03(\x0b\x32\x17.cln.DecodeRestrictions\x12&\n\x19warning_rune_invalid_utf8\x18J \x01(\tH;\x88\x01\x01\x12\x10\n\x03hex\x18K \x01(\x0cH<\x88\x01\x01\x12\x16\n\tdecrypted\x18L \x01(\x0cH=\x88\x01\x01\x12\x16\n\tsignature\x18M \x01(\tH>\x88\x01\x01\x12\x15\n\x08\x63urrency\x18N \x01(\tH?\x88\x01\x01\x12%\n\x0b\x61mount_msat\x18O \x01(\x0b\x32\x0b.cln.AmountH@\x88\x01\x01\x12\x18\n\x0b\x64\x65scription\x18P \x01(\tHA\x88\x01\x01\x12\x15\n\x08\x66\x65\x61tures\x18Q \x01(\x0cHB\x88\x01\x01\x12-\n\x06routes\x18R \x01(\x0b\x32\x18.cln.DecodeRoutehintListHC\x88\x01\x01\x12\x1c\n\x0foffer_issuer_id\x18S \x01(\x0cHD\x88\x01\x01\x12,\n\x1fwarning_missing_offer_issuer_id\x18T \x01(\tHE\x88\x01\x01\x12,\n\x0cinvreq_paths\x18U \x03(\x0b\x32\x16.cln.DecodeInvreqPaths\x12\'\n\x1awarning_empty_blinded_path\x18V \x01(\tHF\x88\x01\x01\x12=\n\x13invreq_bip_353_name\x18W \x01(\x0b\x32\x1b.cln.DecodeInvreqBip353NameHG\x88\x01\x01\x12\x35\n(warning_invreq_bip_353_name_name_invalid\x18X \x01(\tHH\x88\x01\x01\x12\x37\n*warning_invreq_bip_353_name_domain_invalid\x18Y \x01(\tHI\x88\x01\x01\"\x83\x01\n\nDecodeType\x12\x10\n\x0c\x42OLT12_OFFER\x10\x00\x12\x12\n\x0e\x42OLT12_INVOICE\x10\x01\x12\x1a\n\x16\x42OLT12_INVOICE_REQUEST\x10\x02\x12\x12\n\x0e\x42OLT11_INVOICE\x10\x03\x12\x08\n\x04RUNE\x10\x04\x12\x15\n\x11\x45MERGENCY_RECOVER\x10\x05\x42\x0b\n\t_offer_idB\x11\n\x0f_offer_metadataB\x11\n\x0f_offer_currencyB!\n\x1f_warning_unknown_offer_currencyB\x16\n\x14_currency_minor_unitB\x0f\n\r_offer_amountB\x14\n\x12_offer_amount_msatB\x14\n\x12_offer_descriptionB\x0f\n\r_offer_issuerB\x11\n\x0f_offer_featuresB\x18\n\x16_offer_absolute_expiryB\x15\n\x13_offer_quantity_maxB\x10\n\x0e_offer_node_idB \n\x1e_warning_missing_offer_node_idB$\n\"_warning_invalid_offer_descriptionB$\n\"_warning_missing_offer_descriptionB!\n\x1f_warning_invalid_offer_currencyB\x1f\n\x1d_warning_invalid_offer_issuerB\x12\n\x10_invreq_metadataB\x12\n\x10_invreq_payer_idB\x0f\n\r_invreq_chainB\x15\n\x13_invreq_amount_msatB\x12\n\x10_invreq_featuresB\x12\n\x10_invreq_quantityB\x14\n\x12_invreq_payer_noteB\x1c\n\x1a_invreq_recurrence_counterB\x1a\n\x18_invreq_recurrence_startB\"\n _warning_missing_invreq_metadataB\"\n _warning_missing_invreq_payer_idB$\n\"_warning_invalid_invreq_payer_noteB,\n*_warning_missing_invoice_request_signatureB,\n*_warning_invalid_invoice_request_signatureB\x15\n\x13_invoice_created_atB\x1a\n\x18_invoice_relative_expiryB\x17\n\x15_invoice_payment_hashB\x16\n\x14_invoice_amount_msatB\x13\n\x11_invoice_featuresB\x12\n\x10_invoice_node_idB\x1e\n\x1c_invoice_recurrence_basetimeB \n\x1e_warning_missing_invoice_pathsB%\n#_warning_missing_invoice_blindedpayB%\n#_warning_missing_invoice_created_atB\'\n%_warning_missing_invoice_payment_hashB!\n\x1f_warning_missing_invoice_amountB.\n,_warning_missing_invoice_recurrence_basetimeB\"\n _warning_missing_invoice_node_idB$\n\"_warning_missing_invoice_signatureB$\n\"_warning_invalid_invoice_signatureB\r\n\x0b_created_atB\t\n\x07_expiryB\x08\n\x06_payeeB\x0f\n\r_payment_hashB\x13\n\x11_description_hashB\x18\n\x16_min_final_cltv_expiryB\x11\n\x0f_payment_secretB\x13\n\x11_payment_metadataB\x0c\n\n_unique_idB\n\n\x08_versionB\t\n\x07_stringB\x1c\n\x1a_warning_rune_invalid_utf8B\x06\n\x04_hexB\x0c\n\n_decryptedB\x0c\n\n_signatureB\x0b\n\t_currencyB\x0e\n\x0c_amount_msatB\x0e\n\x0c_descriptionB\x0b\n\t_featuresB\t\n\x07_routesB\x12\n\x10_offer_issuer_idB\"\n _warning_missing_offer_issuer_idB\x1d\n\x1b_warning_empty_blinded_pathB\x16\n\x14_invreq_bip_353_nameB+\n)_warning_invreq_bip_353_name_name_invalidB-\n+_warning_invreq_bip_353_name_domain_invalid\"\xec\x01\n\x10\x44\x65\x63odeOfferPaths\x12\x1a\n\rfirst_node_id\x18\x01 \x01(\x0cH\x00\x88\x01\x01\x12\x15\n\x08\x62linding\x18\x02 \x01(\x0cH\x01\x88\x01\x01\x12\x1b\n\x0e\x66irst_scid_dir\x18\x04 \x01(\rH\x02\x88\x01\x01\x12\x17\n\nfirst_scid\x18\x05 \x01(\tH\x03\x88\x01\x01\x12\x1b\n\x0e\x66irst_path_key\x18\x06 \x01(\x0cH\x04\x88\x01\x01\x42\x10\n\x0e_first_node_idB\x0b\n\t_blindingB\x11\n\x0f_first_scid_dirB\r\n\x0b_first_scidB\x11\n\x0f_first_path_key\"\x89\x01\n\x1e\x44\x65\x63odeOfferRecurrencePaywindow\x12\x16\n\x0eseconds_before\x18\x01 \x01(\r\x12\x15\n\rseconds_after\x18\x02 \x01(\r\x12 \n\x13proportional_amount\x18\x03 \x01(\x08H\x00\x88\x01\x01\x42\x16\n\x14_proportional_amount\"\x97\x02\n\x11\x44\x65\x63odeInvreqPaths\x12\x1b\n\x0e\x66irst_scid_dir\x18\x01 \x01(\rH\x00\x88\x01\x01\x12\x15\n\x08\x62linding\x18\x02 \x01(\x0cH\x01\x88\x01\x01\x12\x1a\n\rfirst_node_id\x18\x03 \x01(\x0cH\x02\x88\x01\x01\x12\x17\n\nfirst_scid\x18\x04 \x01(\tH\x03\x88\x01\x01\x12(\n\x04path\x18\x05 \x03(\x0b\x32\x1a.cln.DecodeInvreqPathsPath\x12\x1b\n\x0e\x66irst_path_key\x18\x06 \x01(\x0cH\x04\x88\x01\x01\x42\x11\n\x0f_first_scid_dirB\x0b\n\t_blindingB\x10\n\x0e_first_node_idB\r\n\x0b_first_scidB\x11\n\x0f_first_path_key\"R\n\x15\x44\x65\x63odeInvreqPathsPath\x12\x17\n\x0f\x62linded_node_id\x18\x01 \x01(\x0c\x12 \n\x18\x65ncrypted_recipient_data\x18\x02 \x01(\x0c\"T\n\x16\x44\x65\x63odeInvreqBip353Name\x12\x11\n\x04name\x18\x01 \x01(\tH\x00\x88\x01\x01\x12\x13\n\x06\x64omain\x18\x02 \x01(\tH\x01\x88\x01\x01\x42\x07\n\x05_nameB\t\n\x07_domain\"S\n\x16\x44\x65\x63odeInvoicePathsPath\x12\x17\n\x0f\x62linded_node_id\x18\x01 \x01(\x0c\x12 \n\x18\x65ncrypted_recipient_data\x18\x02 \x01(\x0c\"X\n\x16\x44\x65\x63odeInvoiceFallbacks\x12\x0f\n\x07version\x18\x01 \x01(\r\x12\x0b\n\x03hex\x18\x02 \x01(\x0c\x12\x14\n\x07\x61\x64\x64ress\x18\x03 \x01(\tH\x00\x88\x01\x01\x42\n\n\x08_address\"\xaa\x02\n\x0f\x44\x65\x63odeFallbacks\x12\x36\n)warning_invoice_fallbacks_version_invalid\x18\x01 \x01(\tH\x00\x88\x01\x01\x12;\n\titem_type\x18\x02 \x01(\x0e\x32(.cln.DecodeFallbacks.DecodeFallbacksType\x12\x11\n\x04\x61\x64\x64r\x18\x03 \x01(\tH\x01\x88\x01\x01\x12\x0b\n\x03hex\x18\x04 \x01(\x0c\"K\n\x13\x44\x65\x63odeFallbacksType\x12\t\n\x05P2PKH\x10\x00\x12\x08\n\x04P2SH\x10\x01\x12\n\n\x06P2WPKH\x10\x02\x12\t\n\x05P2WSH\x10\x03\x12\x08\n\x04P2TR\x10\x04\x42,\n*_warning_invoice_fallbacks_version_invalidB\x07\n\x05_addr\"(\n\x0b\x44\x65\x63odeExtra\x12\x0b\n\x03tag\x18\x01 \x01(\t\x12\x0c\n\x04\x64\x61ta\x18\x02 \x01(\t\";\n\x12\x44\x65\x63odeRestrictions\x12\x14\n\x0c\x61lternatives\x18\x01 \x03(\t\x12\x0f\n\x07summary\x18\x02 \x01(\t\"\xc2\x01\n\rDelpayRequest\x12\x14\n\x0cpayment_hash\x18\x01 \x01(\x0c\x12/\n\x06status\x18\x02 \x01(\x0e\x32\x1f.cln.DelpayRequest.DelpayStatus\x12\x13\n\x06partid\x18\x03 \x01(\x04H\x00\x88\x01\x01\x12\x14\n\x07groupid\x18\x04 \x01(\x04H\x01\x88\x01\x01\"(\n\x0c\x44\x65lpayStatus\x12\x0c\n\x08\x43OMPLETE\x10\x00\x12\n\n\x06\x46\x41ILED\x10\x01\x42\t\n\x07_partidB\n\n\x08_groupid\"7\n\x0e\x44\x65lpayResponse\x12%\n\x08payments\x18\x01 \x03(\x0b\x32\x13.cln.DelpayPayments\"\xcb\x05\n\x0e\x44\x65lpayPayments\x12\x1a\n\rcreated_index\x18\x01 \x01(\x04H\x00\x88\x01\x01\x12\n\n\x02id\x18\x02 \x01(\x04\x12\x14\n\x0cpayment_hash\x18\x03 \x01(\x0c\x12\x38\n\x06status\x18\x04 \x01(\x0e\x32(.cln.DelpayPayments.DelpayPaymentsStatus\x12%\n\x10\x61mount_sent_msat\x18\x05 \x01(\x0b\x32\x0b.cln.Amount\x12\x13\n\x06partid\x18\x06 \x01(\x04H\x01\x88\x01\x01\x12\x18\n\x0b\x64\x65stination\x18\x07 \x01(\x0cH\x02\x88\x01\x01\x12%\n\x0b\x61mount_msat\x18\x08 \x01(\x0b\x32\x0b.cln.AmountH\x03\x88\x01\x01\x12\x12\n\ncreated_at\x18\t \x01(\x04\x12\x1a\n\rupdated_index\x18\n \x01(\x04H\x04\x88\x01\x01\x12\x19\n\x0c\x63ompleted_at\x18\x0b \x01(\x04H\x05\x88\x01\x01\x12\x14\n\x07groupid\x18\x0c \x01(\x04H\x06\x88\x01\x01\x12\x1d\n\x10payment_preimage\x18\r \x01(\x0cH\x07\x88\x01\x01\x12\x12\n\x05label\x18\x0e \x01(\tH\x08\x88\x01\x01\x12\x13\n\x06\x62olt11\x18\x0f \x01(\tH\t\x88\x01\x01\x12\x13\n\x06\x62olt12\x18\x10 \x01(\tH\n\x88\x01\x01\x12\x17\n\nerroronion\x18\x11 \x01(\x0cH\x0b\x88\x01\x01\"=\n\x14\x44\x65lpayPaymentsStatus\x12\x0b\n\x07PENDING\x10\x00\x12\n\n\x06\x46\x41ILED\x10\x01\x12\x0c\n\x08\x43OMPLETE\x10\x02\x42\x10\n\x0e_created_indexB\t\n\x07_partidB\x0e\n\x0c_destinationB\x0e\n\x0c_amount_msatB\x10\n\x0e_updated_indexB\x0f\n\r_completed_atB\n\n\x08_groupidB\x13\n\x11_payment_preimageB\x08\n\x06_labelB\t\n\x07_bolt11B\t\n\x07_bolt12B\r\n\x0b_erroronion\"\xb3\x01\n\x11\x44\x65lforwardRequest\x12\x12\n\nin_channel\x18\x01 \x01(\t\x12\x12\n\nin_htlc_id\x18\x02 \x01(\x04\x12\x37\n\x06status\x18\x03 \x01(\x0e\x32\'.cln.DelforwardRequest.DelforwardStatus\"=\n\x10\x44\x65lforwardStatus\x12\x0b\n\x07SETTLED\x10\x00\x12\x10\n\x0cLOCAL_FAILED\x10\x01\x12\n\n\x06\x46\x41ILED\x10\x02\"\x14\n\x12\x44\x65lforwardResponse\"\'\n\x13\x44isableofferRequest\x12\x10\n\x08offer_id\x18\x01 \x01(\x0c\"\x88\x01\n\x14\x44isableofferResponse\x12\x10\n\x08offer_id\x18\x01 \x01(\x0c\x12\x0e\n\x06\x61\x63tive\x18\x02 \x01(\x08\x12\x12\n\nsingle_use\x18\x03 \x01(\x08\x12\x0e\n\x06\x62olt12\x18\x04 \x01(\t\x12\x0c\n\x04used\x18\x05 \x01(\x08\x12\x12\n\x05label\x18\x06 \x01(\tH\x00\x88\x01\x01\x42\x08\n\x06_label\"&\n\x12\x45nableofferRequest\x12\x10\n\x08offer_id\x18\x01 \x01(\x0c\"\x87\x01\n\x13\x45nableofferResponse\x12\x10\n\x08offer_id\x18\x01 \x01(\x0c\x12\x0e\n\x06\x61\x63tive\x18\x02 \x01(\x08\x12\x12\n\nsingle_use\x18\x03 \x01(\x08\x12\x0e\n\x06\x62olt12\x18\x04 \x01(\t\x12\x0c\n\x04used\x18\x05 \x01(\x08\x12\x12\n\x05label\x18\x06 \x01(\tH\x00\x88\x01\x01\x42\x08\n\x06_label\"=\n\x11\x44isconnectRequest\x12\n\n\x02id\x18\x01 \x01(\x0c\x12\x12\n\x05\x66orce\x18\x02 \x01(\x08H\x00\x88\x01\x01\x42\x08\n\x06_force\"\x14\n\x12\x44isconnectResponse\"k\n\x0f\x46\x65\x65ratesRequest\x12\x31\n\x05style\x18\x01 \x01(\x0e\x32\".cln.FeeratesRequest.FeeratesStyle\"%\n\rFeeratesStyle\x12\t\n\x05PERKB\x10\x00\x12\t\n\x05PERKW\x10\x01\"\x9a\x02\n\x10\x46\x65\x65ratesResponse\x12%\n\x18warning_missing_feerates\x18\x01 \x01(\tH\x00\x88\x01\x01\x12&\n\x05perkb\x18\x02 \x01(\x0b\x32\x12.cln.FeeratesPerkbH\x01\x88\x01\x01\x12&\n\x05perkw\x18\x03 \x01(\x0b\x32\x12.cln.FeeratesPerkwH\x02\x88\x01\x01\x12\x44\n\x15onchain_fee_estimates\x18\x04 \x01(\x0b\x32 .cln.FeeratesOnchainFeeEstimatesH\x03\x88\x01\x01\x42\x1b\n\x19_warning_missing_feeratesB\x08\n\x06_perkbB\x08\n\x06_perkwB\x18\n\x16_onchain_fee_estimates\"\xd3\x03\n\rFeeratesPerkb\x12\x16\n\x0emin_acceptable\x18\x01 \x01(\r\x12\x16\n\x0emax_acceptable\x18\x02 \x01(\r\x12\x14\n\x07opening\x18\x03 \x01(\rH\x00\x88\x01\x01\x12\x19\n\x0cmutual_close\x18\x04 \x01(\rH\x01\x88\x01\x01\x12\x1d\n\x10unilateral_close\x18\x05 \x01(\rH\x02\x88\x01\x01\x12\x1a\n\rdelayed_to_us\x18\x06 \x01(\rH\x03\x88\x01\x01\x12\x1c\n\x0fhtlc_resolution\x18\x07 \x01(\rH\x04\x88\x01\x01\x12\x14\n\x07penalty\x18\x08 \x01(\rH\x05\x88\x01\x01\x12.\n\testimates\x18\t \x03(\x0b\x32\x1b.cln.FeeratesPerkbEstimates\x12\x12\n\x05\x66loor\x18\n \x01(\rH\x06\x88\x01\x01\x12$\n\x17unilateral_anchor_close\x18\x0b \x01(\rH\x07\x88\x01\x01\x42\n\n\x08_openingB\x0f\n\r_mutual_closeB\x13\n\x11_unilateral_closeB\x10\n\x0e_delayed_to_usB\x12\n\x10_htlc_resolutionB\n\n\x08_penaltyB\x08\n\x06_floorB\x1a\n\x18_unilateral_anchor_close\"W\n\x16\x46\x65\x65ratesPerkbEstimates\x12\x12\n\nblockcount\x18\x01 \x01(\r\x12\x0f\n\x07\x66\x65\x65rate\x18\x02 \x01(\r\x12\x18\n\x10smoothed_feerate\x18\x03 \x01(\r\"\xd3\x03\n\rFeeratesPerkw\x12\x16\n\x0emin_acceptable\x18\x01 \x01(\r\x12\x16\n\x0emax_acceptable\x18\x02 \x01(\r\x12\x14\n\x07opening\x18\x03 \x01(\rH\x00\x88\x01\x01\x12\x19\n\x0cmutual_close\x18\x04 \x01(\rH\x01\x88\x01\x01\x12\x1d\n\x10unilateral_close\x18\x05 \x01(\rH\x02\x88\x01\x01\x12\x1a\n\rdelayed_to_us\x18\x06 \x01(\rH\x03\x88\x01\x01\x12\x1c\n\x0fhtlc_resolution\x18\x07 \x01(\rH\x04\x88\x01\x01\x12\x14\n\x07penalty\x18\x08 \x01(\rH\x05\x88\x01\x01\x12.\n\testimates\x18\t \x03(\x0b\x32\x1b.cln.FeeratesPerkwEstimates\x12\x12\n\x05\x66loor\x18\n \x01(\rH\x06\x88\x01\x01\x12$\n\x17unilateral_anchor_close\x18\x0b \x01(\rH\x07\x88\x01\x01\x42\n\n\x08_openingB\x0f\n\r_mutual_closeB\x13\n\x11_unilateral_closeB\x10\n\x0e_delayed_to_usB\x12\n\x10_htlc_resolutionB\n\n\x08_penaltyB\x08\n\x06_floorB\x1a\n\x18_unilateral_anchor_close\"W\n\x16\x46\x65\x65ratesPerkwEstimates\x12\x12\n\nblockcount\x18\x01 \x01(\r\x12\x0f\n\x07\x66\x65\x65rate\x18\x02 \x01(\r\x12\x18\n\x10smoothed_feerate\x18\x03 \x01(\r\"\x99\x02\n\x1b\x46\x65\x65ratesOnchainFeeEstimates\x12 \n\x18opening_channel_satoshis\x18\x01 \x01(\x04\x12\x1d\n\x15mutual_close_satoshis\x18\x02 \x01(\x04\x12!\n\x19unilateral_close_satoshis\x18\x03 \x01(\x04\x12\x1d\n\x15htlc_timeout_satoshis\x18\x04 \x01(\x04\x12\x1d\n\x15htlc_success_satoshis\x18\x05 \x01(\x04\x12\x30\n#unilateral_close_nonanchor_satoshis\x18\x06 \x01(\x04H\x00\x88\x01\x01\x42&\n$_unilateral_close_nonanchor_satoshis\"%\n\x12\x46\x65tchbip353Request\x12\x0f\n\x07\x61\x64\x64ress\x18\x01 \x01(\t\"X\n\x13\x46\x65tchbip353Response\x12\r\n\x05proof\x18\x01 \x01(\t\x12\x32\n\x0cinstructions\x18\x02 \x03(\x0b\x32\x1c.cln.Fetchbip353Instructions\"\xf7\x01\n\x17\x46\x65tchbip353Instructions\x12\x18\n\x0b\x64\x65scription\x18\x01 \x01(\tH\x00\x88\x01\x01\x12\x12\n\x05offer\x18\x02 \x01(\tH\x01\x88\x01\x01\x12\x14\n\x07onchain\x18\x03 \x01(\tH\x02\x88\x01\x01\x12!\n\x14offchain_amount_msat\x18\x04 \x01(\x04H\x03\x88\x01\x01\x12\x1f\n\x12onchain_amount_sat\x18\x05 \x01(\x04H\x04\x88\x01\x01\x42\x0e\n\x0c_descriptionB\x08\n\x06_offerB\n\n\x08_onchainB\x17\n\x15_offchain_amount_msatB\x15\n\x13_onchain_amount_sat\"\xb9\x03\n\x13\x46\x65tchinvoiceRequest\x12\r\n\x05offer\x18\x01 \x01(\t\x12%\n\x0b\x61mount_msat\x18\x02 \x01(\x0b\x32\x0b.cln.AmountH\x00\x88\x01\x01\x12\x15\n\x08quantity\x18\x03 \x01(\x04H\x01\x88\x01\x01\x12\x1f\n\x12recurrence_counter\x18\x04 \x01(\x04H\x02\x88\x01\x01\x12\x1d\n\x10recurrence_start\x18\x05 \x01(\x01H\x03\x88\x01\x01\x12\x1d\n\x10recurrence_label\x18\x06 \x01(\tH\x04\x88\x01\x01\x12\x14\n\x07timeout\x18\x07 \x01(\x01H\x05\x88\x01\x01\x12\x17\n\npayer_note\x18\x08 \x01(\tH\x06\x88\x01\x01\x12\x1b\n\x0epayer_metadata\x18\t \x01(\tH\x07\x88\x01\x01\x12\x13\n\x06\x62ip353\x18\n \x01(\tH\x08\x88\x01\x01\x42\x0e\n\x0c_amount_msatB\x0b\n\t_quantityB\x15\n\x13_recurrence_counterB\x13\n\x11_recurrence_startB\x13\n\x11_recurrence_labelB\n\n\x08_timeoutB\r\n\x0b_payer_noteB\x11\n\x0f_payer_metadataB\t\n\x07_bip353\"\x99\x01\n\x14\x46\x65tchinvoiceResponse\x12\x0f\n\x07invoice\x18\x01 \x01(\t\x12)\n\x07\x63hanges\x18\x02 \x01(\x0b\x32\x18.cln.FetchinvoiceChanges\x12\x35\n\x0bnext_period\x18\x03 \x01(\x0b\x32\x1b.cln.FetchinvoiceNextPeriodH\x00\x88\x01\x01\x42\x0e\n\x0c_next_period\"\x82\x02\n\x13\x46\x65tchinvoiceChanges\x12!\n\x14\x64\x65scription_appended\x18\x01 \x01(\tH\x00\x88\x01\x01\x12\x18\n\x0b\x64\x65scription\x18\x02 \x01(\tH\x01\x88\x01\x01\x12\x1b\n\x0evendor_removed\x18\x03 \x01(\tH\x02\x88\x01\x01\x12\x13\n\x06vendor\x18\x04 \x01(\tH\x03\x88\x01\x01\x12%\n\x0b\x61mount_msat\x18\x05 \x01(\x0b\x32\x0b.cln.AmountH\x04\x88\x01\x01\x42\x17\n\x15_description_appendedB\x0e\n\x0c_descriptionB\x11\n\x0f_vendor_removedB\t\n\x07_vendorB\x0e\n\x0c_amount_msat\"}\n\x16\x46\x65tchinvoiceNextPeriod\x12\x0f\n\x07\x63ounter\x18\x01 \x01(\x04\x12\x11\n\tstarttime\x18\x02 \x01(\x04\x12\x0f\n\x07\x65ndtime\x18\x03 \x01(\x04\x12\x17\n\x0fpaywindow_start\x18\x04 \x01(\x04\x12\x15\n\rpaywindow_end\x18\x05 \x01(\x04\"&\n\x18\x46undchannelCancelRequest\x12\n\n\x02id\x18\x01 \x01(\x0c\".\n\x19\x46undchannelCancelResponse\x12\x11\n\tcancelled\x18\x01 \x01(\t\"6\n\x1a\x46undchannelCompleteRequest\x12\n\n\x02id\x18\x01 \x01(\x0c\x12\x0c\n\x04psbt\x18\x02 \x01(\t\"N\n\x1b\x46undchannelCompleteResponse\x12\x12\n\nchannel_id\x18\x01 \x01(\x0c\x12\x1b\n\x13\x63ommitments_secured\x18\x02 \x01(\x08\"\xfb\x03\n\x12\x46undchannelRequest\x12 \n\x06\x61mount\x18\x01 \x01(\x0b\x32\x10.cln.AmountOrAll\x12\"\n\x07\x66\x65\x65rate\x18\x02 \x01(\x0b\x32\x0c.cln.FeerateH\x00\x88\x01\x01\x12\x15\n\x08\x61nnounce\x18\x03 \x01(\x08H\x01\x88\x01\x01\x12#\n\tpush_msat\x18\x05 \x01(\x0b\x32\x0b.cln.AmountH\x02\x88\x01\x01\x12\x15\n\x08\x63lose_to\x18\x06 \x01(\tH\x03\x88\x01\x01\x12%\n\x0brequest_amt\x18\x07 \x01(\x0b\x32\x0b.cln.AmountH\x04\x88\x01\x01\x12\x1a\n\rcompact_lease\x18\x08 \x01(\tH\x05\x88\x01\x01\x12\n\n\x02id\x18\t \x01(\x0c\x12\x14\n\x07minconf\x18\n \x01(\rH\x06\x88\x01\x01\x12\x1c\n\x05utxos\x18\x0b \x03(\x0b\x32\r.cln.Outpoint\x12\x15\n\x08mindepth\x18\x0c \x01(\rH\x07\x88\x01\x01\x12!\n\x07reserve\x18\r \x01(\x0b\x32\x0b.cln.AmountH\x08\x88\x01\x01\x12\x14\n\x0c\x63hannel_type\x18\x0e \x03(\rB\n\n\x08_feerateB\x0b\n\t_announceB\x0c\n\n_push_msatB\x0b\n\t_close_toB\x0e\n\x0c_request_amtB\x10\n\x0e_compact_leaseB\n\n\x08_minconfB\x0b\n\t_mindepthB\n\n\x08_reserve\"\xe4\x01\n\x13\x46undchannelResponse\x12\n\n\x02tx\x18\x01 \x01(\x0c\x12\x0c\n\x04txid\x18\x02 \x01(\x0c\x12\x0e\n\x06outnum\x18\x03 \x01(\r\x12\x12\n\nchannel_id\x18\x04 \x01(\x0c\x12\x15\n\x08\x63lose_to\x18\x05 \x01(\x0cH\x00\x88\x01\x01\x12\x15\n\x08mindepth\x18\x06 \x01(\rH\x01\x88\x01\x01\x12\x36\n\x0c\x63hannel_type\x18\x07 \x01(\x0b\x32\x1b.cln.FundchannelChannelTypeH\x02\x88\x01\x01\x42\x0b\n\t_close_toB\x0b\n\t_mindepthB\x0f\n\r_channel_type\"K\n\x16\x46undchannelChannelType\x12\x0c\n\x04\x62its\x18\x01 \x03(\r\x12#\n\x05names\x18\x02 \x03(\x0e\x32\x14.cln.ChannelTypeName\"\xd6\x02\n\x17\x46undchannelStartRequest\x12\n\n\x02id\x18\x01 \x01(\x0c\x12\x1b\n\x06\x61mount\x18\x02 \x01(\x0b\x32\x0b.cln.Amount\x12\"\n\x07\x66\x65\x65rate\x18\x03 \x01(\x0b\x32\x0c.cln.FeerateH\x00\x88\x01\x01\x12\x15\n\x08\x61nnounce\x18\x04 \x01(\x08H\x01\x88\x01\x01\x12\x15\n\x08\x63lose_to\x18\x05 \x01(\tH\x02\x88\x01\x01\x12#\n\tpush_msat\x18\x06 \x01(\x0b\x32\x0b.cln.AmountH\x03\x88\x01\x01\x12\x15\n\x08mindepth\x18\x07 \x01(\rH\x04\x88\x01\x01\x12!\n\x07reserve\x18\x08 \x01(\x0b\x32\x0b.cln.AmountH\x05\x88\x01\x01\x12\x14\n\x0c\x63hannel_type\x18\t \x03(\rB\n\n\x08_feerateB\x0b\n\t_announceB\x0b\n\t_close_toB\x0c\n\n_push_msatB\x0b\n\t_mindepthB\n\n\x08_reserve\"\xf6\x01\n\x18\x46undchannelStartResponse\x12\x17\n\x0f\x66unding_address\x18\x01 \x01(\t\x12\x14\n\x0cscriptpubkey\x18\x02 \x01(\x0c\x12;\n\x0c\x63hannel_type\x18\x03 \x01(\x0b\x32 .cln.FundchannelStartChannelTypeH\x00\x88\x01\x01\x12\x15\n\x08\x63lose_to\x18\x04 \x01(\x0cH\x01\x88\x01\x01\x12\x15\n\rwarning_usage\x18\x05 \x01(\t\x12\x15\n\x08mindepth\x18\x06 \x01(\rH\x02\x88\x01\x01\x42\x0f\n\r_channel_typeB\x0b\n\t_close_toB\x0b\n\t_mindepth\"P\n\x1b\x46undchannelStartChannelType\x12\x0c\n\x04\x62its\x18\x01 \x03(\r\x12#\n\x05names\x18\x02 \x03(\x0e\x32\x14.cln.ChannelTypeName\"\x9d\x01\n\rGetlogRequest\x12\x32\n\x05level\x18\x01 \x01(\x0e\x32\x1e.cln.GetlogRequest.GetlogLevelH\x00\x88\x01\x01\"N\n\x0bGetlogLevel\x12\n\n\x06\x42ROKEN\x10\x00\x12\x0b\n\x07UNUSUAL\x10\x01\x12\x08\n\x04INFO\x10\x02\x12\t\n\x05\x44\x45\x42UG\x10\x03\x12\x06\n\x02IO\x10\x04\x12\t\n\x05TRACE\x10\x05\x42\x08\n\x06_level\"h\n\x0eGetlogResponse\x12\x12\n\ncreated_at\x18\x01 \x01(\t\x12\x12\n\nbytes_used\x18\x02 \x01(\r\x12\x11\n\tbytes_max\x18\x03 \x01(\r\x12\x1b\n\x03log\x18\x04 \x03(\x0b\x32\x0e.cln.GetlogLog\"\xe8\x02\n\tGetlogLog\x12/\n\titem_type\x18\x01 \x01(\x0e\x32\x1c.cln.GetlogLog.GetlogLogType\x12\x18\n\x0bnum_skipped\x18\x02 \x01(\rH\x00\x88\x01\x01\x12\x11\n\x04time\x18\x03 \x01(\tH\x01\x88\x01\x01\x12\x13\n\x06source\x18\x04 \x01(\tH\x02\x88\x01\x01\x12\x10\n\x03log\x18\x05 \x01(\tH\x03\x88\x01\x01\x12\x14\n\x07node_id\x18\x06 \x01(\x0cH\x04\x88\x01\x01\x12\x11\n\x04\x64\x61ta\x18\x07 \x01(\x0cH\x05\x88\x01\x01\"l\n\rGetlogLogType\x12\x0b\n\x07SKIPPED\x10\x00\x12\n\n\x06\x42ROKEN\x10\x01\x12\x0b\n\x07UNUSUAL\x10\x02\x12\x08\n\x04INFO\x10\x03\x12\t\n\x05\x44\x45\x42UG\x10\x04\x12\t\n\x05IO_IN\x10\x05\x12\n\n\x06IO_OUT\x10\x06\x12\t\n\x05TRACE\x10\x07\x42\x0e\n\x0c_num_skippedB\x07\n\x05_timeB\t\n\x07_sourceB\x06\n\x04_logB\n\n\x08_node_idB\x07\n\x05_data\"\xd9\x08\n\x13\x46underupdateRequest\x12@\n\x06policy\x18\x01 \x01(\x0e\x32+.cln.FunderupdateRequest.FunderupdatePolicyH\x00\x88\x01\x01\x12$\n\npolicy_mod\x18\x02 \x01(\x0b\x32\x0b.cln.AmountH\x01\x88\x01\x01\x12\x18\n\x0bleases_only\x18\x03 \x01(\x08H\x02\x88\x01\x01\x12\x30\n\x16min_their_funding_msat\x18\x04 \x01(\x0b\x32\x0b.cln.AmountH\x03\x88\x01\x01\x12\x30\n\x16max_their_funding_msat\x18\x05 \x01(\x0b\x32\x0b.cln.AmountH\x04\x88\x01\x01\x12.\n\x14per_channel_min_msat\x18\x06 \x01(\x0b\x32\x0b.cln.AmountH\x05\x88\x01\x01\x12.\n\x14per_channel_max_msat\x18\x07 \x01(\x0b\x32\x0b.cln.AmountH\x06\x88\x01\x01\x12+\n\x11reserve_tank_msat\x18\x08 \x01(\x0b\x32\x0b.cln.AmountH\x07\x88\x01\x01\x12\x19\n\x0c\x66uzz_percent\x18\t \x01(\rH\x08\x88\x01\x01\x12\x1d\n\x10\x66und_probability\x18\n \x01(\rH\t\x88\x01\x01\x12-\n\x13lease_fee_base_msat\x18\x0b \x01(\x0b\x32\x0b.cln.AmountH\n\x88\x01\x01\x12\x1c\n\x0flease_fee_basis\x18\x0c \x01(\rH\x0b\x88\x01\x01\x12\x1b\n\x0e\x66unding_weight\x18\r \x01(\rH\x0c\x88\x01\x01\x12\x33\n\x19\x63hannel_fee_max_base_msat\x18\x0e \x01(\x0b\x32\x0b.cln.AmountH\r\x88\x01\x01\x12\x35\n(channel_fee_max_proportional_thousandths\x18\x0f \x01(\rH\x0e\x88\x01\x01\x12\x1a\n\rcompact_lease\x18\x10 \x01(\x0cH\x0f\x88\x01\x01\"9\n\x12\x46underupdatePolicy\x12\t\n\x05MATCH\x10\x00\x12\r\n\tAVAILABLE\x10\x01\x12\t\n\x05\x46IXED\x10\x02\x42\t\n\x07_policyB\r\n\x0b_policy_modB\x0e\n\x0c_leases_onlyB\x19\n\x17_min_their_funding_msatB\x19\n\x17_max_their_funding_msatB\x17\n\x15_per_channel_min_msatB\x17\n\x15_per_channel_max_msatB\x14\n\x12_reserve_tank_msatB\x0f\n\r_fuzz_percentB\x13\n\x11_fund_probabilityB\x16\n\x14_lease_fee_base_msatB\x12\n\x10_lease_fee_basisB\x11\n\x0f_funding_weightB\x1c\n\x1a_channel_fee_max_base_msatB+\n)_channel_fee_max_proportional_thousandthsB\x10\n\x0e_compact_lease\"\xdf\x06\n\x14\x46underupdateResponse\x12\x0f\n\x07summary\x18\x01 \x01(\t\x12<\n\x06policy\x18\x02 \x01(\x0e\x32,.cln.FunderupdateResponse.FunderupdatePolicy\x12\x12\n\npolicy_mod\x18\x03 \x01(\r\x12\x13\n\x0bleases_only\x18\x04 \x01(\x08\x12+\n\x16min_their_funding_msat\x18\x05 \x01(\x0b\x32\x0b.cln.Amount\x12+\n\x16max_their_funding_msat\x18\x06 \x01(\x0b\x32\x0b.cln.Amount\x12)\n\x14per_channel_min_msat\x18\x07 \x01(\x0b\x32\x0b.cln.Amount\x12)\n\x14per_channel_max_msat\x18\x08 \x01(\x0b\x32\x0b.cln.Amount\x12&\n\x11reserve_tank_msat\x18\t \x01(\x0b\x32\x0b.cln.Amount\x12\x14\n\x0c\x66uzz_percent\x18\n \x01(\r\x12\x18\n\x10\x66und_probability\x18\x0b \x01(\r\x12-\n\x13lease_fee_base_msat\x18\x0c \x01(\x0b\x32\x0b.cln.AmountH\x00\x88\x01\x01\x12\x1c\n\x0flease_fee_basis\x18\r \x01(\rH\x01\x88\x01\x01\x12\x1b\n\x0e\x66unding_weight\x18\x0e \x01(\rH\x02\x88\x01\x01\x12\x33\n\x19\x63hannel_fee_max_base_msat\x18\x0f \x01(\x0b\x32\x0b.cln.AmountH\x03\x88\x01\x01\x12\x35\n(channel_fee_max_proportional_thousandths\x18\x10 \x01(\rH\x04\x88\x01\x01\x12\x1a\n\rcompact_lease\x18\x11 \x01(\x0cH\x05\x88\x01\x01\"9\n\x12\x46underupdatePolicy\x12\t\n\x05MATCH\x10\x00\x12\r\n\tAVAILABLE\x10\x01\x12\t\n\x05\x46IXED\x10\x02\x42\x16\n\x14_lease_fee_base_msatB\x12\n\x10_lease_fee_basisB\x11\n\x0f_funding_weightB\x1c\n\x1a_channel_fee_max_base_msatB+\n)_channel_fee_max_proportional_thousandthsB\x10\n\x0e_compact_lease\"\xec\x01\n\x0fGetrouteRequest\x12\n\n\x02id\x18\x01 \x01(\x0c\x12\x12\n\nriskfactor\x18\x03 \x01(\x04\x12\x11\n\x04\x63ltv\x18\x04 \x01(\rH\x00\x88\x01\x01\x12\x13\n\x06\x66romid\x18\x05 \x01(\x0cH\x01\x88\x01\x01\x12\x18\n\x0b\x66uzzpercent\x18\x06 \x01(\rH\x02\x88\x01\x01\x12\x0f\n\x07\x65xclude\x18\x07 \x03(\t\x12\x14\n\x07maxhops\x18\x08 \x01(\rH\x03\x88\x01\x01\x12 \n\x0b\x61mount_msat\x18\t \x01(\x0b\x32\x0b.cln.AmountB\x07\n\x05_cltvB\t\n\x07_fromidB\x0e\n\x0c_fuzzpercentB\n\n\x08_maxhops\"5\n\x10GetrouteResponse\x12!\n\x05route\x18\x01 \x03(\x0b\x32\x12.cln.GetrouteRoute\"\xc5\x01\n\rGetrouteRoute\x12\n\n\x02id\x18\x01 \x01(\x0c\x12\x0f\n\x07\x63hannel\x18\x02 \x01(\t\x12\x11\n\tdirection\x18\x03 \x01(\r\x12 \n\x0b\x61mount_msat\x18\x04 \x01(\x0b\x32\x0b.cln.Amount\x12\r\n\x05\x64\x65lay\x18\x05 \x01(\r\x12\x34\n\x05style\x18\x06 \x01(\x0e\x32%.cln.GetrouteRoute.GetrouteRouteStyle\"\x1d\n\x12GetrouteRouteStyle\x12\x07\n\x03TLV\x10\x00\"t\n\x14ListaddressesRequest\x12\x14\n\x07\x61\x64\x64ress\x18\x01 \x01(\tH\x00\x88\x01\x01\x12\x12\n\x05start\x18\x02 \x01(\x04H\x01\x88\x01\x01\x12\x12\n\x05limit\x18\x03 \x01(\rH\x02\x88\x01\x01\x42\n\n\x08_addressB\x08\n\x06_startB\x08\n\x06_limit\"G\n\x15ListaddressesResponse\x12.\n\taddresses\x18\x01 \x03(\x0b\x32\x1b.cln.ListaddressesAddresses\"d\n\x16ListaddressesAddresses\x12\x0e\n\x06keyidx\x18\x01 \x01(\x04\x12\x13\n\x06\x62\x65\x63h32\x18\x02 \x01(\tH\x00\x88\x01\x01\x12\x11\n\x04p2tr\x18\x03 \x01(\tH\x01\x88\x01\x01\x42\t\n\x07_bech32B\x07\n\x05_p2tr\"\xb7\x03\n\x13ListforwardsRequest\x12@\n\x06status\x18\x01 \x01(\x0e\x32+.cln.ListforwardsRequest.ListforwardsStatusH\x00\x88\x01\x01\x12\x17\n\nin_channel\x18\x02 \x01(\tH\x01\x88\x01\x01\x12\x18\n\x0bout_channel\x18\x03 \x01(\tH\x02\x88\x01\x01\x12>\n\x05index\x18\x04 \x01(\x0e\x32*.cln.ListforwardsRequest.ListforwardsIndexH\x03\x88\x01\x01\x12\x12\n\x05start\x18\x05 \x01(\x04H\x04\x88\x01\x01\x12\x12\n\x05limit\x18\x06 \x01(\rH\x05\x88\x01\x01\"L\n\x12ListforwardsStatus\x12\x0b\n\x07OFFERED\x10\x00\x12\x0b\n\x07SETTLED\x10\x01\x12\x10\n\x0cLOCAL_FAILED\x10\x02\x12\n\n\x06\x46\x41ILED\x10\x03\"-\n\x11ListforwardsIndex\x12\x0b\n\x07\x43REATED\x10\x00\x12\x0b\n\x07UPDATED\x10\x01\x42\t\n\x07_statusB\r\n\x0b_in_channelB\x0e\n\x0c_out_channelB\x08\n\x06_indexB\x08\n\x06_startB\x08\n\x06_limit\"C\n\x14ListforwardsResponse\x12+\n\x08\x66orwards\x18\x01 \x03(\x0b\x32\x19.cln.ListforwardsForwards\"\xb4\x06\n\x14ListforwardsForwards\x12\x12\n\nin_channel\x18\x01 \x01(\t\x12\x1c\n\x07in_msat\x18\x02 \x01(\x0b\x32\x0b.cln.Amount\x12\x44\n\x06status\x18\x03 \x01(\x0e\x32\x34.cln.ListforwardsForwards.ListforwardsForwardsStatus\x12\x15\n\rreceived_time\x18\x04 \x01(\x01\x12\x18\n\x0bout_channel\x18\x05 \x01(\tH\x00\x88\x01\x01\x12\"\n\x08\x66\x65\x65_msat\x18\x07 \x01(\x0b\x32\x0b.cln.AmountH\x01\x88\x01\x01\x12\"\n\x08out_msat\x18\x08 \x01(\x0b\x32\x0b.cln.AmountH\x02\x88\x01\x01\x12G\n\x05style\x18\t \x01(\x0e\x32\x33.cln.ListforwardsForwards.ListforwardsForwardsStyleH\x03\x88\x01\x01\x12\x17\n\nin_htlc_id\x18\n \x01(\x04H\x04\x88\x01\x01\x12\x18\n\x0bout_htlc_id\x18\x0b \x01(\x04H\x05\x88\x01\x01\x12\x1a\n\rcreated_index\x18\x0c \x01(\x04H\x06\x88\x01\x01\x12\x1a\n\rupdated_index\x18\r \x01(\x04H\x07\x88\x01\x01\x12\x1a\n\rresolved_time\x18\x0e \x01(\x01H\x08\x88\x01\x01\x12\x15\n\x08\x66\x61ilcode\x18\x0f \x01(\rH\t\x88\x01\x01\x12\x17\n\nfailreason\x18\x10 \x01(\tH\n\x88\x01\x01\"T\n\x1aListforwardsForwardsStatus\x12\x0b\n\x07OFFERED\x10\x00\x12\x0b\n\x07SETTLED\x10\x01\x12\x10\n\x0cLOCAL_FAILED\x10\x02\x12\n\n\x06\x46\x41ILED\x10\x03\"0\n\x19ListforwardsForwardsStyle\x12\n\n\x06LEGACY\x10\x00\x12\x07\n\x03TLV\x10\x01\x42\x0e\n\x0c_out_channelB\x0b\n\t_fee_msatB\x0b\n\t_out_msatB\x08\n\x06_styleB\r\n\x0b_in_htlc_idB\x0e\n\x0c_out_htlc_idB\x10\n\x0e_created_indexB\x10\n\x0e_updated_indexB\x10\n\x0e_resolved_timeB\x0b\n\t_failcodeB\r\n\x0b_failreason\"a\n\x11ListoffersRequest\x12\x15\n\x08offer_id\x18\x01 \x01(\x0cH\x00\x88\x01\x01\x12\x18\n\x0b\x61\x63tive_only\x18\x02 \x01(\x08H\x01\x88\x01\x01\x42\x0b\n\t_offer_idB\x0e\n\x0c_active_only\";\n\x12ListoffersResponse\x12%\n\x06offers\x18\x01 \x03(\x0b\x32\x15.cln.ListoffersOffers\"\x84\x01\n\x10ListoffersOffers\x12\x10\n\x08offer_id\x18\x01 \x01(\x0c\x12\x0e\n\x06\x61\x63tive\x18\x02 \x01(\x08\x12\x12\n\nsingle_use\x18\x03 \x01(\x08\x12\x0e\n\x06\x62olt12\x18\x04 \x01(\t\x12\x0c\n\x04used\x18\x05 \x01(\x08\x12\x12\n\x05label\x18\x06 \x01(\tH\x00\x88\x01\x01\x42\x08\n\x06_label\"\x84\x03\n\x0fListpaysRequest\x12\x13\n\x06\x62olt11\x18\x01 \x01(\tH\x00\x88\x01\x01\x12\x19\n\x0cpayment_hash\x18\x02 \x01(\x0cH\x01\x88\x01\x01\x12\x38\n\x06status\x18\x03 \x01(\x0e\x32#.cln.ListpaysRequest.ListpaysStatusH\x02\x88\x01\x01\x12\x36\n\x05index\x18\x04 \x01(\x0e\x32\".cln.ListpaysRequest.ListpaysIndexH\x03\x88\x01\x01\x12\x12\n\x05start\x18\x05 \x01(\x04H\x04\x88\x01\x01\x12\x12\n\x05limit\x18\x06 \x01(\rH\x05\x88\x01\x01\"7\n\x0eListpaysStatus\x12\x0b\n\x07PENDING\x10\x00\x12\x0c\n\x08\x43OMPLETE\x10\x01\x12\n\n\x06\x46\x41ILED\x10\x02\")\n\rListpaysIndex\x12\x0b\n\x07\x43REATED\x10\x00\x12\x0b\n\x07UPDATED\x10\x01\x42\t\n\x07_bolt11B\x0f\n\r_payment_hashB\t\n\x07_statusB\x08\n\x06_indexB\x08\n\x06_startB\x08\n\x06_limit\"3\n\x10ListpaysResponse\x12\x1f\n\x04pays\x18\x01 \x03(\x0b\x32\x11.cln.ListpaysPays\"\xdb\x05\n\x0cListpaysPays\x12\x14\n\x0cpayment_hash\x18\x01 \x01(\x0c\x12\x34\n\x06status\x18\x02 \x01(\x0e\x32$.cln.ListpaysPays.ListpaysPaysStatus\x12\x18\n\x0b\x64\x65stination\x18\x03 \x01(\x0cH\x00\x88\x01\x01\x12\x12\n\ncreated_at\x18\x04 \x01(\x04\x12\x12\n\x05label\x18\x05 \x01(\tH\x01\x88\x01\x01\x12\x13\n\x06\x62olt11\x18\x06 \x01(\tH\x02\x88\x01\x01\x12\x13\n\x06\x62olt12\x18\x07 \x01(\tH\x03\x88\x01\x01\x12%\n\x0b\x61mount_msat\x18\x08 \x01(\x0b\x32\x0b.cln.AmountH\x04\x88\x01\x01\x12*\n\x10\x61mount_sent_msat\x18\t \x01(\x0b\x32\x0b.cln.AmountH\x05\x88\x01\x01\x12\x17\n\nerroronion\x18\n \x01(\x0cH\x06\x88\x01\x01\x12\x18\n\x0b\x64\x65scription\x18\x0b \x01(\tH\x07\x88\x01\x01\x12\x19\n\x0c\x63ompleted_at\x18\x0c \x01(\x04H\x08\x88\x01\x01\x12\x15\n\x08preimage\x18\r \x01(\x0cH\t\x88\x01\x01\x12\x1c\n\x0fnumber_of_parts\x18\x0e \x01(\x04H\n\x88\x01\x01\x12\x1a\n\rcreated_index\x18\x0f \x01(\x04H\x0b\x88\x01\x01\x12\x1a\n\rupdated_index\x18\x10 \x01(\x04H\x0c\x88\x01\x01\";\n\x12ListpaysPaysStatus\x12\x0b\n\x07PENDING\x10\x00\x12\n\n\x06\x46\x41ILED\x10\x01\x12\x0c\n\x08\x43OMPLETE\x10\x02\x42\x0e\n\x0c_destinationB\x08\n\x06_labelB\t\n\x07_bolt11B\t\n\x07_bolt12B\x0e\n\x0c_amount_msatB\x13\n\x11_amount_sent_msatB\r\n\x0b_erroronionB\x0e\n\x0c_descriptionB\x0f\n\r_completed_atB\x0b\n\t_preimageB\x12\n\x10_number_of_partsB\x10\n\x0e_created_indexB\x10\n\x0e_updated_index\"\xd6\x01\n\x10ListhtlcsRequest\x12\x0f\n\x02id\x18\x01 \x01(\tH\x00\x88\x01\x01\x12\x38\n\x05index\x18\x02 \x01(\x0e\x32$.cln.ListhtlcsRequest.ListhtlcsIndexH\x01\x88\x01\x01\x12\x12\n\x05start\x18\x03 \x01(\x04H\x02\x88\x01\x01\x12\x12\n\x05limit\x18\x04 \x01(\rH\x03\x88\x01\x01\"*\n\x0eListhtlcsIndex\x12\x0b\n\x07\x43REATED\x10\x00\x12\x0b\n\x07UPDATED\x10\x01\x42\x05\n\x03_idB\x08\n\x06_indexB\x08\n\x06_startB\x08\n\x06_limit\"7\n\x11ListhtlcsResponse\x12\"\n\x05htlcs\x18\x01 \x03(\x0b\x32\x13.cln.ListhtlcsHtlcs\"\xe5\x02\n\x0eListhtlcsHtlcs\x12\x18\n\x10short_channel_id\x18\x01 \x01(\t\x12\n\n\x02id\x18\x02 \x01(\x04\x12\x0e\n\x06\x65xpiry\x18\x03 \x01(\r\x12 \n\x0b\x61mount_msat\x18\x04 \x01(\x0b\x32\x0b.cln.Amount\x12>\n\tdirection\x18\x05 \x01(\x0e\x32+.cln.ListhtlcsHtlcs.ListhtlcsHtlcsDirection\x12\x14\n\x0cpayment_hash\x18\x06 \x01(\x0c\x12\x1d\n\x05state\x18\x07 \x01(\x0e\x32\x0e.cln.HtlcState\x12\x1a\n\rcreated_index\x18\x08 \x01(\x04H\x00\x88\x01\x01\x12\x1a\n\rupdated_index\x18\t \x01(\x04H\x01\x88\x01\x01\"*\n\x17ListhtlcsHtlcsDirection\x12\x07\n\x03OUT\x10\x00\x12\x06\n\x02IN\x10\x01\x42\x10\n\x0e_created_indexB\x10\n\x0e_updated_index\"\xb2\x02\n\x17MultifundchannelRequest\x12\x37\n\x0c\x64\x65stinations\x18\x01 \x03(\x0b\x32!.cln.MultifundchannelDestinations\x12\"\n\x07\x66\x65\x65rate\x18\x02 \x01(\x0b\x32\x0c.cln.FeerateH\x00\x88\x01\x01\x12\x14\n\x07minconf\x18\x03 \x01(\x12H\x01\x88\x01\x01\x12\x1c\n\x05utxos\x18\x04 \x03(\x0b\x32\r.cln.Outpoint\x12\x18\n\x0bminchannels\x18\x05 \x01(\x12H\x02\x88\x01\x01\x12-\n\x12\x63ommitment_feerate\x18\x06 \x01(\x0b\x32\x0c.cln.FeerateH\x03\x88\x01\x01\x42\n\n\x08_feerateB\n\n\x08_minconfB\x0e\n\x0c_minchannelsB\x15\n\x13_commitment_feerate\"\x97\x01\n\x18MultifundchannelResponse\x12\n\n\x02tx\x18\x01 \x01(\x0c\x12\x0c\n\x04txid\x18\x02 \x01(\x0c\x12\x34\n\x0b\x63hannel_ids\x18\x03 \x03(\x0b\x32\x1f.cln.MultifundchannelChannelIds\x12+\n\x06\x66\x61iled\x18\x04 \x03(\x0b\x32\x1b.cln.MultifundchannelFailed\"\xff\x02\n\x1cMultifundchannelDestinations\x12\n\n\x02id\x18\x01 \x01(\t\x12 \n\x06\x61mount\x18\x02 \x01(\x0b\x32\x10.cln.AmountOrAll\x12\x15\n\x08\x61nnounce\x18\x03 \x01(\x08H\x00\x88\x01\x01\x12#\n\tpush_msat\x18\x04 \x01(\x0b\x32\x0b.cln.AmountH\x01\x88\x01\x01\x12\x15\n\x08\x63lose_to\x18\x05 \x01(\tH\x02\x88\x01\x01\x12%\n\x0brequest_amt\x18\x06 \x01(\x0b\x32\x0b.cln.AmountH\x03\x88\x01\x01\x12\x1a\n\rcompact_lease\x18\x07 \x01(\tH\x04\x88\x01\x01\x12\x15\n\x08mindepth\x18\x08 \x01(\rH\x05\x88\x01\x01\x12!\n\x07reserve\x18\t \x01(\x0b\x32\x0b.cln.AmountH\x06\x88\x01\x01\x42\x0b\n\t_announceB\x0c\n\n_push_msatB\x0b\n\t_close_toB\x0e\n\x0c_request_amtB\x10\n\x0e_compact_leaseB\x0b\n\t_mindepthB\n\n\x08_reserve\"\xc8\x01\n\x1aMultifundchannelChannelIds\x12\n\n\x02id\x18\x01 \x01(\x0c\x12\x0e\n\x06outnum\x18\x02 \x01(\r\x12\x12\n\nchannel_id\x18\x03 \x01(\x0c\x12\x45\n\x0c\x63hannel_type\x18\x04 \x01(\x0b\x32*.cln.MultifundchannelChannelIdsChannelTypeH\x00\x88\x01\x01\x12\x15\n\x08\x63lose_to\x18\x05 \x01(\x0cH\x01\x88\x01\x01\x42\x0f\n\r_channel_typeB\x0b\n\t_close_to\"Z\n%MultifundchannelChannelIdsChannelType\x12\x0c\n\x04\x62its\x18\x01 \x03(\r\x12#\n\x05names\x18\x02 \x03(\x0e\x32\x14.cln.ChannelTypeName\"\x93\x02\n\x16MultifundchannelFailed\x12\n\n\x02id\x18\x01 \x01(\x0c\x12H\n\x06method\x18\x02 \x01(\x0e\x32\x38.cln.MultifundchannelFailed.MultifundchannelFailedMethod\x12/\n\x05\x65rror\x18\x03 \x01(\x0b\x32 .cln.MultifundchannelFailedError\"r\n\x1cMultifundchannelFailedMethod\x12\x0b\n\x07\x43ONNECT\x10\x00\x12\x14\n\x10OPENCHANNEL_INIT\x10\x01\x12\x15\n\x11\x46UNDCHANNEL_START\x10\x02\x12\x18\n\x14\x46UNDCHANNEL_COMPLETE\x10\x03\"<\n\x1bMultifundchannelFailedError\x12\x0c\n\x04\x63ode\x18\x01 \x01(\x12\x12\x0f\n\x07message\x18\x02 \x01(\t\"\xa8\x01\n\x14MultiwithdrawRequest\x12 \n\x07outputs\x18\x01 \x03(\x0b\x32\x0f.cln.OutputDesc\x12\"\n\x07\x66\x65\x65rate\x18\x02 \x01(\x0b\x32\x0c.cln.FeerateH\x00\x88\x01\x01\x12\x14\n\x07minconf\x18\x03 \x01(\rH\x01\x88\x01\x01\x12\x1c\n\x05utxos\x18\x04 \x03(\x0b\x32\r.cln.OutpointB\n\n\x08_feerateB\n\n\x08_minconf\"1\n\x15MultiwithdrawResponse\x12\n\n\x02tx\x18\x01 \x01(\x0c\x12\x0c\n\x04txid\x18\x02 \x01(\x0c\"\xa0\x04\n\x0cOfferRequest\x12\x0e\n\x06\x61mount\x18\x01 \x01(\t\x12\x18\n\x0b\x64\x65scription\x18\x02 \x01(\tH\x00\x88\x01\x01\x12\x13\n\x06issuer\x18\x03 \x01(\tH\x01\x88\x01\x01\x12\x12\n\x05label\x18\x04 \x01(\tH\x02\x88\x01\x01\x12\x19\n\x0cquantity_max\x18\x05 \x01(\x04H\x03\x88\x01\x01\x12\x1c\n\x0f\x61\x62solute_expiry\x18\x06 \x01(\x04H\x04\x88\x01\x01\x12\x17\n\nrecurrence\x18\x07 \x01(\tH\x05\x88\x01\x01\x12\x1c\n\x0frecurrence_base\x18\x08 \x01(\tH\x06\x88\x01\x01\x12!\n\x14recurrence_paywindow\x18\t \x01(\tH\x07\x88\x01\x01\x12\x1d\n\x10recurrence_limit\x18\n \x01(\rH\x08\x88\x01\x01\x12\x17\n\nsingle_use\x18\x0b \x01(\x08H\t\x88\x01\x01\x12(\n\x1brecurrence_start_any_period\x18\x0c \x01(\x08H\n\x88\x01\x01\x42\x0e\n\x0c_descriptionB\t\n\x07_issuerB\x08\n\x06_labelB\x0f\n\r_quantity_maxB\x12\n\x10_absolute_expiryB\r\n\x0b_recurrenceB\x12\n\x10_recurrence_baseB\x17\n\x15_recurrence_paywindowB\x13\n\x11_recurrence_limitB\r\n\x0b_single_useB\x1e\n\x1c_recurrence_start_any_period\"\x92\x01\n\rOfferResponse\x12\x10\n\x08offer_id\x18\x01 \x01(\x0c\x12\x0e\n\x06\x61\x63tive\x18\x02 \x01(\x08\x12\x12\n\nsingle_use\x18\x03 \x01(\x08\x12\x0e\n\x06\x62olt12\x18\x04 \x01(\t\x12\x0c\n\x04used\x18\x05 \x01(\x08\x12\x0f\n\x07\x63reated\x18\x06 \x01(\x08\x12\x12\n\x05label\x18\x07 \x01(\tH\x00\x88\x01\x01\x42\x08\n\x06_label\"-\n\x17OpenchannelAbortRequest\x12\x12\n\nchannel_id\x18\x01 \x01(\x0c\"X\n\x18OpenchannelAbortResponse\x12\x12\n\nchannel_id\x18\x01 \x01(\x0c\x12\x18\n\x10\x63hannel_canceled\x18\x02 \x01(\x08\x12\x0e\n\x06reason\x18\x03 \x01(\t\"\x9e\x01\n\x16OpenchannelBumpRequest\x12\x12\n\nchannel_id\x18\x01 \x01(\x0c\x12\x13\n\x0binitialpsbt\x18\x02 \x01(\t\x12*\n\x0f\x66unding_feerate\x18\x03 \x01(\x0b\x32\x0c.cln.FeerateH\x00\x88\x01\x01\x12\x1b\n\x06\x61mount\x18\x04 \x01(\x0b\x32\x0b.cln.AmountB\x12\n\x10_funding_feerate\"\x83\x02\n\x17OpenchannelBumpResponse\x12\x12\n\nchannel_id\x18\x01 \x01(\x0c\x12:\n\x0c\x63hannel_type\x18\x02 \x01(\x0b\x32\x1f.cln.OpenchannelBumpChannelTypeH\x00\x88\x01\x01\x12\x0c\n\x04psbt\x18\x03 \x01(\t\x12\x1b\n\x13\x63ommitments_secured\x18\x04 \x01(\x08\x12\x16\n\x0e\x66unding_serial\x18\x05 \x01(\x04\x12&\n\x19requires_confirmed_inputs\x18\x06 \x01(\x08H\x01\x88\x01\x01\x42\x0f\n\r_channel_typeB\x1c\n\x1a_requires_confirmed_inputs\"O\n\x1aOpenchannelBumpChannelType\x12\x0c\n\x04\x62its\x18\x01 \x03(\r\x12#\n\x05names\x18\x02 \x03(\x0e\x32\x14.cln.ChannelTypeName\"\x9f\x03\n\x16OpenchannelInitRequest\x12\n\n\x02id\x18\x01 \x01(\x0c\x12\x13\n\x0binitialpsbt\x18\x02 \x01(\t\x12-\n\x12\x63ommitment_feerate\x18\x03 \x01(\x0b\x32\x0c.cln.FeerateH\x00\x88\x01\x01\x12*\n\x0f\x66unding_feerate\x18\x04 \x01(\x0b\x32\x0c.cln.FeerateH\x01\x88\x01\x01\x12\x15\n\x08\x61nnounce\x18\x05 \x01(\x08H\x02\x88\x01\x01\x12\x15\n\x08\x63lose_to\x18\x06 \x01(\tH\x03\x88\x01\x01\x12%\n\x0brequest_amt\x18\x07 \x01(\x0b\x32\x0b.cln.AmountH\x04\x88\x01\x01\x12\x1a\n\rcompact_lease\x18\x08 \x01(\x0cH\x05\x88\x01\x01\x12\x14\n\x0c\x63hannel_type\x18\t \x03(\r\x12\x1b\n\x06\x61mount\x18\n \x01(\x0b\x32\x0b.cln.AmountB\x15\n\x13_commitment_feerateB\x12\n\x10_funding_feerateB\x0b\n\t_announceB\x0b\n\t_close_toB\x0e\n\x0c_request_amtB\x10\n\x0e_compact_lease\"\x83\x02\n\x17OpenchannelInitResponse\x12\x12\n\nchannel_id\x18\x01 \x01(\x0c\x12\x0c\n\x04psbt\x18\x02 \x01(\t\x12:\n\x0c\x63hannel_type\x18\x03 \x01(\x0b\x32\x1f.cln.OpenchannelInitChannelTypeH\x00\x88\x01\x01\x12\x1b\n\x13\x63ommitments_secured\x18\x04 \x01(\x08\x12\x16\n\x0e\x66unding_serial\x18\x05 \x01(\x04\x12&\n\x19requires_confirmed_inputs\x18\x06 \x01(\x08H\x01\x88\x01\x01\x42\x0f\n\r_channel_typeB\x1c\n\x1a_requires_confirmed_inputs\"O\n\x1aOpenchannelInitChannelType\x12\x0c\n\x04\x62its\x18\x01 \x03(\r\x12#\n\x05names\x18\x02 \x03(\x0e\x32\x14.cln.ChannelTypeName\"C\n\x18OpenchannelSignedRequest\x12\x12\n\nchannel_id\x18\x01 \x01(\x0c\x12\x13\n\x0bsigned_psbt\x18\x02 \x01(\t\"I\n\x19OpenchannelSignedResponse\x12\x12\n\nchannel_id\x18\x01 \x01(\x0c\x12\n\n\x02tx\x18\x02 \x01(\x0c\x12\x0c\n\x04txid\x18\x03 \x01(\x0c\"<\n\x18OpenchannelUpdateRequest\x12\x12\n\nchannel_id\x18\x01 \x01(\x0c\x12\x0c\n\x04psbt\x18\x02 \x01(\t\"\xab\x02\n\x19OpenchannelUpdateResponse\x12\x12\n\nchannel_id\x18\x01 \x01(\x0c\x12<\n\x0c\x63hannel_type\x18\x02 \x01(\x0b\x32!.cln.OpenchannelUpdateChannelTypeH\x00\x88\x01\x01\x12\x0c\n\x04psbt\x18\x03 \x01(\t\x12\x1b\n\x13\x63ommitments_secured\x18\x04 \x01(\x08\x12\x16\n\x0e\x66unding_outnum\x18\x05 \x01(\r\x12\x15\n\x08\x63lose_to\x18\x06 \x01(\x0cH\x01\x88\x01\x01\x12&\n\x19requires_confirmed_inputs\x18\x07 \x01(\x08H\x02\x88\x01\x01\x42\x0f\n\r_channel_typeB\x0b\n\t_close_toB\x1c\n\x1a_requires_confirmed_inputs\"Q\n\x1cOpenchannelUpdateChannelType\x12\x0c\n\x04\x62its\x18\x01 \x03(\r\x12#\n\x05names\x18\x02 \x03(\x0e\x32\x14.cln.ChannelTypeName\"Y\n\x0bPingRequest\x12\n\n\x02id\x18\x01 \x01(\x0c\x12\x10\n\x03len\x18\x02 \x01(\rH\x00\x88\x01\x01\x12\x16\n\tpongbytes\x18\x03 \x01(\rH\x01\x88\x01\x01\x42\x06\n\x04_lenB\x0c\n\n_pongbytes\"\x1e\n\x0cPingResponse\x12\x0e\n\x06totlen\x18\x01 \x01(\r\"\x91\x01\n\rPluginRequest\x12)\n\nsubcommand\x18\x01 \x01(\x0e\x32\x15.cln.PluginSubcommand\x12\x13\n\x06plugin\x18\x02 \x01(\tH\x00\x88\x01\x01\x12\x16\n\tdirectory\x18\x03 \x01(\tH\x01\x88\x01\x01\x12\x0f\n\x07options\x18\x04 \x03(\tB\t\n\x07_pluginB\x0c\n\n_directory\"}\n\x0ePluginResponse\x12&\n\x07\x63ommand\x18\x01 \x01(\x0e\x32\x15.cln.PluginSubcommand\x12#\n\x07plugins\x18\x02 \x03(\x0b\x32\x12.cln.PluginPlugins\x12\x13\n\x06result\x18\x03 \x01(\tH\x00\x88\x01\x01\x42\t\n\x07_result\">\n\rPluginPlugins\x12\x0c\n\x04name\x18\x01 \x01(\t\x12\x0e\n\x06\x61\x63tive\x18\x02 \x01(\x08\x12\x0f\n\x07\x64ynamic\x18\x03 \x01(\x08\"<\n\x14RenepaystatusRequest\x12\x16\n\tinvstring\x18\x01 \x01(\tH\x00\x88\x01\x01\x42\x0c\n\n_invstring\"G\n\x15RenepaystatusResponse\x12.\n\tpaystatus\x18\x01 \x03(\x0b\x32\x1b.cln.RenepaystatusPaystatus\"\xe2\x03\n\x16RenepaystatusPaystatus\x12\x0e\n\x06\x62olt11\x18\x01 \x01(\t\x12\x1d\n\x10payment_preimage\x18\x02 \x01(\x0cH\x00\x88\x01\x01\x12\x14\n\x0cpayment_hash\x18\x03 \x01(\x0c\x12\x12\n\ncreated_at\x18\x04 \x01(\x01\x12\x0f\n\x07groupid\x18\x05 \x01(\r\x12\x12\n\x05parts\x18\x06 \x01(\rH\x01\x88\x01\x01\x12 \n\x0b\x61mount_msat\x18\x07 \x01(\x0b\x32\x0b.cln.Amount\x12*\n\x10\x61mount_sent_msat\x18\x08 \x01(\x0b\x32\x0b.cln.AmountH\x02\x88\x01\x01\x12H\n\x06status\x18\t \x01(\x0e\x32\x38.cln.RenepaystatusPaystatus.RenepaystatusPaystatusStatus\x12\x18\n\x0b\x64\x65stination\x18\n \x01(\x0cH\x03\x88\x01\x01\x12\r\n\x05notes\x18\x0b \x03(\t\"E\n\x1cRenepaystatusPaystatusStatus\x12\x0c\n\x08\x43OMPLETE\x10\x00\x12\x0b\n\x07PENDING\x10\x01\x12\n\n\x06\x46\x41ILED\x10\x02\x42\x13\n\x11_payment_preimageB\x08\n\x06_partsB\x13\n\x11_amount_sent_msatB\x0e\n\x0c_destination\"\xda\x02\n\x0eRenepayRequest\x12\x11\n\tinvstring\x18\x01 \x01(\t\x12%\n\x0b\x61mount_msat\x18\x02 \x01(\x0b\x32\x0b.cln.AmountH\x00\x88\x01\x01\x12 \n\x06maxfee\x18\x03 \x01(\x0b\x32\x0b.cln.AmountH\x01\x88\x01\x01\x12\x15\n\x08maxdelay\x18\x04 \x01(\rH\x02\x88\x01\x01\x12\x16\n\tretry_for\x18\x05 \x01(\rH\x03\x88\x01\x01\x12\x18\n\x0b\x64\x65scription\x18\x06 \x01(\tH\x04\x88\x01\x01\x12\x12\n\x05label\x18\x07 \x01(\tH\x05\x88\x01\x01\x12\x1b\n\x0e\x64\x65v_use_shadow\x18\x08 \x01(\x08H\x06\x88\x01\x01\x12\x0f\n\x07\x65xclude\x18\t \x03(\tB\x0e\n\x0c_amount_msatB\t\n\x07_maxfeeB\x0b\n\t_maxdelayB\x0c\n\n_retry_forB\x0e\n\x0c_descriptionB\x08\n\x06_labelB\x11\n\x0f_dev_use_shadow\"\xa5\x03\n\x0fRenepayResponse\x12\x18\n\x10payment_preimage\x18\x01 \x01(\x0c\x12\x14\n\x0cpayment_hash\x18\x02 \x01(\x0c\x12\x12\n\ncreated_at\x18\x03 \x01(\x01\x12\r\n\x05parts\x18\x04 \x01(\r\x12 \n\x0b\x61mount_msat\x18\x05 \x01(\x0b\x32\x0b.cln.Amount\x12%\n\x10\x61mount_sent_msat\x18\x06 \x01(\x0b\x32\x0b.cln.Amount\x12\x32\n\x06status\x18\x07 \x01(\x0e\x32\".cln.RenepayResponse.RenepayStatus\x12\x18\n\x0b\x64\x65stination\x18\x08 \x01(\x0cH\x00\x88\x01\x01\x12\x13\n\x06\x62olt11\x18\t \x01(\tH\x01\x88\x01\x01\x12\x13\n\x06\x62olt12\x18\n \x01(\tH\x02\x88\x01\x01\x12\x14\n\x07groupid\x18\x0b \x01(\x04H\x03\x88\x01\x01\"6\n\rRenepayStatus\x12\x0c\n\x08\x43OMPLETE\x10\x00\x12\x0b\n\x07PENDING\x10\x01\x12\n\n\x06\x46\x41ILED\x10\x02\x42\x0e\n\x0c_destinationB\t\n\x07_bolt11B\t\n\x07_bolt12B\n\n\x08_groupid\"l\n\x14ReserveinputsRequest\x12\x0c\n\x04psbt\x18\x01 \x01(\t\x12\x16\n\texclusive\x18\x02 \x01(\x08H\x00\x88\x01\x01\x12\x14\n\x07reserve\x18\x03 \x01(\rH\x01\x88\x01\x01\x42\x0c\n\n_exclusiveB\n\n\x08_reserve\"M\n\x15ReserveinputsResponse\x12\x34\n\x0creservations\x18\x01 \x03(\x0b\x32\x1e.cln.ReserveinputsReservations\"z\n\x19ReserveinputsReservations\x12\x0c\n\x04txid\x18\x01 \x01(\x0c\x12\x0c\n\x04vout\x18\x02 \x01(\r\x12\x14\n\x0cwas_reserved\x18\x03 \x01(\x08\x12\x10\n\x08reserved\x18\x04 \x01(\x08\x12\x19\n\x11reserved_to_block\x18\x05 \x01(\r\"4\n\x14SendcustommsgRequest\x12\x0f\n\x07node_id\x18\x01 \x01(\x0c\x12\x0b\n\x03msg\x18\x02 \x01(\x0c\"\'\n\x15SendcustommsgResponse\x12\x0e\n\x06status\x18\x01 \x01(\t\"\xb0\x01\n\x12SendinvoiceRequest\x12\x0e\n\x06invreq\x18\x01 \x01(\t\x12\r\n\x05label\x18\x02 \x01(\t\x12%\n\x0b\x61mount_msat\x18\x03 \x01(\x0b\x32\x0b.cln.AmountH\x00\x88\x01\x01\x12\x14\n\x07timeout\x18\x04 \x01(\rH\x01\x88\x01\x01\x12\x15\n\x08quantity\x18\x05 \x01(\x04H\x02\x88\x01\x01\x42\x0e\n\x0c_amount_msatB\n\n\x08_timeoutB\x0b\n\t_quantity\"\xcf\x04\n\x13SendinvoiceResponse\x12\r\n\x05label\x18\x01 \x01(\t\x12\x13\n\x0b\x64\x65scription\x18\x02 \x01(\t\x12\x14\n\x0cpayment_hash\x18\x03 \x01(\x0c\x12:\n\x06status\x18\x04 \x01(\x0e\x32*.cln.SendinvoiceResponse.SendinvoiceStatus\x12\x12\n\nexpires_at\x18\x05 \x01(\x04\x12%\n\x0b\x61mount_msat\x18\x06 \x01(\x0b\x32\x0b.cln.AmountH\x00\x88\x01\x01\x12\x13\n\x06\x62olt12\x18\x07 \x01(\tH\x01\x88\x01\x01\x12\x1a\n\rcreated_index\x18\x08 \x01(\x04H\x02\x88\x01\x01\x12\x1a\n\rupdated_index\x18\t \x01(\x04H\x03\x88\x01\x01\x12\x16\n\tpay_index\x18\n \x01(\x04H\x04\x88\x01\x01\x12.\n\x14\x61mount_received_msat\x18\x0b \x01(\x0b\x32\x0b.cln.AmountH\x05\x88\x01\x01\x12\x14\n\x07paid_at\x18\x0c \x01(\x04H\x06\x88\x01\x01\x12\x1d\n\x10payment_preimage\x18\r \x01(\x0cH\x07\x88\x01\x01\"6\n\x11SendinvoiceStatus\x12\n\n\x06UNPAID\x10\x00\x12\x08\n\x04PAID\x10\x01\x12\x0b\n\x07\x45XPIRED\x10\x02\x42\x0e\n\x0c_amount_msatB\t\n\x07_bolt12B\x10\n\x0e_created_indexB\x10\n\x0e_updated_indexB\x0c\n\n_pay_indexB\x17\n\x15_amount_received_msatB\n\n\x08_paid_atB\x13\n\x11_payment_preimage\"\xaa\x02\n\x11SetchannelRequest\x12\n\n\x02id\x18\x01 \x01(\t\x12!\n\x07\x66\x65\x65\x62\x61se\x18\x02 \x01(\x0b\x32\x0b.cln.AmountH\x00\x88\x01\x01\x12\x13\n\x06\x66\x65\x65ppm\x18\x03 \x01(\rH\x01\x88\x01\x01\x12!\n\x07htlcmin\x18\x04 \x01(\x0b\x32\x0b.cln.AmountH\x02\x88\x01\x01\x12!\n\x07htlcmax\x18\x05 \x01(\x0b\x32\x0b.cln.AmountH\x03\x88\x01\x01\x12\x19\n\x0c\x65nforcedelay\x18\x06 \x01(\rH\x04\x88\x01\x01\x12\x1c\n\x0fignorefeelimits\x18\x07 \x01(\x08H\x05\x88\x01\x01\x42\n\n\x08_feebaseB\t\n\x07_feeppmB\n\n\x08_htlcminB\n\n\x08_htlcmaxB\x0f\n\r_enforcedelayB\x12\n\x10_ignorefeelimits\"?\n\x12SetchannelResponse\x12)\n\x08\x63hannels\x18\x01 \x03(\x0b\x32\x17.cln.SetchannelChannels\"\xca\x03\n\x12SetchannelChannels\x12\x0f\n\x07peer_id\x18\x01 \x01(\x0c\x12\x12\n\nchannel_id\x18\x02 \x01(\x0c\x12\x1d\n\x10short_channel_id\x18\x03 \x01(\tH\x00\x88\x01\x01\x12\"\n\rfee_base_msat\x18\x04 \x01(\x0b\x32\x0b.cln.Amount\x12#\n\x1b\x66\x65\x65_proportional_millionths\x18\x05 \x01(\r\x12*\n\x15minimum_htlc_out_msat\x18\x06 \x01(\x0b\x32\x0b.cln.Amount\x12$\n\x17warning_htlcmin_too_low\x18\x07 \x01(\tH\x01\x88\x01\x01\x12*\n\x15maximum_htlc_out_msat\x18\x08 \x01(\x0b\x32\x0b.cln.Amount\x12%\n\x18warning_htlcmax_too_high\x18\t \x01(\tH\x02\x88\x01\x01\x12\x1e\n\x11ignore_fee_limits\x18\n \x01(\x08H\x03\x88\x01\x01\x42\x13\n\x11_short_channel_idB\x1a\n\x18_warning_htlcmin_too_lowB\x1b\n\x19_warning_htlcmax_too_highB\x14\n\x12_ignore_fee_limits\"b\n\x10SetconfigRequest\x12\x0e\n\x06\x63onfig\x18\x01 \x01(\t\x12\x10\n\x03val\x18\x02 \x01(\tH\x00\x88\x01\x01\x12\x16\n\ttransient\x18\x03 \x01(\x08H\x01\x88\x01\x01\x42\x06\n\x04_valB\x0c\n\n_transient\"9\n\x11SetconfigResponse\x12$\n\x06\x63onfig\x18\x01 \x01(\x0b\x32\x14.cln.SetconfigConfig\"\xa5\x02\n\x0fSetconfigConfig\x12\x0e\n\x06\x63onfig\x18\x01 \x01(\t\x12\x0e\n\x06source\x18\x02 \x01(\t\x12\x13\n\x06plugin\x18\x03 \x01(\tH\x00\x88\x01\x01\x12\x0f\n\x07\x64ynamic\x18\x04 \x01(\x08\x12\x10\n\x03set\x18\x05 \x01(\x08H\x01\x88\x01\x01\x12\x16\n\tvalue_str\x18\x06 \x01(\tH\x02\x88\x01\x01\x12$\n\nvalue_msat\x18\x07 \x01(\x0b\x32\x0b.cln.AmountH\x03\x88\x01\x01\x12\x16\n\tvalue_int\x18\x08 \x01(\x12H\x04\x88\x01\x01\x12\x17\n\nvalue_bool\x18\t \x01(\x08H\x05\x88\x01\x01\x42\t\n\x07_pluginB\x06\n\x04_setB\x0c\n\n_value_strB\r\n\x0b_value_msatB\x0c\n\n_value_intB\r\n\x0b_value_bool\"6\n\x15SetpsbtversionRequest\x12\x0c\n\x04psbt\x18\x01 \x01(\t\x12\x0f\n\x07version\x18\x02 \x01(\r\"&\n\x16SetpsbtversionResponse\x12\x0c\n\x04psbt\x18\x01 \x01(\t\"\'\n\x12SigninvoiceRequest\x12\x11\n\tinvstring\x18\x01 \x01(\t\"%\n\x13SigninvoiceResponse\x12\x0e\n\x06\x62olt11\x18\x01 \x01(\t\"%\n\x12SignmessageRequest\x12\x0f\n\x07message\x18\x01 \x01(\t\"F\n\x13SignmessageResponse\x12\x11\n\tsignature\x18\x01 \x01(\x0c\x12\r\n\x05recid\x18\x02 \x01(\x0c\x12\r\n\x05zbase\x18\x03 \x01(\t\"\xc8\x01\n\x11SpliceInitRequest\x12\x12\n\nchannel_id\x18\x01 \x01(\x0c\x12\x17\n\x0frelative_amount\x18\x02 \x01(\x12\x12\x18\n\x0binitialpsbt\x18\x03 \x01(\tH\x00\x88\x01\x01\x12\x1b\n\x0e\x66\x65\x65rate_per_kw\x18\x04 \x01(\rH\x01\x88\x01\x01\x12\x1a\n\rforce_feerate\x18\x05 \x01(\x08H\x02\x88\x01\x01\x42\x0e\n\x0c_initialpsbtB\x11\n\x0f_feerate_per_kwB\x10\n\x0e_force_feerate\"\"\n\x12SpliceInitResponse\x12\x0c\n\x04psbt\x18\x01 \x01(\t\"_\n\x13SpliceSignedRequest\x12\x12\n\nchannel_id\x18\x01 \x01(\x0c\x12\x0c\n\x04psbt\x18\x02 \x01(\t\x12\x17\n\nsign_first\x18\x03 \x01(\x08H\x00\x88\x01\x01\x42\r\n\x0b_sign_first\"^\n\x14SpliceSignedResponse\x12\n\n\x02tx\x18\x01 \x01(\x0c\x12\x0c\n\x04txid\x18\x02 \x01(\x0c\x12\x13\n\x06outnum\x18\x03 \x01(\rH\x00\x88\x01\x01\x12\x0c\n\x04psbt\x18\x04 \x01(\tB\t\n\x07_outnum\"7\n\x13SpliceUpdateRequest\x12\x12\n\nchannel_id\x18\x01 \x01(\x0c\x12\x0c\n\x04psbt\x18\x02 \x01(\t\"y\n\x14SpliceUpdateResponse\x12\x0c\n\x04psbt\x18\x01 \x01(\t\x12\x1b\n\x13\x63ommitments_secured\x18\x02 \x01(\x08\x12\x1f\n\x12signatures_secured\x18\x03 \x01(\x08H\x00\x88\x01\x01\x42\x15\n\x13_signatures_secured\"\xc6\x01\n\x10\x44\x65vspliceRequest\x12\x16\n\x0escript_or_json\x18\x01 \x01(\t\x12\x13\n\x06\x64ryrun\x18\x02 \x01(\x08H\x00\x88\x01\x01\x12\x1a\n\rforce_feerate\x18\x03 \x01(\x08H\x01\x88\x01\x01\x12\x16\n\tdebug_log\x18\x04 \x01(\x08H\x02\x88\x01\x01\x12\x17\n\ndev_wetrun\x18\x05 \x01(\x08H\x03\x88\x01\x01\x42\t\n\x07_dryrunB\x10\n\x0e_force_feerateB\x0c\n\n_debug_logB\r\n\x0b_dev_wetrun\"\x80\x01\n\x11\x44\x65vspliceResponse\x12\x0e\n\x06\x64ryrun\x18\x01 \x03(\t\x12\x11\n\x04psbt\x18\x02 \x01(\tH\x00\x88\x01\x01\x12\x0f\n\x02tx\x18\x03 \x01(\tH\x01\x88\x01\x01\x12\x11\n\x04txid\x18\x04 \x01(\tH\x02\x88\x01\x01\x12\x0b\n\x03log\x18\x05 \x03(\tB\x07\n\x05_psbtB\x05\n\x03_txB\x07\n\x05_txid\"H\n\x16UnreserveinputsRequest\x12\x0c\n\x04psbt\x18\x01 \x01(\t\x12\x14\n\x07reserve\x18\x02 \x01(\rH\x00\x88\x01\x01\x42\n\n\x08_reserve\"Q\n\x17UnreserveinputsResponse\x12\x36\n\x0creservations\x18\x01 \x03(\x0b\x32 .cln.UnreserveinputsReservations\"\x97\x01\n\x1bUnreserveinputsReservations\x12\x0c\n\x04txid\x18\x01 \x01(\x0c\x12\x0c\n\x04vout\x18\x02 \x01(\r\x12\x14\n\x0cwas_reserved\x18\x03 \x01(\x08\x12\x10\n\x08reserved\x18\x04 \x01(\x08\x12\x1e\n\x11reserved_to_block\x18\x05 \x01(\rH\x00\x88\x01\x01\x42\x14\n\x12_reserved_to_block\"n\n\x14UpgradewalletRequest\x12\"\n\x07\x66\x65\x65rate\x18\x01 \x01(\x0b\x32\x0c.cln.FeerateH\x00\x88\x01\x01\x12\x17\n\nreservedok\x18\x02 \x01(\x08H\x01\x88\x01\x01\x42\n\n\x08_feerateB\r\n\x0b_reservedok\"\x95\x01\n\x15UpgradewalletResponse\x12\x1a\n\rupgraded_outs\x18\x01 \x01(\x04H\x00\x88\x01\x01\x12\x11\n\x04psbt\x18\x02 \x01(\tH\x01\x88\x01\x01\x12\x0f\n\x02tx\x18\x03 \x01(\x0cH\x02\x88\x01\x01\x12\x11\n\x04txid\x18\x04 \x01(\x0cH\x03\x88\x01\x01\x42\x10\n\x0e_upgraded_outsB\x07\n\x05_psbtB\x05\n\x03_txB\x07\n\x05_txid\"O\n\x16WaitblockheightRequest\x12\x13\n\x0b\x62lockheight\x18\x01 \x01(\r\x12\x14\n\x07timeout\x18\x02 \x01(\rH\x00\x88\x01\x01\x42\n\n\x08_timeout\".\n\x17WaitblockheightResponse\x12\x13\n\x0b\x62lockheight\x18\x01 \x01(\r\"\xa6\x02\n\x0bWaitRequest\x12\x31\n\tsubsystem\x18\x01 \x01(\x0e\x32\x1e.cln.WaitRequest.WaitSubsystem\x12\x31\n\tindexname\x18\x02 \x01(\x0e\x32\x1e.cln.WaitRequest.WaitIndexname\x12\x11\n\tnextvalue\x18\x03 \x01(\x04\"f\n\rWaitSubsystem\x12\x0c\n\x08INVOICES\x10\x00\x12\x0c\n\x08\x46ORWARDS\x10\x01\x12\x0c\n\x08SENDPAYS\x10\x02\x12\t\n\x05HTLCS\x10\x03\x12\x0e\n\nCHAINMOVES\x10\x04\x12\x10\n\x0c\x43HANNELMOVES\x10\x05\"6\n\rWaitIndexname\x12\x0b\n\x07\x43REATED\x10\x00\x12\x0b\n\x07UPDATED\x10\x01\x12\x0b\n\x07\x44\x45LETED\x10\x02\"\x97\x05\n\x0cWaitResponse\x12\x32\n\tsubsystem\x18\x01 \x01(\x0e\x32\x1f.cln.WaitResponse.WaitSubsystem\x12\x14\n\x07\x63reated\x18\x02 \x01(\x04H\x00\x88\x01\x01\x12\x14\n\x07updated\x18\x03 \x01(\x04H\x01\x88\x01\x01\x12\x14\n\x07\x64\x65leted\x18\x04 \x01(\x04H\x02\x88\x01\x01\x12&\n\x07\x64\x65tails\x18\x05 \x01(\x0b\x32\x10.cln.WaitDetailsH\x03\x88\x01\x01\x12(\n\x08\x66orwards\x18\x06 \x01(\x0b\x32\x11.cln.WaitForwardsH\x04\x88\x01\x01\x12(\n\x08invoices\x18\x07 \x01(\x0b\x32\x11.cln.WaitInvoicesH\x05\x88\x01\x01\x12(\n\x08sendpays\x18\x08 \x01(\x0b\x32\x11.cln.WaitSendpaysH\x06\x88\x01\x01\x12\"\n\x05htlcs\x18\t \x01(\x0b\x32\x0e.cln.WaitHtlcsH\x07\x88\x01\x01\x12,\n\nchainmoves\x18\n \x01(\x0b\x32\x13.cln.WaitChainmovesH\x08\x88\x01\x01\x12\x30\n\x0c\x63hannelmoves\x18\x0b \x01(\x0b\x32\x15.cln.WaitChannelmovesH\t\x88\x01\x01\"f\n\rWaitSubsystem\x12\x0c\n\x08INVOICES\x10\x00\x12\x0c\n\x08\x46ORWARDS\x10\x01\x12\x0c\n\x08SENDPAYS\x10\x02\x12\t\n\x05HTLCS\x10\x03\x12\x0e\n\nCHAINMOVES\x10\x04\x12\x10\n\x0c\x43HANNELMOVES\x10\x05\x42\n\n\x08_createdB\n\n\x08_updatedB\n\n\x08_deletedB\n\n\x08_detailsB\x0b\n\t_forwardsB\x0b\n\t_invoicesB\x0b\n\t_sendpaysB\x08\n\x06_htlcsB\r\n\x0b_chainmovesB\x0f\n\r_channelmoves\"\xcb\x02\n\x0cWaitForwards\x12\x39\n\x06status\x18\x01 \x01(\x0e\x32$.cln.WaitForwards.WaitForwardsStatusH\x00\x88\x01\x01\x12\x17\n\nin_channel\x18\x02 \x01(\tH\x01\x88\x01\x01\x12\x17\n\nin_htlc_id\x18\x03 \x01(\x04H\x02\x88\x01\x01\x12!\n\x07in_msat\x18\x04 \x01(\x0b\x32\x0b.cln.AmountH\x03\x88\x01\x01\x12\x18\n\x0bout_channel\x18\x05 \x01(\tH\x04\x88\x01\x01\"L\n\x12WaitForwardsStatus\x12\x0b\n\x07OFFERED\x10\x00\x12\x0b\n\x07SETTLED\x10\x01\x12\n\n\x06\x46\x41ILED\x10\x02\x12\x10\n\x0cLOCAL_FAILED\x10\x03\x42\t\n\x07_statusB\r\n\x0b_in_channelB\r\n\x0b_in_htlc_idB\n\n\x08_in_msatB\x0e\n\x0c_out_channel\"\x95\x02\n\x0cWaitInvoices\x12\x39\n\x06status\x18\x01 \x01(\x0e\x32$.cln.WaitInvoices.WaitInvoicesStatusH\x00\x88\x01\x01\x12\x12\n\x05label\x18\x02 \x01(\tH\x01\x88\x01\x01\x12\x18\n\x0b\x64\x65scription\x18\x03 \x01(\tH\x02\x88\x01\x01\x12\x13\n\x06\x62olt11\x18\x04 \x01(\tH\x03\x88\x01\x01\x12\x13\n\x06\x62olt12\x18\x05 \x01(\tH\x04\x88\x01\x01\"7\n\x12WaitInvoicesStatus\x12\n\n\x06UNPAID\x10\x00\x12\x08\n\x04PAID\x10\x01\x12\x0b\n\x07\x45XPIRED\x10\x02\x42\t\n\x07_statusB\x08\n\x06_labelB\x0e\n\x0c_descriptionB\t\n\x07_bolt11B\t\n\x07_bolt12\"\xff\x01\n\x0cWaitSendpays\x12\x39\n\x06status\x18\x01 \x01(\x0e\x32$.cln.WaitSendpays.WaitSendpaysStatusH\x00\x88\x01\x01\x12\x13\n\x06partid\x18\x02 \x01(\x04H\x01\x88\x01\x01\x12\x14\n\x07groupid\x18\x03 \x01(\x04H\x02\x88\x01\x01\x12\x19\n\x0cpayment_hash\x18\x04 \x01(\x0cH\x03\x88\x01\x01\";\n\x12WaitSendpaysStatus\x12\x0b\n\x07PENDING\x10\x00\x12\n\n\x06\x46\x41ILED\x10\x01\x12\x0c\n\x08\x43OMPLETE\x10\x02\x42\t\n\x07_statusB\t\n\x07_partidB\n\n\x08_groupidB\x0f\n\r_payment_hash\"\xa8\x07\n\tWaitHtlcs\x12\x31\n\x05state\x18\x01 \x01(\x0e\x32\x1d.cln.WaitHtlcs.WaitHtlcsStateH\x00\x88\x01\x01\x12\x14\n\x07htlc_id\x18\x02 \x01(\x04H\x01\x88\x01\x01\x12\x1d\n\x10short_channel_id\x18\x03 \x01(\tH\x02\x88\x01\x01\x12\x18\n\x0b\x63ltv_expiry\x18\x04 \x01(\rH\x03\x88\x01\x01\x12%\n\x0b\x61mount_msat\x18\x05 \x01(\x0b\x32\x0b.cln.AmountH\x04\x88\x01\x01\x12\x39\n\tdirection\x18\x06 \x01(\x0e\x32!.cln.WaitHtlcs.WaitHtlcsDirectionH\x05\x88\x01\x01\x12\x19\n\x0cpayment_hash\x18\x07 \x01(\x0cH\x06\x88\x01\x01\"\x8a\x04\n\x0eWaitHtlcsState\x12\x11\n\rSENT_ADD_HTLC\x10\x00\x12\x13\n\x0fSENT_ADD_COMMIT\x10\x01\x12\x17\n\x13RCVD_ADD_REVOCATION\x10\x02\x12\x17\n\x13RCVD_ADD_ACK_COMMIT\x10\x03\x12\x1b\n\x17SENT_ADD_ACK_REVOCATION\x10\x04\x12\x14\n\x10RCVD_REMOVE_HTLC\x10\x05\x12\x16\n\x12RCVD_REMOVE_COMMIT\x10\x06\x12\x1a\n\x16SENT_REMOVE_REVOCATION\x10\x07\x12\x1a\n\x16SENT_REMOVE_ACK_COMMIT\x10\x08\x12\x1e\n\x1aRCVD_REMOVE_ACK_REVOCATION\x10\t\x12\x11\n\rRCVD_ADD_HTLC\x10\n\x12\x13\n\x0fRCVD_ADD_COMMIT\x10\x0b\x12\x17\n\x13SENT_ADD_REVOCATION\x10\x0c\x12\x17\n\x13SENT_ADD_ACK_COMMIT\x10\r\x12\x1b\n\x17RCVD_ADD_ACK_REVOCATION\x10\x0e\x12\x14\n\x10SENT_REMOVE_HTLC\x10\x0f\x12\x16\n\x12SENT_REMOVE_COMMIT\x10\x10\x12\x1a\n\x16RCVD_REMOVE_REVOCATION\x10\x11\x12\x1a\n\x16RCVD_REMOVE_ACK_COMMIT\x10\x12\x12\x1e\n\x1aSENT_REMOVE_ACK_REVOCATION\x10\x13\"%\n\x12WaitHtlcsDirection\x12\x07\n\x03OUT\x10\x00\x12\x06\n\x02IN\x10\x01\x42\x08\n\x06_stateB\n\n\x08_htlc_idB\x13\n\x11_short_channel_idB\x0e\n\x0c_cltv_expiryB\x0e\n\x0c_amount_msatB\x0c\n\n_directionB\x0f\n\r_payment_hash\"d\n\x0eWaitChainmoves\x12\x0f\n\x07\x61\x63\x63ount\x18\x01 \x01(\t\x12 \n\x0b\x63redit_msat\x18\x02 \x01(\x0b\x32\x0b.cln.Amount\x12\x1f\n\ndebit_msat\x18\x03 \x01(\x0b\x32\x0b.cln.Amount\"f\n\x10WaitChannelmoves\x12\x0f\n\x07\x61\x63\x63ount\x18\x01 \x01(\t\x12 \n\x0b\x63redit_msat\x18\x02 \x01(\x0b\x32\x0b.cln.Amount\x12\x1f\n\ndebit_msat\x18\x03 \x01(\x0b\x32\x0b.cln.Amount\"\xfc\x04\n\x0bWaitDetails\x12\x37\n\x06status\x18\x01 \x01(\x0e\x32\".cln.WaitDetails.WaitDetailsStatusH\x00\x88\x01\x01\x12\x12\n\x05label\x18\x02 \x01(\tH\x01\x88\x01\x01\x12\x18\n\x0b\x64\x65scription\x18\x03 \x01(\tH\x02\x88\x01\x01\x12\x13\n\x06\x62olt11\x18\x04 \x01(\tH\x03\x88\x01\x01\x12\x13\n\x06\x62olt12\x18\x05 \x01(\tH\x04\x88\x01\x01\x12\x13\n\x06partid\x18\x06 \x01(\x04H\x05\x88\x01\x01\x12\x14\n\x07groupid\x18\x07 \x01(\x04H\x06\x88\x01\x01\x12\x19\n\x0cpayment_hash\x18\x08 \x01(\x0cH\x07\x88\x01\x01\x12\x17\n\nin_channel\x18\t \x01(\tH\x08\x88\x01\x01\x12\x17\n\nin_htlc_id\x18\n \x01(\x04H\t\x88\x01\x01\x12!\n\x07in_msat\x18\x0b \x01(\x0b\x32\x0b.cln.AmountH\n\x88\x01\x01\x12\x18\n\x0bout_channel\x18\x0c \x01(\tH\x0b\x88\x01\x01\"\x89\x01\n\x11WaitDetailsStatus\x12\n\n\x06UNPAID\x10\x00\x12\x08\n\x04PAID\x10\x01\x12\x0b\n\x07\x45XPIRED\x10\x02\x12\x0b\n\x07PENDING\x10\x03\x12\n\n\x06\x46\x41ILED\x10\x04\x12\x0c\n\x08\x43OMPLETE\x10\x05\x12\x0b\n\x07OFFERED\x10\x06\x12\x0b\n\x07SETTLED\x10\x07\x12\x10\n\x0cLOCAL_FAILED\x10\x08\x42\t\n\x07_statusB\x08\n\x06_labelB\x0e\n\x0c_descriptionB\t\n\x07_bolt11B\t\n\x07_bolt12B\t\n\x07_partidB\n\n\x08_groupidB\x0f\n\r_payment_hashB\r\n\x0b_in_channelB\r\n\x0b_in_htlc_idB\n\n\x08_in_msatB\x0e\n\x0c_out_channel\"4\n\x12ListconfigsRequest\x12\x13\n\x06\x63onfig\x18\x01 \x01(\tH\x00\x88\x01\x01\x42\t\n\x07_config\"P\n\x13ListconfigsResponse\x12-\n\x07\x63onfigs\x18\x01 \x01(\x0b\x32\x17.cln.ListconfigsConfigsH\x00\x88\x01\x01\x42\n\n\x08_configs\"\xe9.\n\x12ListconfigsConfigs\x12.\n\x04\x63onf\x18\x01 \x01(\x0b\x32\x1b.cln.ListconfigsConfigsConfH\x00\x88\x01\x01\x12\x38\n\tdeveloper\x18\x02 \x01(\x0b\x32 .cln.ListconfigsConfigsDeveloperH\x01\x88\x01\x01\x12?\n\rclear_plugins\x18\x03 \x01(\x0b\x32#.cln.ListconfigsConfigsClearpluginsH\x02\x88\x01\x01\x12;\n\x0b\x64isable_mpp\x18\x04 \x01(\x0b\x32!.cln.ListconfigsConfigsDisablemppH\x03\x88\x01\x01\x12\x34\n\x07mainnet\x18\x05 \x01(\x0b\x32\x1e.cln.ListconfigsConfigsMainnetH\x04\x88\x01\x01\x12\x34\n\x07regtest\x18\x06 \x01(\x0b\x32\x1e.cln.ListconfigsConfigsRegtestH\x05\x88\x01\x01\x12\x32\n\x06signet\x18\x07 \x01(\x0b\x32\x1d.cln.ListconfigsConfigsSignetH\x06\x88\x01\x01\x12\x34\n\x07testnet\x18\x08 \x01(\x0b\x32\x1e.cln.ListconfigsConfigsTestnetH\x07\x88\x01\x01\x12\x45\n\x10important_plugin\x18\t \x01(\x0b\x32&.cln.ListconfigsConfigsImportantpluginH\x08\x88\x01\x01\x12\x32\n\x06plugin\x18\n \x01(\x0b\x32\x1d.cln.ListconfigsConfigsPluginH\t\x88\x01\x01\x12\x39\n\nplugin_dir\x18\x0b \x01(\x0b\x32 .cln.ListconfigsConfigsPlugindirH\n\x88\x01\x01\x12?\n\rlightning_dir\x18\x0c \x01(\x0b\x32#.cln.ListconfigsConfigsLightningdirH\x0b\x88\x01\x01\x12\x34\n\x07network\x18\r \x01(\x0b\x32\x1e.cln.ListconfigsConfigsNetworkH\x0c\x88\x01\x01\x12N\n\x15\x61llow_deprecated_apis\x18\x0e \x01(\x0b\x32*.cln.ListconfigsConfigsAllowdeprecatedapisH\r\x88\x01\x01\x12\x35\n\x08rpc_file\x18\x0f \x01(\x0b\x32\x1e.cln.ListconfigsConfigsRpcfileH\x0e\x88\x01\x01\x12\x41\n\x0e\x64isable_plugin\x18\x10 \x01(\x0b\x32$.cln.ListconfigsConfigsDisablepluginH\x0f\x88\x01\x01\x12\x44\n\x10\x61lways_use_proxy\x18\x11 \x01(\x0b\x32%.cln.ListconfigsConfigsAlwaysuseproxyH\x10\x88\x01\x01\x12\x32\n\x06\x64\x61\x65mon\x18\x12 \x01(\x0b\x32\x1d.cln.ListconfigsConfigsDaemonH\x11\x88\x01\x01\x12\x32\n\x06wallet\x18\x13 \x01(\x0b\x32\x1d.cln.ListconfigsConfigsWalletH\x12\x88\x01\x01\x12\x41\n\x0elarge_channels\x18\x14 \x01(\x0b\x32$.cln.ListconfigsConfigsLargechannelsH\x13\x88\x01\x01\x12P\n\x16\x65xperimental_dual_fund\x18\x15 \x01(\x0b\x32+.cln.ListconfigsConfigsExperimentaldualfundH\x14\x88\x01\x01\x12O\n\x15\x65xperimental_splicing\x18\x16 \x01(\x0b\x32+.cln.ListconfigsConfigsExperimentalsplicingH\x15\x88\x01\x01\x12Z\n\x1b\x65xperimental_onion_messages\x18\x17 \x01(\x0b\x32\x30.cln.ListconfigsConfigsExperimentalonionmessagesH\x16\x88\x01\x01\x12K\n\x13\x65xperimental_offers\x18\x18 \x01(\x0b\x32).cln.ListconfigsConfigsExperimentaloffersH\x17\x88\x01\x01\x12i\n#experimental_shutdown_wrong_funding\x18\x19 \x01(\x0b\x32\x37.cln.ListconfigsConfigsExperimentalshutdownwrongfundingH\x18\x88\x01\x01\x12V\n\x19\x65xperimental_peer_storage\x18\x1a \x01(\x0b\x32..cln.ListconfigsConfigsExperimentalpeerstorageH\x19\x88\x01\x01\x12M\n\x14\x65xperimental_anchors\x18\x1b \x01(\x0b\x32*.cln.ListconfigsConfigsExperimentalanchorsH\x1a\x88\x01\x01\x12\x45\n\x10\x64\x61tabase_upgrade\x18\x1c \x01(\x0b\x32&.cln.ListconfigsConfigsDatabaseupgradeH\x1b\x88\x01\x01\x12,\n\x03rgb\x18\x1d \x01(\x0b\x32\x1a.cln.ListconfigsConfigsRgbH\x1c\x88\x01\x01\x12\x30\n\x05\x61lias\x18\x1e \x01(\x0b\x32\x1c.cln.ListconfigsConfigsAliasH\x1d\x88\x01\x01\x12\x35\n\x08pid_file\x18\x1f \x01(\x0b\x32\x1e.cln.ListconfigsConfigsPidfileH\x1e\x88\x01\x01\x12\x46\n\x11ignore_fee_limits\x18 \x01(\x0b\x32&.cln.ListconfigsConfigsIgnorefeelimitsH\x1f\x88\x01\x01\x12\x45\n\x10watchtime_blocks\x18! \x01(\x0b\x32&.cln.ListconfigsConfigsWatchtimeblocksH \x88\x01\x01\x12J\n\x13max_locktime_blocks\x18\" \x01(\x0b\x32(.cln.ListconfigsConfigsMaxlocktimeblocksH!\x88\x01\x01\x12\x45\n\x10\x66unding_confirms\x18# \x01(\x0b\x32&.cln.ListconfigsConfigsFundingconfirmsH\"\x88\x01\x01\x12\x39\n\ncltv_delta\x18$ \x01(\x0b\x32 .cln.ListconfigsConfigsCltvdeltaH#\x88\x01\x01\x12\x39\n\ncltv_final\x18% \x01(\x0b\x32 .cln.ListconfigsConfigsCltvfinalH$\x88\x01\x01\x12;\n\x0b\x63ommit_time\x18& \x01(\x0b\x32!.cln.ListconfigsConfigsCommittimeH%\x88\x01\x01\x12\x35\n\x08\x66\x65\x65_base\x18\' \x01(\x0b\x32\x1e.cln.ListconfigsConfigsFeebaseH&\x88\x01\x01\x12\x32\n\x06rescan\x18( \x01(\x0b\x32\x1d.cln.ListconfigsConfigsRescanH\'\x88\x01\x01\x12\x42\n\x0f\x66\x65\x65_per_satoshi\x18) \x01(\x0b\x32$.cln.ListconfigsConfigsFeepersatoshiH(\x88\x01\x01\x12L\n\x14max_concurrent_htlcs\x18* \x01(\x0b\x32).cln.ListconfigsConfigsMaxconcurrenthtlcsH)\x88\x01\x01\x12\x46\n\x11htlc_minimum_msat\x18+ \x01(\x0b\x32&.cln.ListconfigsConfigsHtlcminimummsatH*\x88\x01\x01\x12\x46\n\x11htlc_maximum_msat\x18, \x01(\x0b\x32&.cln.ListconfigsConfigsHtlcmaximummsatH+\x88\x01\x01\x12X\n\x1bmax_dust_htlc_exposure_msat\x18- \x01(\x0b\x32..cln.ListconfigsConfigsMaxdusthtlcexposuremsatH,\x88\x01\x01\x12\x44\n\x10min_capacity_sat\x18. \x01(\x0b\x32%.cln.ListconfigsConfigsMincapacitysatH-\x88\x01\x01\x12.\n\x04\x61\x64\x64r\x18/ \x01(\x0b\x32\x1b.cln.ListconfigsConfigsAddrH.\x88\x01\x01\x12?\n\rannounce_addr\x18\x30 \x01(\x0b\x32#.cln.ListconfigsConfigsAnnounceaddrH/\x88\x01\x01\x12\x37\n\tbind_addr\x18\x31 \x01(\x0b\x32\x1f.cln.ListconfigsConfigsBindaddrH0\x88\x01\x01\x12\x34\n\x07offline\x18\x32 \x01(\x0b\x32\x1e.cln.ListconfigsConfigsOfflineH1\x88\x01\x01\x12:\n\nautolisten\x18\x33 \x01(\x0b\x32!.cln.ListconfigsConfigsAutolistenH2\x88\x01\x01\x12\x30\n\x05proxy\x18\x34 \x01(\x0b\x32\x1c.cln.ListconfigsConfigsProxyH3\x88\x01\x01\x12;\n\x0b\x64isable_dns\x18\x35 \x01(\x0b\x32!.cln.ListconfigsConfigsDisablednsH4\x88\x01\x01\x12T\n\x18\x61nnounce_addr_discovered\x18\x36 \x01(\x0b\x32-.cln.ListconfigsConfigsAnnounceaddrdiscoveredH5\x88\x01\x01\x12]\n\x1d\x61nnounce_addr_discovered_port\x18\x37 \x01(\x0b\x32\x31.cln.ListconfigsConfigsAnnounceaddrdiscoveredportH6\x88\x01\x01\x12?\n\rencrypted_hsm\x18\x38 \x01(\x0b\x32#.cln.ListconfigsConfigsEncryptedhsmH7\x88\x01\x01\x12>\n\rrpc_file_mode\x18\x39 \x01(\x0b\x32\".cln.ListconfigsConfigsRpcfilemodeH8\x88\x01\x01\x12\x37\n\tlog_level\x18: \x01(\x0b\x32\x1f.cln.ListconfigsConfigsLoglevelH9\x88\x01\x01\x12\x39\n\nlog_prefix\x18; \x01(\x0b\x32 .cln.ListconfigsConfigsLogprefixH:\x88\x01\x01\x12\x35\n\x08log_file\x18< \x01(\x0b\x32\x1e.cln.ListconfigsConfigsLogfileH;\x88\x01\x01\x12\x41\n\x0elog_timestamps\x18= \x01(\x0b\x32$.cln.ListconfigsConfigsLogtimestampsH<\x88\x01\x01\x12\x41\n\x0e\x66orce_feerates\x18> \x01(\x0b\x32$.cln.ListconfigsConfigsForcefeeratesH=\x88\x01\x01\x12\x38\n\tsubdaemon\x18? \x01(\x0b\x32 .cln.ListconfigsConfigsSubdaemonH>\x88\x01\x01\x12Q\n\x16\x66\x65tchinvoice_noconnect\x18@ \x01(\x0b\x32,.cln.ListconfigsConfigsFetchinvoicenoconnectH?\x88\x01\x01\x12L\n\x14tor_service_password\x18\x42 \x01(\x0b\x32).cln.ListconfigsConfigsTorservicepasswordH@\x88\x01\x01\x12\x46\n\x11\x61nnounce_addr_dns\x18\x43 \x01(\x0b\x32&.cln.ListconfigsConfigsAnnounceaddrdnsHA\x88\x01\x01\x12T\n\x18require_confirmed_inputs\x18\x44 \x01(\x0b\x32-.cln.ListconfigsConfigsRequireconfirmedinputsHB\x88\x01\x01\x12\x39\n\ncommit_fee\x18\x45 \x01(\x0b\x32 .cln.ListconfigsConfigsCommitfeeHC\x88\x01\x01\x12N\n\x15\x63ommit_feerate_offset\x18\x46 \x01(\x0b\x32*.cln.ListconfigsConfigsCommitfeerateoffsetHD\x88\x01\x01\x12T\n\x18\x61utoconnect_seeker_peers\x18G \x01(\x0b\x32-.cln.ListconfigsConfigsAutoconnectseekerpeersHE\x88\x01\x01\x42\x07\n\x05_confB\x0c\n\n_developerB\x10\n\x0e_clear_pluginsB\x0e\n\x0c_disable_mppB\n\n\x08_mainnetB\n\n\x08_regtestB\t\n\x07_signetB\n\n\x08_testnetB\x13\n\x11_important_pluginB\t\n\x07_pluginB\r\n\x0b_plugin_dirB\x10\n\x0e_lightning_dirB\n\n\x08_networkB\x18\n\x16_allow_deprecated_apisB\x0b\n\t_rpc_fileB\x11\n\x0f_disable_pluginB\x13\n\x11_always_use_proxyB\t\n\x07_daemonB\t\n\x07_walletB\x11\n\x0f_large_channelsB\x19\n\x17_experimental_dual_fundB\x18\n\x16_experimental_splicingB\x1e\n\x1c_experimental_onion_messagesB\x16\n\x14_experimental_offersB&\n$_experimental_shutdown_wrong_fundingB\x1c\n\x1a_experimental_peer_storageB\x17\n\x15_experimental_anchorsB\x13\n\x11_database_upgradeB\x06\n\x04_rgbB\x08\n\x06_aliasB\x0b\n\t_pid_fileB\x14\n\x12_ignore_fee_limitsB\x13\n\x11_watchtime_blocksB\x16\n\x14_max_locktime_blocksB\x13\n\x11_funding_confirmsB\r\n\x0b_cltv_deltaB\r\n\x0b_cltv_finalB\x0e\n\x0c_commit_timeB\x0b\n\t_fee_baseB\t\n\x07_rescanB\x12\n\x10_fee_per_satoshiB\x17\n\x15_max_concurrent_htlcsB\x14\n\x12_htlc_minimum_msatB\x14\n\x12_htlc_maximum_msatB\x1e\n\x1c_max_dust_htlc_exposure_msatB\x13\n\x11_min_capacity_satB\x07\n\x05_addrB\x10\n\x0e_announce_addrB\x0c\n\n_bind_addrB\n\n\x08_offlineB\r\n\x0b_autolistenB\x08\n\x06_proxyB\x0e\n\x0c_disable_dnsB\x1b\n\x19_announce_addr_discoveredB \n\x1e_announce_addr_discovered_portB\x10\n\x0e_encrypted_hsmB\x10\n\x0e_rpc_file_modeB\x0c\n\n_log_levelB\r\n\x0b_log_prefixB\x0b\n\t_log_fileB\x11\n\x0f_log_timestampsB\x11\n\x0f_force_feeratesB\x0c\n\n_subdaemonB\x19\n\x17_fetchinvoice_noconnectB\x17\n\x15_tor_service_passwordB\x14\n\x12_announce_addr_dnsB\x1b\n\x19_require_confirmed_inputsB\r\n\x0b_commit_feeB\x18\n\x16_commit_feerate_offsetB\x1b\n\x19_autoconnect_seeker_peers\"\xa2\x01\n\x16ListconfigsConfigsConf\x12\x11\n\tvalue_str\x18\x01 \x01(\t\x12H\n\x06source\x18\x02 \x01(\x0e\x32\x38.cln.ListconfigsConfigsConf.ListconfigsConfigsConfSource\"+\n\x1cListconfigsConfigsConfSource\x12\x0b\n\x07\x43MDLINE\x10\x00\":\n\x1bListconfigsConfigsDeveloper\x12\x0b\n\x03set\x18\x01 \x01(\x08\x12\x0e\n\x06source\x18\x02 \x01(\t\"=\n\x1eListconfigsConfigsClearplugins\x12\x0b\n\x03set\x18\x01 \x01(\x08\x12\x0e\n\x06source\x18\x02 \x01(\t\"[\n\x1cListconfigsConfigsDisablempp\x12\x0b\n\x03set\x18\x01 \x01(\x08\x12\x0e\n\x06source\x18\x02 \x01(\t\x12\x13\n\x06plugin\x18\x03 \x01(\tH\x00\x88\x01\x01\x42\t\n\x07_plugin\"8\n\x19ListconfigsConfigsMainnet\x12\x0b\n\x03set\x18\x01 \x01(\x08\x12\x0e\n\x06source\x18\x02 \x01(\t\"8\n\x19ListconfigsConfigsRegtest\x12\x0b\n\x03set\x18\x01 \x01(\x08\x12\x0e\n\x06source\x18\x02 \x01(\t\"7\n\x18ListconfigsConfigsSignet\x12\x0b\n\x03set\x18\x01 \x01(\x08\x12\x0e\n\x06source\x18\x02 \x01(\t\"8\n\x19ListconfigsConfigsTestnet\x12\x0b\n\x03set\x18\x01 \x01(\x08\x12\x0e\n\x06source\x18\x02 \x01(\t\"H\n!ListconfigsConfigsImportantplugin\x12\x12\n\nvalues_str\x18\x01 \x03(\t\x12\x0f\n\x07sources\x18\x02 \x03(\t\"?\n\x18ListconfigsConfigsPlugin\x12\x12\n\nvalues_str\x18\x01 \x03(\t\x12\x0f\n\x07sources\x18\x02 \x03(\t\"B\n\x1bListconfigsConfigsPlugindir\x12\x12\n\nvalues_str\x18\x01 \x03(\t\x12\x0f\n\x07sources\x18\x02 \x03(\t\"C\n\x1eListconfigsConfigsLightningdir\x12\x11\n\tvalue_str\x18\x01 \x01(\t\x12\x0e\n\x06source\x18\x02 \x01(\t\">\n\x19ListconfigsConfigsNetwork\x12\x11\n\tvalue_str\x18\x01 \x01(\t\x12\x0e\n\x06source\x18\x02 \x01(\t\"K\n%ListconfigsConfigsAllowdeprecatedapis\x12\x12\n\nvalue_bool\x18\x01 \x01(\x08\x12\x0e\n\x06source\x18\x02 \x01(\t\">\n\x19ListconfigsConfigsRpcfile\x12\x11\n\tvalue_str\x18\x01 \x01(\t\x12\x0e\n\x06source\x18\x02 \x01(\t\"F\n\x1fListconfigsConfigsDisableplugin\x12\x12\n\nvalues_str\x18\x01 \x03(\t\x12\x0f\n\x07sources\x18\x02 \x03(\t\"F\n ListconfigsConfigsAlwaysuseproxy\x12\x12\n\nvalue_bool\x18\x01 \x01(\x08\x12\x0e\n\x06source\x18\x02 \x01(\t\"7\n\x18ListconfigsConfigsDaemon\x12\x0b\n\x03set\x18\x01 \x01(\x08\x12\x0e\n\x06source\x18\x02 \x01(\t\"=\n\x18ListconfigsConfigsWallet\x12\x11\n\tvalue_str\x18\x01 \x01(\t\x12\x0e\n\x06source\x18\x02 \x01(\t\">\n\x1fListconfigsConfigsLargechannels\x12\x0b\n\x03set\x18\x01 \x01(\x08\x12\x0e\n\x06source\x18\x02 \x01(\t\"E\n&ListconfigsConfigsExperimentaldualfund\x12\x0b\n\x03set\x18\x01 \x01(\x08\x12\x0e\n\x06source\x18\x02 \x01(\t\"E\n&ListconfigsConfigsExperimentalsplicing\x12\x0b\n\x03set\x18\x01 \x01(\x08\x12\x0e\n\x06source\x18\x02 \x01(\t\"J\n+ListconfigsConfigsExperimentalonionmessages\x12\x0b\n\x03set\x18\x01 \x01(\x08\x12\x0e\n\x06source\x18\x02 \x01(\t\"C\n$ListconfigsConfigsExperimentaloffers\x12\x0b\n\x03set\x18\x01 \x01(\x08\x12\x0e\n\x06source\x18\x02 \x01(\t\"Q\n2ListconfigsConfigsExperimentalshutdownwrongfunding\x12\x0b\n\x03set\x18\x01 \x01(\x08\x12\x0e\n\x06source\x18\x02 \x01(\t\"H\n)ListconfigsConfigsExperimentalpeerstorage\x12\x0b\n\x03set\x18\x01 \x01(\x08\x12\x0e\n\x06source\x18\x02 \x01(\t\"D\n%ListconfigsConfigsExperimentalanchors\x12\x0b\n\x03set\x18\x01 \x01(\x08\x12\x0e\n\x06source\x18\x02 \x01(\t\"G\n!ListconfigsConfigsDatabaseupgrade\x12\x12\n\nvalue_bool\x18\x01 \x01(\x08\x12\x0e\n\x06source\x18\x02 \x01(\t\":\n\x15ListconfigsConfigsRgb\x12\x11\n\tvalue_str\x18\x01 \x01(\x0c\x12\x0e\n\x06source\x18\x02 \x01(\t\"<\n\x17ListconfigsConfigsAlias\x12\x11\n\tvalue_str\x18\x01 \x01(\t\x12\x0e\n\x06source\x18\x02 \x01(\t\">\n\x19ListconfigsConfigsPidfile\x12\x11\n\tvalue_str\x18\x01 \x01(\t\x12\x0e\n\x06source\x18\x02 \x01(\t\"G\n!ListconfigsConfigsIgnorefeelimits\x12\x12\n\nvalue_bool\x18\x01 \x01(\x08\x12\x0e\n\x06source\x18\x02 \x01(\t\"F\n!ListconfigsConfigsWatchtimeblocks\x12\x11\n\tvalue_int\x18\x01 \x01(\r\x12\x0e\n\x06source\x18\x02 \x01(\t\"H\n#ListconfigsConfigsMaxlocktimeblocks\x12\x11\n\tvalue_int\x18\x01 \x01(\r\x12\x0e\n\x06source\x18\x02 \x01(\t\"F\n!ListconfigsConfigsFundingconfirms\x12\x11\n\tvalue_int\x18\x01 \x01(\r\x12\x0e\n\x06source\x18\x02 \x01(\t\"@\n\x1bListconfigsConfigsCltvdelta\x12\x11\n\tvalue_int\x18\x01 \x01(\r\x12\x0e\n\x06source\x18\x02 \x01(\t\"@\n\x1bListconfigsConfigsCltvfinal\x12\x11\n\tvalue_int\x18\x01 \x01(\r\x12\x0e\n\x06source\x18\x02 \x01(\t\"A\n\x1cListconfigsConfigsCommittime\x12\x11\n\tvalue_int\x18\x01 \x01(\r\x12\x0e\n\x06source\x18\x02 \x01(\t\">\n\x19ListconfigsConfigsFeebase\x12\x11\n\tvalue_int\x18\x01 \x01(\r\x12\x0e\n\x06source\x18\x02 \x01(\t\"=\n\x18ListconfigsConfigsRescan\x12\x11\n\tvalue_int\x18\x01 \x01(\x12\x12\x0e\n\x06source\x18\x02 \x01(\t\"D\n\x1fListconfigsConfigsFeepersatoshi\x12\x11\n\tvalue_int\x18\x01 \x01(\r\x12\x0e\n\x06source\x18\x02 \x01(\t\"I\n$ListconfigsConfigsMaxconcurrenthtlcs\x12\x11\n\tvalue_int\x18\x01 \x01(\r\x12\x0e\n\x06source\x18\x02 \x01(\t\"T\n!ListconfigsConfigsHtlcminimummsat\x12\x1f\n\nvalue_msat\x18\x01 \x01(\x0b\x32\x0b.cln.Amount\x12\x0e\n\x06source\x18\x02 \x01(\t\"T\n!ListconfigsConfigsHtlcmaximummsat\x12\x1f\n\nvalue_msat\x18\x01 \x01(\x0b\x32\x0b.cln.Amount\x12\x0e\n\x06source\x18\x02 \x01(\t\"\\\n)ListconfigsConfigsMaxdusthtlcexposuremsat\x12\x1f\n\nvalue_msat\x18\x01 \x01(\x0b\x32\x0b.cln.Amount\x12\x0e\n\x06source\x18\x02 \x01(\t\"g\n ListconfigsConfigsMincapacitysat\x12\x11\n\tvalue_int\x18\x01 \x01(\x04\x12\x0e\n\x06source\x18\x02 \x01(\t\x12\x14\n\x07\x64ynamic\x18\x03 \x01(\x08H\x00\x88\x01\x01\x42\n\n\x08_dynamic\"=\n\x16ListconfigsConfigsAddr\x12\x12\n\nvalues_str\x18\x01 \x03(\t\x12\x0f\n\x07sources\x18\x02 \x03(\t\"E\n\x1eListconfigsConfigsAnnounceaddr\x12\x12\n\nvalues_str\x18\x01 \x03(\t\x12\x0f\n\x07sources\x18\x02 \x03(\t\"A\n\x1aListconfigsConfigsBindaddr\x12\x12\n\nvalues_str\x18\x01 \x03(\t\x12\x0f\n\x07sources\x18\x02 \x03(\t\"8\n\x19ListconfigsConfigsOffline\x12\x0b\n\x03set\x18\x01 \x01(\x08\x12\x0e\n\x06source\x18\x02 \x01(\t\"B\n\x1cListconfigsConfigsAutolisten\x12\x12\n\nvalue_bool\x18\x01 \x01(\x08\x12\x0e\n\x06source\x18\x02 \x01(\t\"<\n\x17ListconfigsConfigsProxy\x12\x11\n\tvalue_str\x18\x01 \x01(\t\x12\x0e\n\x06source\x18\x02 \x01(\t\";\n\x1cListconfigsConfigsDisabledns\x12\x0b\n\x03set\x18\x01 \x01(\x08\x12\x0e\n\x06source\x18\x02 \x01(\t\"\x80\x02\n(ListconfigsConfigsAnnounceaddrdiscovered\x12q\n\tvalue_str\x18\x01 \x01(\x0e\x32^.cln.ListconfigsConfigsAnnounceaddrdiscovered.ListconfigsConfigsAnnounceaddrdiscoveredValueStr\x12\x0e\n\x06source\x18\x02 \x01(\t\"Q\n0ListconfigsConfigsAnnounceaddrdiscoveredValueStr\x12\x08\n\x04TRUE\x10\x00\x12\t\n\x05\x46\x41LSE\x10\x01\x12\x08\n\x04\x41UTO\x10\x02\"Q\n,ListconfigsConfigsAnnounceaddrdiscoveredport\x12\x11\n\tvalue_int\x18\x01 \x01(\r\x12\x0e\n\x06source\x18\x02 \x01(\t\"=\n\x1eListconfigsConfigsEncryptedhsm\x12\x0b\n\x03set\x18\x01 \x01(\x08\x12\x0e\n\x06source\x18\x02 \x01(\t\"B\n\x1dListconfigsConfigsRpcfilemode\x12\x11\n\tvalue_str\x18\x01 \x01(\t\x12\x0e\n\x06source\x18\x02 \x01(\t\"?\n\x1aListconfigsConfigsLoglevel\x12\x11\n\tvalue_str\x18\x01 \x01(\t\x12\x0e\n\x06source\x18\x02 \x01(\t\"@\n\x1bListconfigsConfigsLogprefix\x12\x11\n\tvalue_str\x18\x01 \x01(\t\x12\x0e\n\x06source\x18\x02 \x01(\t\"@\n\x19ListconfigsConfigsLogfile\x12\x12\n\nvalues_str\x18\x01 \x03(\t\x12\x0f\n\x07sources\x18\x02 \x03(\t\"E\n\x1fListconfigsConfigsLogtimestamps\x12\x12\n\nvalue_bool\x18\x01 \x01(\x08\x12\x0e\n\x06source\x18\x02 \x01(\t\"D\n\x1fListconfigsConfigsForcefeerates\x12\x11\n\tvalue_str\x18\x01 \x01(\t\x12\x0e\n\x06source\x18\x02 \x01(\t\"B\n\x1bListconfigsConfigsSubdaemon\x12\x12\n\nvalues_str\x18\x01 \x03(\t\x12\x0f\n\x07sources\x18\x02 \x03(\t\"f\n\'ListconfigsConfigsFetchinvoicenoconnect\x12\x0b\n\x03set\x18\x01 \x01(\x08\x12\x0e\n\x06source\x18\x02 \x01(\t\x12\x13\n\x06plugin\x18\x03 \x01(\tH\x00\x88\x01\x01\x42\t\n\x07_plugin\"I\n$ListconfigsConfigsTorservicepassword\x12\x11\n\tvalue_str\x18\x01 \x01(\t\x12\x0e\n\x06source\x18\x02 \x01(\t\"G\n!ListconfigsConfigsAnnounceaddrdns\x12\x12\n\nvalue_bool\x18\x01 \x01(\x08\x12\x0e\n\x06source\x18\x02 \x01(\t\"N\n(ListconfigsConfigsRequireconfirmedinputs\x12\x12\n\nvalue_bool\x18\x01 \x01(\x08\x12\x0e\n\x06source\x18\x02 \x01(\t\"@\n\x1bListconfigsConfigsCommitfee\x12\x11\n\tvalue_int\x18\x01 \x01(\x04\x12\x0e\n\x06source\x18\x02 \x01(\t\"J\n%ListconfigsConfigsCommitfeerateoffset\x12\x11\n\tvalue_int\x18\x01 \x01(\r\x12\x0e\n\x06source\x18\x02 \x01(\t\"M\n(ListconfigsConfigsAutoconnectseekerpeers\x12\x11\n\tvalue_int\x18\x01 \x01(\r\x12\x0e\n\x06source\x18\x02 \x01(\t\"\r\n\x0bStopRequest\"q\n\x0cStopResponse\x12\x31\n\x06result\x18\x01 \x01(\x0e\x32\x1c.cln.StopResponse.StopResultH\x00\x88\x01\x01\"#\n\nStopResult\x12\x15\n\x11SHUTDOWN_COMPLETE\x10\x00\x42\t\n\x07_result\"/\n\x0bHelpRequest\x12\x14\n\x07\x63ommand\x18\x01 \x01(\tH\x00\x88\x01\x01\x42\n\n\x08_command\"\x95\x01\n\x0cHelpResponse\x12\x1b\n\x04help\x18\x01 \x03(\x0b\x32\r.cln.HelpHelp\x12:\n\x0b\x66ormat_hint\x18\x02 \x01(\x0e\x32 .cln.HelpResponse.HelpFormathintH\x00\x88\x01\x01\"\x1c\n\x0eHelpFormathint\x12\n\n\x06SIMPLE\x10\x00\x42\x0e\n\x0c_format_hint\"\x1b\n\x08HelpHelp\x12\x0f\n\x07\x63ommand\x18\x01 \x01(\t\"g\n\x18PreapprovekeysendRequest\x12\x13\n\x0b\x64\x65stination\x18\x01 \x01(\x0c\x12\x14\n\x0cpayment_hash\x18\x02 \x01(\x0c\x12 \n\x0b\x61mount_msat\x18\x03 \x01(\x0b\x32\x0b.cln.Amount\"\x1b\n\x19PreapprovekeysendResponse\"*\n\x18PreapproveinvoiceRequest\x12\x0e\n\x06\x62olt11\x18\x01 \x01(\t\"\x1b\n\x19PreapproveinvoiceResponse\"\x15\n\x13StaticbackupRequest\"#\n\x14StaticbackupResponse\x12\x0b\n\x03scb\x18\x01 \x03(\x0c\"d\n\x16\x42kprchannelsapyRequest\x12\x17\n\nstart_time\x18\x01 \x01(\x04H\x00\x88\x01\x01\x12\x15\n\x08\x65nd_time\x18\x02 \x01(\x04H\x01\x88\x01\x01\x42\r\n\x0b_start_timeB\x0b\n\t_end_time\"P\n\x17\x42kprchannelsapyResponse\x12\x35\n\x0c\x63hannels_apy\x18\x01 \x03(\x0b\x32\x1f.cln.BkprchannelsapyChannelsApy\"\xf9\x06\n\x1a\x42kprchannelsapyChannelsApy\x12\x0f\n\x07\x61\x63\x63ount\x18\x01 \x01(\t\x12$\n\x0frouted_out_msat\x18\x02 \x01(\x0b\x32\x0b.cln.Amount\x12#\n\x0erouted_in_msat\x18\x03 \x01(\x0b\x32\x0b.cln.Amount\x12(\n\x13lease_fee_paid_msat\x18\x04 \x01(\x0b\x32\x0b.cln.Amount\x12*\n\x15lease_fee_earned_msat\x18\x05 \x01(\x0b\x32\x0b.cln.Amount\x12$\n\x0fpushed_out_msat\x18\x06 \x01(\x0b\x32\x0b.cln.Amount\x12#\n\x0epushed_in_msat\x18\x07 \x01(\x0b\x32\x0b.cln.Amount\x12+\n\x16our_start_balance_msat\x18\x08 \x01(\x0b\x32\x0b.cln.Amount\x12/\n\x1a\x63hannel_start_balance_msat\x18\t \x01(\x0b\x32\x0b.cln.Amount\x12\"\n\rfees_out_msat\x18\n \x01(\x0b\x32\x0b.cln.Amount\x12&\n\x0c\x66\x65\x65s_in_msat\x18\x0b \x01(\x0b\x32\x0b.cln.AmountH\x00\x88\x01\x01\x12\x17\n\x0futilization_out\x18\x0c \x01(\t\x12$\n\x17utilization_out_initial\x18\r \x01(\tH\x01\x88\x01\x01\x12\x16\n\x0eutilization_in\x18\x0e \x01(\t\x12#\n\x16utilization_in_initial\x18\x0f \x01(\tH\x02\x88\x01\x01\x12\x0f\n\x07\x61py_out\x18\x10 \x01(\t\x12\x1c\n\x0f\x61py_out_initial\x18\x11 \x01(\tH\x03\x88\x01\x01\x12\x0e\n\x06\x61py_in\x18\x12 \x01(\t\x12\x1b\n\x0e\x61py_in_initial\x18\x13 \x01(\tH\x04\x88\x01\x01\x12\x11\n\tapy_total\x18\x14 \x01(\t\x12\x1e\n\x11\x61py_total_initial\x18\x15 \x01(\tH\x05\x88\x01\x01\x12\x16\n\tapy_lease\x18\x16 \x01(\tH\x06\x88\x01\x01\x42\x0f\n\r_fees_in_msatB\x1a\n\x18_utilization_out_initialB\x19\n\x17_utilization_in_initialB\x12\n\x10_apy_out_initialB\x11\n\x0f_apy_in_initialB\x14\n\x12_apy_total_initialB\x0c\n\n_apy_lease\"\xd2\x01\n\x18\x42kprdumpincomecsvRequest\x12\x12\n\ncsv_format\x18\x01 \x01(\t\x12\x15\n\x08\x63sv_file\x18\x02 \x01(\tH\x00\x88\x01\x01\x12\x1d\n\x10\x63onsolidate_fees\x18\x03 \x01(\x08H\x01\x88\x01\x01\x12\x17\n\nstart_time\x18\x04 \x01(\x04H\x02\x88\x01\x01\x12\x15\n\x08\x65nd_time\x18\x05 \x01(\x04H\x03\x88\x01\x01\x42\x0b\n\t_csv_fileB\x13\n\x11_consolidate_feesB\r\n\x0b_start_timeB\x0b\n\t_end_time\"\xd4\x01\n\x19\x42kprdumpincomecsvResponse\x12\x10\n\x08\x63sv_file\x18\x01 \x01(\t\x12M\n\ncsv_format\x18\x02 \x01(\x0e\x32\x39.cln.BkprdumpincomecsvResponse.BkprdumpincomecsvCsvFormat\"V\n\x1a\x42kprdumpincomecsvCsvFormat\x12\x0f\n\x0b\x43OINTRACKER\x10\x00\x12\n\n\x06KOINLY\x10\x01\x12\x0b\n\x07HARMONY\x10\x02\x12\x0e\n\nQUICKBOOKS\x10\x03\"%\n\x12\x42kprinspectRequest\x12\x0f\n\x07\x61\x63\x63ount\x18\x01 \x01(\t\"7\n\x13\x42kprinspectResponse\x12 \n\x03txs\x18\x01 \x03(\x0b\x32\x13.cln.BkprinspectTxs\"\x9a\x01\n\x0e\x42kprinspectTxs\x12\x0c\n\x04txid\x18\x01 \x01(\x0c\x12\x18\n\x0b\x62lockheight\x18\x02 \x01(\rH\x00\x88\x01\x01\x12#\n\x0e\x66\x65\x65s_paid_msat\x18\x03 \x01(\x0b\x32\x0b.cln.Amount\x12+\n\x07outputs\x18\x04 \x03(\x0b\x32\x1a.cln.BkprinspectTxsOutputsB\x0e\n\x0c_blockheight\"\xbc\x03\n\x15\x42kprinspectTxsOutputs\x12\x0f\n\x07\x61\x63\x63ount\x18\x01 \x01(\t\x12\x0e\n\x06outnum\x18\x02 \x01(\r\x12&\n\x11output_value_msat\x18\x03 \x01(\x0b\x32\x0b.cln.Amount\x12\x10\n\x08\x63urrency\x18\x04 \x01(\t\x12%\n\x0b\x63redit_msat\x18\x05 \x01(\x0b\x32\x0b.cln.AmountH\x00\x88\x01\x01\x12$\n\ndebit_msat\x18\x06 \x01(\x0b\x32\x0b.cln.AmountH\x01\x88\x01\x01\x12 \n\x13originating_account\x18\x07 \x01(\tH\x02\x88\x01\x01\x12\x17\n\noutput_tag\x18\x08 \x01(\tH\x03\x88\x01\x01\x12\x16\n\tspend_tag\x18\t \x01(\tH\x04\x88\x01\x01\x12\x1a\n\rspending_txid\x18\n \x01(\x0cH\x05\x88\x01\x01\x12\x17\n\npayment_id\x18\x0b \x01(\x0cH\x06\x88\x01\x01\x42\x0e\n\x0c_credit_msatB\r\n\x0b_debit_msatB\x16\n\x14_originating_accountB\r\n\x0b_output_tagB\x0c\n\n_spend_tagB\x10\n\x0e_spending_txidB\r\n\x0b_payment_id\"h\n\x1c\x42kprlistaccounteventsRequest\x12\x14\n\x07\x61\x63\x63ount\x18\x01 \x01(\tH\x00\x88\x01\x01\x12\x17\n\npayment_id\x18\x02 \x01(\tH\x01\x88\x01\x01\x42\n\n\x08_accountB\r\n\x0b_payment_id\"Q\n\x1d\x42kprlistaccounteventsResponse\x12\x30\n\x06\x65vents\x18\x01 \x03(\x0b\x32 .cln.BkprlistaccounteventsEvents\"\xa1\x05\n\x1b\x42kprlistaccounteventsEvents\x12\x0f\n\x07\x61\x63\x63ount\x18\x01 \x01(\t\x12S\n\titem_type\x18\x02 \x01(\x0e\x32@.cln.BkprlistaccounteventsEvents.BkprlistaccounteventsEventsType\x12\x0b\n\x03tag\x18\x03 \x01(\t\x12 \n\x0b\x63redit_msat\x18\x04 \x01(\x0b\x32\x0b.cln.Amount\x12\x1f\n\ndebit_msat\x18\x05 \x01(\x0b\x32\x0b.cln.Amount\x12\x10\n\x08\x63urrency\x18\x06 \x01(\t\x12\x11\n\ttimestamp\x18\x07 \x01(\r\x12\x15\n\x08outpoint\x18\x08 \x01(\tH\x00\x88\x01\x01\x12\x18\n\x0b\x62lockheight\x18\t \x01(\rH\x01\x88\x01\x01\x12\x13\n\x06origin\x18\n \x01(\tH\x02\x88\x01\x01\x12\x17\n\npayment_id\x18\x0b \x01(\x0cH\x03\x88\x01\x01\x12\x11\n\x04txid\x18\x0c \x01(\x0cH\x04\x88\x01\x01\x12\x18\n\x0b\x64\x65scription\x18\r \x01(\tH\x05\x88\x01\x01\x12#\n\tfees_msat\x18\x0e \x01(\x0b\x32\x0b.cln.AmountH\x06\x88\x01\x01\x12\x19\n\x0cis_rebalance\x18\x0f \x01(\x08H\x07\x88\x01\x01\x12\x14\n\x07part_id\x18\x10 \x01(\rH\x08\x88\x01\x01\"J\n\x1f\x42kprlistaccounteventsEventsType\x12\x0f\n\x0bONCHAIN_FEE\x10\x00\x12\t\n\x05\x43HAIN\x10\x01\x12\x0b\n\x07\x43HANNEL\x10\x02\x42\x0b\n\t_outpointB\x0e\n\x0c_blockheightB\t\n\x07_originB\r\n\x0b_payment_idB\x07\n\x05_txidB\x0e\n\x0c_descriptionB\x0c\n\n_fees_msatB\x0f\n\r_is_rebalanceB\n\n\x08_part_id\"\x19\n\x17\x42kprlistbalancesRequest\"K\n\x18\x42kprlistbalancesResponse\x12/\n\x08\x61\x63\x63ounts\x18\x01 \x03(\x0b\x32\x1d.cln.BkprlistbalancesAccounts\"\xc6\x02\n\x18\x42kprlistbalancesAccounts\x12\x0f\n\x07\x61\x63\x63ount\x18\x01 \x01(\t\x12\x37\n\x08\x62\x61lances\x18\x02 \x03(\x0b\x32%.cln.BkprlistbalancesAccountsBalances\x12\x14\n\x07peer_id\x18\x03 \x01(\x0cH\x00\x88\x01\x01\x12\x16\n\twe_opened\x18\x04 \x01(\x08H\x01\x88\x01\x01\x12\x1b\n\x0e\x61\x63\x63ount_closed\x18\x05 \x01(\x08H\x02\x88\x01\x01\x12\x1d\n\x10\x61\x63\x63ount_resolved\x18\x06 \x01(\x08H\x03\x88\x01\x01\x12\x1e\n\x11resolved_at_block\x18\x07 \x01(\rH\x04\x88\x01\x01\x42\n\n\x08_peer_idB\x0c\n\n_we_openedB\x11\n\x0f_account_closedB\x13\n\x11_account_resolvedB\x14\n\x12_resolved_at_block\"X\n BkprlistbalancesAccountsBalances\x12!\n\x0c\x62\x61lance_msat\x18\x01 \x01(\x0b\x32\x0b.cln.Amount\x12\x11\n\tcoin_type\x18\x02 \x01(\t\"\x97\x01\n\x15\x42kprlistincomeRequest\x12\x1d\n\x10\x63onsolidate_fees\x18\x01 \x01(\x08H\x00\x88\x01\x01\x12\x17\n\nstart_time\x18\x02 \x01(\rH\x01\x88\x01\x01\x12\x15\n\x08\x65nd_time\x18\x03 \x01(\rH\x02\x88\x01\x01\x42\x13\n\x11_consolidate_feesB\r\n\x0b_start_timeB\x0b\n\t_end_time\"P\n\x16\x42kprlistincomeResponse\x12\x36\n\rincome_events\x18\x01 \x03(\x0b\x32\x1f.cln.BkprlistincomeIncomeEvents\"\xb4\x02\n\x1a\x42kprlistincomeIncomeEvents\x12\x0f\n\x07\x61\x63\x63ount\x18\x01 \x01(\t\x12\x0b\n\x03tag\x18\x02 \x01(\t\x12 \n\x0b\x63redit_msat\x18\x03 \x01(\x0b\x32\x0b.cln.Amount\x12\x1f\n\ndebit_msat\x18\x04 \x01(\x0b\x32\x0b.cln.Amount\x12\x10\n\x08\x63urrency\x18\x05 \x01(\t\x12\x11\n\ttimestamp\x18\x06 \x01(\r\x12\x18\n\x0b\x64\x65scription\x18\x07 \x01(\tH\x00\x88\x01\x01\x12\x15\n\x08outpoint\x18\x08 \x01(\tH\x01\x88\x01\x01\x12\x11\n\x04txid\x18\t \x01(\x0cH\x02\x88\x01\x01\x12\x17\n\npayment_id\x18\n \x01(\x0cH\x03\x88\x01\x01\x42\x0e\n\x0c_descriptionB\x0b\n\t_outpointB\x07\n\x05_txidB\r\n\x0b_payment_id\"P\n%BkpreditdescriptionbypaymentidRequest\x12\x12\n\npayment_id\x18\x01 \x01(\t\x12\x13\n\x0b\x64\x65scription\x18\x02 \x01(\t\"e\n&BkpreditdescriptionbypaymentidResponse\x12;\n\x07updated\x18\x01 \x03(\x0b\x32*.cln.BkpreditdescriptionbypaymentidUpdated\"\xa3\x05\n%BkpreditdescriptionbypaymentidUpdated\x12\x0f\n\x07\x61\x63\x63ount\x18\x01 \x01(\t\x12g\n\titem_type\x18\x02 \x01(\x0e\x32T.cln.BkpreditdescriptionbypaymentidUpdated.BkpreditdescriptionbypaymentidUpdatedType\x12\x0b\n\x03tag\x18\x03 \x01(\t\x12 \n\x0b\x63redit_msat\x18\x04 \x01(\x0b\x32\x0b.cln.Amount\x12\x1f\n\ndebit_msat\x18\x05 \x01(\x0b\x32\x0b.cln.Amount\x12\x10\n\x08\x63urrency\x18\x06 \x01(\t\x12\x11\n\ttimestamp\x18\x07 \x01(\r\x12\x13\n\x0b\x64\x65scription\x18\x08 \x01(\t\x12\x15\n\x08outpoint\x18\t \x01(\tH\x00\x88\x01\x01\x12\x18\n\x0b\x62lockheight\x18\n \x01(\rH\x01\x88\x01\x01\x12\x13\n\x06origin\x18\x0b \x01(\tH\x02\x88\x01\x01\x12\x17\n\npayment_id\x18\x0c \x01(\x0cH\x03\x88\x01\x01\x12\x11\n\x04txid\x18\r \x01(\x0cH\x04\x88\x01\x01\x12#\n\tfees_msat\x18\x0e \x01(\x0b\x32\x0b.cln.AmountH\x05\x88\x01\x01\x12\x19\n\x0cis_rebalance\x18\x0f \x01(\x08H\x06\x88\x01\x01\x12\x14\n\x07part_id\x18\x10 \x01(\rH\x07\x88\x01\x01\"C\n)BkpreditdescriptionbypaymentidUpdatedType\x12\t\n\x05\x43HAIN\x10\x00\x12\x0b\n\x07\x43HANNEL\x10\x01\x42\x0b\n\t_outpointB\x0e\n\x0c_blockheightB\t\n\x07_originB\r\n\x0b_payment_idB\x07\n\x05_txidB\x0c\n\n_fees_msatB\x0f\n\r_is_rebalanceB\n\n\x08_part_id\"M\n$BkpreditdescriptionbyoutpointRequest\x12\x10\n\x08outpoint\x18\x01 \x01(\t\x12\x13\n\x0b\x64\x65scription\x18\x02 \x01(\t\"c\n%BkpreditdescriptionbyoutpointResponse\x12:\n\x07updated\x18\x01 \x03(\x0b\x32).cln.BkpreditdescriptionbyoutpointUpdated\"\x9f\x05\n$BkpreditdescriptionbyoutpointUpdated\x12\x0f\n\x07\x61\x63\x63ount\x18\x01 \x01(\t\x12\x65\n\titem_type\x18\x02 \x01(\x0e\x32R.cln.BkpreditdescriptionbyoutpointUpdated.BkpreditdescriptionbyoutpointUpdatedType\x12\x0b\n\x03tag\x18\x03 \x01(\t\x12 \n\x0b\x63redit_msat\x18\x04 \x01(\x0b\x32\x0b.cln.Amount\x12\x1f\n\ndebit_msat\x18\x05 \x01(\x0b\x32\x0b.cln.Amount\x12\x10\n\x08\x63urrency\x18\x06 \x01(\t\x12\x11\n\ttimestamp\x18\x07 \x01(\r\x12\x13\n\x0b\x64\x65scription\x18\x08 \x01(\t\x12\x15\n\x08outpoint\x18\t \x01(\tH\x00\x88\x01\x01\x12\x18\n\x0b\x62lockheight\x18\n \x01(\rH\x01\x88\x01\x01\x12\x13\n\x06origin\x18\x0b \x01(\tH\x02\x88\x01\x01\x12\x17\n\npayment_id\x18\x0c \x01(\x0cH\x03\x88\x01\x01\x12\x11\n\x04txid\x18\r \x01(\x0cH\x04\x88\x01\x01\x12#\n\tfees_msat\x18\x0e \x01(\x0b\x32\x0b.cln.AmountH\x05\x88\x01\x01\x12\x19\n\x0cis_rebalance\x18\x0f \x01(\x08H\x06\x88\x01\x01\x12\x14\n\x07part_id\x18\x10 \x01(\rH\x07\x88\x01\x01\"B\n(BkpreditdescriptionbyoutpointUpdatedType\x12\t\n\x05\x43HAIN\x10\x00\x12\x0b\n\x07\x43HANNEL\x10\x01\x42\x0b\n\t_outpointB\x0e\n\x0c_blockheightB\t\n\x07_originB\r\n\x0b_payment_idB\x07\n\x05_txidB\x0c\n\n_fees_msatB\x0f\n\r_is_rebalanceB\n\n\x08_part_id\"n\n\x14\x42lacklistruneRequest\x12\x12\n\x05start\x18\x01 \x01(\x04H\x00\x88\x01\x01\x12\x10\n\x03\x65nd\x18\x02 \x01(\x04H\x01\x88\x01\x01\x12\x13\n\x06relist\x18\x03 \x01(\x08H\x02\x88\x01\x01\x42\x08\n\x06_startB\x06\n\x04_endB\t\n\x07_relist\"G\n\x15\x42lacklistruneResponse\x12.\n\tblacklist\x18\x01 \x03(\x0b\x32\x1b.cln.BlacklistruneBlacklist\"4\n\x16\x42lacklistruneBlacklist\x12\r\n\x05start\x18\x01 \x01(\x04\x12\x0b\n\x03\x65nd\x18\x02 \x01(\x04\"p\n\x10\x43heckruneRequest\x12\x0c\n\x04rune\x18\x01 \x01(\t\x12\x13\n\x06nodeid\x18\x02 \x01(\tH\x00\x88\x01\x01\x12\x13\n\x06method\x18\x03 \x01(\tH\x01\x88\x01\x01\x12\x0e\n\x06params\x18\x04 \x03(\tB\t\n\x07_nodeidB\t\n\x07_method\"\"\n\x11\x43heckruneResponse\x12\r\n\x05valid\x18\x01 \x01(\x08\"E\n\x11\x43reateruneRequest\x12\x11\n\x04rune\x18\x01 \x01(\tH\x00\x88\x01\x01\x12\x14\n\x0crestrictions\x18\x02 \x03(\tB\x07\n\x05_rune\"{\n\x12\x43reateruneResponse\x12\x0c\n\x04rune\x18\x01 \x01(\t\x12\x11\n\tunique_id\x18\x02 \x01(\t\x12&\n\x19warning_unrestricted_rune\x18\x03 \x01(\tH\x00\x88\x01\x01\x42\x1c\n\x1a_warning_unrestricted_rune\".\n\x10ShowrunesRequest\x12\x11\n\x04rune\x18\x01 \x01(\tH\x00\x88\x01\x01\x42\x07\n\x05_rune\"7\n\x11ShowrunesResponse\x12\"\n\x05runes\x18\x01 \x03(\x0b\x32\x13.cln.ShowrunesRunes\"\x9d\x02\n\x0eShowrunesRunes\x12\x0c\n\x04rune\x18\x01 \x01(\t\x12\x11\n\tunique_id\x18\x02 \x01(\t\x12\x35\n\x0crestrictions\x18\x03 \x03(\x0b\x32\x1f.cln.ShowrunesRunesRestrictions\x12\x1f\n\x17restrictions_as_english\x18\x04 \x01(\t\x12\x13\n\x06stored\x18\x05 \x01(\x08H\x00\x88\x01\x01\x12\x18\n\x0b\x62lacklisted\x18\x06 \x01(\x08H\x01\x88\x01\x01\x12\x16\n\tlast_used\x18\x07 \x01(\x01H\x02\x88\x01\x01\x12\x15\n\x08our_rune\x18\x08 \x01(\x08H\x03\x88\x01\x01\x42\t\n\x07_storedB\x0e\n\x0c_blacklistedB\x0c\n\n_last_usedB\x0b\n\t_our_rune\"p\n\x1aShowrunesRunesRestrictions\x12\x41\n\x0c\x61lternatives\x18\x01 \x03(\x0b\x32+.cln.ShowrunesRunesRestrictionsAlternatives\x12\x0f\n\x07\x65nglish\x18\x02 \x01(\t\"n\n&ShowrunesRunesRestrictionsAlternatives\x12\x11\n\tfieldname\x18\x01 \x01(\t\x12\r\n\x05value\x18\x02 \x01(\t\x12\x11\n\tcondition\x18\x03 \x01(\t\x12\x0f\n\x07\x65nglish\x18\x04 \x01(\t\"B\n\x17\x41skreneunreserveRequest\x12\'\n\x04path\x18\x01 \x03(\x0b\x32\x19.cln.AskreneunreservePath\"\x1a\n\x18\x41skreneunreserveResponse\"t\n\x14\x41skreneunreservePath\x12 \n\x0b\x61mount_msat\x18\x03 \x01(\x0b\x32\x0b.cln.Amount\x12!\n\x14short_channel_id_dir\x18\x04 \x01(\tH\x00\x88\x01\x01\x42\x17\n\x15_short_channel_id_dir\"8\n\x18\x41skrenelistlayersRequest\x12\x12\n\x05layer\x18\x01 \x01(\tH\x00\x88\x01\x01\x42\x08\n\x06_layer\"I\n\x19\x41skrenelistlayersResponse\x12,\n\x06layers\x18\x01 \x03(\x0b\x32\x1c.cln.AskrenelistlayersLayers\"\x81\x03\n\x17\x41skrenelistlayersLayers\x12\r\n\x05layer\x18\x01 \x01(\t\x12\x16\n\x0e\x64isabled_nodes\x18\x02 \x03(\x0c\x12\x45\n\x10\x63reated_channels\x18\x03 \x03(\x0b\x32+.cln.AskrenelistlayersLayersCreatedChannels\x12<\n\x0b\x63onstraints\x18\x04 \x03(\x0b\x32\'.cln.AskrenelistlayersLayersConstraints\x12\x17\n\npersistent\x18\x05 \x01(\x08H\x00\x88\x01\x01\x12\x19\n\x11\x64isabled_channels\x18\x06 \x03(\t\x12\x43\n\x0f\x63hannel_updates\x18\x07 \x03(\x0b\x32*.cln.AskrenelistlayersLayersChannelUpdates\x12\x32\n\x06\x62iases\x18\x08 \x03(\x0b\x32\".cln.AskrenelistlayersLayersBiasesB\r\n\x0b_persistent\"\x8b\x01\n&AskrenelistlayersLayersCreatedChannels\x12\x0e\n\x06source\x18\x01 \x01(\x0c\x12\x13\n\x0b\x64\x65stination\x18\x02 \x01(\x0c\x12\x18\n\x10short_channel_id\x18\x03 \x01(\t\x12\"\n\rcapacity_msat\x18\x04 \x01(\x0b\x32\x0b.cln.Amount\"\xa8\x03\n%AskrenelistlayersLayersChannelUpdates\x12\x1c\n\x14short_channel_id_dir\x18\x01 \x01(\t\x12\x14\n\x07\x65nabled\x18\x02 \x01(\x08H\x00\x88\x01\x01\x12+\n\x11htlc_minimum_msat\x18\x03 \x01(\x0b\x32\x0b.cln.AmountH\x01\x88\x01\x01\x12+\n\x11htlc_maximum_msat\x18\x04 \x01(\x0b\x32\x0b.cln.AmountH\x02\x88\x01\x01\x12\'\n\rfee_base_msat\x18\x05 \x01(\x0b\x32\x0b.cln.AmountH\x03\x88\x01\x01\x12(\n\x1b\x66\x65\x65_proportional_millionths\x18\x06 \x01(\rH\x04\x88\x01\x01\x12\x1e\n\x11\x63ltv_expiry_delta\x18\x07 \x01(\rH\x05\x88\x01\x01\x42\n\n\x08_enabledB\x14\n\x12_htlc_minimum_msatB\x14\n\x12_htlc_maximum_msatB\x10\n\x0e_fee_base_msatB\x1e\n\x1c_fee_proportional_millionthsB\x14\n\x12_cltv_expiry_delta\"\xf8\x01\n\"AskrenelistlayersLayersConstraints\x12&\n\x0cmaximum_msat\x18\x03 \x01(\x0b\x32\x0b.cln.AmountH\x00\x88\x01\x01\x12&\n\x0cminimum_msat\x18\x04 \x01(\x0b\x32\x0b.cln.AmountH\x01\x88\x01\x01\x12!\n\x14short_channel_id_dir\x18\x05 \x01(\tH\x02\x88\x01\x01\x12\x16\n\ttimestamp\x18\x06 \x01(\x04H\x03\x88\x01\x01\x42\x0f\n\r_maximum_msatB\x0f\n\r_minimum_msatB\x17\n\x15_short_channel_id_dirB\x0c\n\n_timestamp\"u\n\x1d\x41skrenelistlayersLayersBiases\x12\x1c\n\x14short_channel_id_dir\x18\x01 \x01(\t\x12\x0c\n\x04\x62ias\x18\x02 \x01(\x12\x12\x18\n\x0b\x64\x65scription\x18\x03 \x01(\tH\x00\x88\x01\x01\x42\x0e\n\x0c_description\"R\n\x19\x41skrenecreatelayerRequest\x12\r\n\x05layer\x18\x01 \x01(\t\x12\x17\n\npersistent\x18\x02 \x01(\x08H\x00\x88\x01\x01\x42\r\n\x0b_persistent\"K\n\x1a\x41skrenecreatelayerResponse\x12-\n\x06layers\x18\x01 \x03(\x0b\x32\x1d.cln.AskrenecreatelayerLayers\"\xf2\x02\n\x18\x41skrenecreatelayerLayers\x12\r\n\x05layer\x18\x01 \x01(\t\x12\x12\n\npersistent\x18\x02 \x01(\x08\x12\x16\n\x0e\x64isabled_nodes\x18\x03 \x03(\x0c\x12\x19\n\x11\x64isabled_channels\x18\x04 \x03(\t\x12\x46\n\x10\x63reated_channels\x18\x05 \x03(\x0b\x32,.cln.AskrenecreatelayerLayersCreatedChannels\x12\x44\n\x0f\x63hannel_updates\x18\x06 \x03(\x0b\x32+.cln.AskrenecreatelayerLayersChannelUpdates\x12=\n\x0b\x63onstraints\x18\x07 \x03(\x0b\x32(.cln.AskrenecreatelayerLayersConstraints\x12\x33\n\x06\x62iases\x18\x08 \x03(\x0b\x32#.cln.AskrenecreatelayerLayersBiases\"\x8c\x01\n\'AskrenecreatelayerLayersCreatedChannels\x12\x0e\n\x06source\x18\x01 \x01(\x0c\x12\x13\n\x0b\x64\x65stination\x18\x02 \x01(\x0c\x12\x18\n\x10short_channel_id\x18\x03 \x01(\t\x12\"\n\rcapacity_msat\x18\x04 \x01(\x0b\x32\x0b.cln.Amount\"\xd1\x02\n&AskrenecreatelayerLayersChannelUpdates\x12+\n\x11htlc_minimum_msat\x18\x01 \x01(\x0b\x32\x0b.cln.AmountH\x00\x88\x01\x01\x12+\n\x11htlc_maximum_msat\x18\x02 \x01(\x0b\x32\x0b.cln.AmountH\x01\x88\x01\x01\x12\'\n\rfee_base_msat\x18\x03 \x01(\x0b\x32\x0b.cln.AmountH\x02\x88\x01\x01\x12(\n\x1b\x66\x65\x65_proportional_millionths\x18\x04 \x01(\rH\x03\x88\x01\x01\x12\x12\n\x05\x64\x65lay\x18\x05 \x01(\rH\x04\x88\x01\x01\x42\x14\n\x12_htlc_minimum_msatB\x14\n\x12_htlc_maximum_msatB\x10\n\x0e_fee_base_msatB\x1e\n\x1c_fee_proportional_millionthsB\x08\n\x06_delay\"\xc4\x01\n#AskrenecreatelayerLayersConstraints\x12\x18\n\x10short_channel_id\x18\x01 \x01(\t\x12\x11\n\tdirection\x18\x02 \x01(\r\x12&\n\x0cmaximum_msat\x18\x03 \x01(\x0b\x32\x0b.cln.AmountH\x00\x88\x01\x01\x12&\n\x0cminimum_msat\x18\x04 \x01(\x0b\x32\x0b.cln.AmountH\x01\x88\x01\x01\x42\x0f\n\r_maximum_msatB\x0f\n\r_minimum_msat\"v\n\x1e\x41skrenecreatelayerLayersBiases\x12\x1c\n\x14short_channel_id_dir\x18\x01 \x01(\t\x12\x0c\n\x04\x62ias\x18\x02 \x01(\x12\x12\x18\n\x0b\x64\x65scription\x18\x03 \x01(\tH\x00\x88\x01\x01\x42\x0e\n\x0c_description\"*\n\x19\x41skreneremovelayerRequest\x12\r\n\x05layer\x18\x01 \x01(\t\"\x1c\n\x1a\x41skreneremovelayerResponse\">\n\x15\x41skrenereserveRequest\x12%\n\x04path\x18\x01 \x03(\x0b\x32\x17.cln.AskrenereservePath\"\x18\n\x16\x41skrenereserveResponse\"r\n\x12\x41skrenereservePath\x12 \n\x0b\x61mount_msat\x18\x03 \x01(\x0b\x32\x0b.cln.Amount\x12!\n\x14short_channel_id_dir\x18\x04 \x01(\tH\x00\x88\x01\x01\x42\x17\n\x15_short_channel_id_dir\"2\n\x11\x41skreneageRequest\x12\r\n\x05layer\x18\x01 \x01(\t\x12\x0e\n\x06\x63utoff\x18\x02 \x01(\x04\"8\n\x12\x41skreneageResponse\x12\r\n\x05layer\x18\x01 \x01(\t\x12\x13\n\x0bnum_removed\x18\x02 \x01(\x04\"\xfb\x01\n\x10GetroutesRequest\x12\x0e\n\x06source\x18\x01 \x01(\x0c\x12\x13\n\x0b\x64\x65stination\x18\x02 \x01(\x0c\x12 \n\x0b\x61mount_msat\x18\x03 \x01(\x0b\x32\x0b.cln.Amount\x12\x0e\n\x06layers\x18\x04 \x03(\t\x12 \n\x0bmaxfee_msat\x18\x05 \x01(\x0b\x32\x0b.cln.Amount\x12\x17\n\nfinal_cltv\x18\x07 \x01(\rH\x00\x88\x01\x01\x12\x15\n\x08maxdelay\x18\x08 \x01(\rH\x01\x88\x01\x01\x12\x15\n\x08maxparts\x18\t \x01(\rH\x02\x88\x01\x01\x42\r\n\x0b_final_cltvB\x0b\n\t_maxdelayB\x0b\n\t_maxparts\"R\n\x11GetroutesResponse\x12\x17\n\x0fprobability_ppm\x18\x01 \x01(\x04\x12$\n\x06routes\x18\x02 \x03(\x0b\x32\x14.cln.GetroutesRoutes\"\x9c\x01\n\x0fGetroutesRoutes\x12\x17\n\x0fprobability_ppm\x18\x01 \x01(\x04\x12 \n\x0b\x61mount_msat\x18\x02 \x01(\x0b\x32\x0b.cln.Amount\x12&\n\x04path\x18\x03 \x03(\x0b\x32\x18.cln.GetroutesRoutesPath\x12\x17\n\nfinal_cltv\x18\x04 \x01(\rH\x00\x88\x01\x01\x42\r\n\x0b_final_cltv\"\x98\x01\n\x13GetroutesRoutesPath\x12 \n\x0b\x61mount_msat\x18\x03 \x01(\x0b\x32\x0b.cln.Amount\x12\x14\n\x0cnext_node_id\x18\x04 \x01(\x0c\x12\r\n\x05\x64\x65lay\x18\x05 \x01(\r\x12!\n\x14short_channel_id_dir\x18\x06 \x01(\tH\x00\x88\x01\x01\x42\x17\n\x15_short_channel_id_dir\"8\n\x19\x41skrenedisablenodeRequest\x12\r\n\x05layer\x18\x01 \x01(\t\x12\x0c\n\x04node\x18\x02 \x01(\x0c\"\x1c\n\x1a\x41skrenedisablenodeResponse\"\xcd\x02\n\x1b\x41skreneinformchannelRequest\x12\r\n\x05layer\x18\x01 \x01(\t\x12!\n\x14short_channel_id_dir\x18\x06 \x01(\tH\x00\x88\x01\x01\x12%\n\x0b\x61mount_msat\x18\x07 \x01(\x0b\x32\x0b.cln.AmountH\x01\x88\x01\x01\x12P\n\x06inform\x18\x08 \x01(\x0e\x32;.cln.AskreneinformchannelRequest.AskreneinformchannelInformH\x02\x88\x01\x01\"O\n\x1a\x41skreneinformchannelInform\x12\x0f\n\x0b\x43ONSTRAINED\x10\x00\x12\x11\n\rUNCONSTRAINED\x10\x01\x12\r\n\tSUCCEEDED\x10\x02\x42\x17\n\x15_short_channel_id_dirB\x0e\n\x0c_amount_msatB\t\n\x07_inform\"Y\n\x1c\x41skreneinformchannelResponse\x12\x39\n\x0b\x63onstraints\x18\x02 \x03(\x0b\x32$.cln.AskreneinformchannelConstraints\"\xd3\x01\n\x1f\x41skreneinformchannelConstraints\x12\x1c\n\x14short_channel_id_dir\x18\x01 \x01(\t\x12\r\n\x05layer\x18\x02 \x01(\t\x12\x11\n\ttimestamp\x18\x03 \x01(\x04\x12&\n\x0cmaximum_msat\x18\x04 \x01(\x0b\x32\x0b.cln.AmountH\x00\x88\x01\x01\x12&\n\x0cminimum_msat\x18\x05 \x01(\x0b\x32\x0b.cln.AmountH\x01\x88\x01\x01\x42\x0f\n\r_maximum_msatB\x0f\n\r_minimum_msat\"\x8f\x01\n\x1b\x41skrenecreatechannelRequest\x12\r\n\x05layer\x18\x01 \x01(\t\x12\x0e\n\x06source\x18\x02 \x01(\x0c\x12\x13\n\x0b\x64\x65stination\x18\x03 \x01(\x0c\x12\x18\n\x10short_channel_id\x18\x04 \x01(\t\x12\"\n\rcapacity_msat\x18\x05 \x01(\x0b\x32\x0b.cln.Amount\"\x1e\n\x1c\x41skrenecreatechannelResponse\"\xad\x03\n\x1b\x41skreneupdatechannelRequest\x12\r\n\x05layer\x18\x01 \x01(\t\x12\x1c\n\x14short_channel_id_dir\x18\x02 \x01(\t\x12\x14\n\x07\x65nabled\x18\x03 \x01(\x08H\x00\x88\x01\x01\x12+\n\x11htlc_minimum_msat\x18\x04 \x01(\x0b\x32\x0b.cln.AmountH\x01\x88\x01\x01\x12+\n\x11htlc_maximum_msat\x18\x05 \x01(\x0b\x32\x0b.cln.AmountH\x02\x88\x01\x01\x12\'\n\rfee_base_msat\x18\x06 \x01(\x0b\x32\x0b.cln.AmountH\x03\x88\x01\x01\x12(\n\x1b\x66\x65\x65_proportional_millionths\x18\x07 \x01(\rH\x04\x88\x01\x01\x12\x1e\n\x11\x63ltv_expiry_delta\x18\x08 \x01(\rH\x05\x88\x01\x01\x42\n\n\x08_enabledB\x14\n\x12_htlc_minimum_msatB\x14\n\x12_htlc_maximum_msatB\x10\n\x0e_fee_base_msatB\x1e\n\x1c_fee_proportional_millionthsB\x14\n\x12_cltv_expiry_delta\"\x1e\n\x1c\x41skreneupdatechannelResponse\"\xa4\x01\n\x19\x41skrenebiaschannelRequest\x12\r\n\x05layer\x18\x01 \x01(\t\x12\x1c\n\x14short_channel_id_dir\x18\x02 \x01(\t\x12\x0c\n\x04\x62ias\x18\x03 \x01(\x12\x12\x18\n\x0b\x64\x65scription\x18\x04 \x01(\tH\x00\x88\x01\x01\x12\x15\n\x08relative\x18\x05 \x01(\x08H\x01\x88\x01\x01\x42\x0e\n\x0c_descriptionB\x0b\n\t_relative\"K\n\x1a\x41skrenebiaschannelResponse\x12-\n\x06\x62iases\x18\x01 \x03(\x0b\x32\x1d.cln.AskrenebiaschannelBiases\"\x7f\n\x18\x41skrenebiaschannelBiases\x12\r\n\x05layer\x18\x01 \x01(\t\x12\x1c\n\x14short_channel_id_dir\x18\x02 \x01(\t\x12\x0c\n\x04\x62ias\x18\x03 \x01(\x12\x12\x18\n\x0b\x64\x65scription\x18\x04 \x01(\tH\x00\x88\x01\x01\x42\x0e\n\x0c_description\" \n\x1e\x41skrenelistreservationsRequest\"a\n\x1f\x41skrenelistreservationsResponse\x12>\n\x0creservations\x18\x01 \x03(\x0b\x32(.cln.AskrenelistreservationsReservations\"\x91\x01\n#AskrenelistreservationsReservations\x12\x1c\n\x14short_channel_id_dir\x18\x01 \x01(\t\x12 \n\x0b\x61mount_msat\x18\x02 \x01(\x0b\x32\x0b.cln.Amount\x12\x16\n\x0e\x61ge_in_seconds\x18\x03 \x01(\x04\x12\x12\n\ncommand_id\x18\x04 \x01(\t\"\xcb\x02\n\x19InjectpaymentonionRequest\x12\r\n\x05onion\x18\x01 \x01(\x0c\x12\x14\n\x0cpayment_hash\x18\x02 \x01(\x0c\x12 \n\x0b\x61mount_msat\x18\x03 \x01(\x0b\x32\x0b.cln.Amount\x12\x13\n\x0b\x63ltv_expiry\x18\x04 \x01(\r\x12\x0e\n\x06partid\x18\x05 \x01(\x04\x12\x0f\n\x07groupid\x18\x06 \x01(\x04\x12\x12\n\x05label\x18\x07 \x01(\tH\x00\x88\x01\x01\x12\x16\n\tinvstring\x18\x08 \x01(\tH\x01\x88\x01\x01\x12\x1a\n\rlocalinvreqid\x18\t \x01(\x0cH\x02\x88\x01\x01\x12*\n\x10\x64\x65stination_msat\x18\n \x01(\x0b\x32\x0b.cln.AmountH\x03\x88\x01\x01\x42\x08\n\x06_labelB\x0c\n\n_invstringB\x10\n\x0e_localinvreqidB\x13\n\x11_destination_msat\"w\n\x1aInjectpaymentonionResponse\x12\x12\n\ncreated_at\x18\x01 \x01(\x04\x12\x14\n\x0c\x63ompleted_at\x18\x02 \x01(\x04\x12\x15\n\rcreated_index\x18\x03 \x01(\x04\x12\x18\n\x10payment_preimage\x18\x04 \x01(\x0c\">\n\x19InjectonionmessageRequest\x12\x10\n\x08path_key\x18\x01 \x01(\x0c\x12\x0f\n\x07message\x18\x02 \x01(\x0c\"\x1c\n\x1aInjectonionmessageResponse\"\x97\x02\n\x0bXpayRequest\x12\x11\n\tinvstring\x18\x01 \x01(\t\x12%\n\x0b\x61mount_msat\x18\x02 \x01(\x0b\x32\x0b.cln.AmountH\x00\x88\x01\x01\x12 \n\x06maxfee\x18\x03 \x01(\x0b\x32\x0b.cln.AmountH\x01\x88\x01\x01\x12\x0e\n\x06layers\x18\x04 \x03(\t\x12\x16\n\tretry_for\x18\x05 \x01(\rH\x02\x88\x01\x01\x12&\n\x0cpartial_msat\x18\x06 \x01(\x0b\x32\x0b.cln.AmountH\x03\x88\x01\x01\x12\x15\n\x08maxdelay\x18\x07 \x01(\rH\x04\x88\x01\x01\x42\x0e\n\x0c_amount_msatB\t\n\x07_maxfeeB\x0c\n\n_retry_forB\x0f\n\r_partial_msatB\x0b\n\t_maxdelay\"\xa1\x01\n\x0cXpayResponse\x12\x18\n\x10payment_preimage\x18\x01 \x01(\x0c\x12\x14\n\x0c\x66\x61iled_parts\x18\x02 \x01(\x04\x12\x18\n\x10successful_parts\x18\x03 \x01(\x04\x12 \n\x0b\x61mount_msat\x18\x04 \x01(\x0b\x32\x0b.cln.Amount\x12%\n\x10\x61mount_sent_msat\x18\x05 \x01(\x0b\x32\x0b.cln.Amount\"=\n\x19SignmessagewithkeyRequest\x12\x0f\n\x07message\x18\x01 \x01(\t\x12\x0f\n\x07\x61\x64\x64ress\x18\x02 \x01(\t\"`\n\x1aSignmessagewithkeyResponse\x12\x0f\n\x07\x61\x64\x64ress\x18\x01 \x01(\t\x12\x0e\n\x06pubkey\x18\x02 \x01(\x0c\x12\x11\n\tsignature\x18\x03 \x01(\x0c\x12\x0e\n\x06\x62\x61se64\x18\x04 \x01(\t\"\x19\n\x17StreamBlockAddedRequest\"6\n\x16\x42lockAddedNotification\x12\x0c\n\x04hash\x18\x01 \x01(\x0c\x12\x0e\n\x06height\x18\x02 \x01(\r\" \n\x1eStreamChannelOpenFailedRequest\"3\n\x1d\x43hannelOpenFailedNotification\x12\x12\n\nchannel_id\x18\x01 \x01(\x0c\"\x1c\n\x1aStreamChannelOpenedRequest\"w\n\x19\x43hannelOpenedNotification\x12\n\n\x02id\x18\x01 \x01(\x0c\x12!\n\x0c\x66unding_msat\x18\x02 \x01(\x0b\x32\x0b.cln.Amount\x12\x14\n\x0c\x66unding_txid\x18\x03 \x01(\x0c\x12\x15\n\rchannel_ready\x18\x04 \x01(\x08\"\x16\n\x14StreamConnectRequest\"\xbe\x01\n\x17PeerConnectNotification\x12\n\n\x02id\x18\x01 \x01(\x0c\x12\x44\n\tdirection\x18\x02 \x01(\x0e\x32\x31.cln.PeerConnectNotification.PeerConnectDirection\x12(\n\x07\x61\x64\x64ress\x18\x03 \x01(\x0b\x32\x17.cln.PeerConnectAddress\"\'\n\x14PeerConnectDirection\x12\x06\n\x02IN\x10\x00\x12\x07\n\x03OUT\x10\x01\"\x9a\x02\n\x12PeerConnectAddress\x12\x41\n\titem_type\x18\x01 \x01(\x0e\x32..cln.PeerConnectAddress.PeerConnectAddressType\x12\x13\n\x06socket\x18\x02 \x01(\tH\x00\x88\x01\x01\x12\x14\n\x07\x61\x64\x64ress\x18\x03 \x01(\tH\x01\x88\x01\x01\x12\x11\n\x04port\x18\x04 \x01(\rH\x02\x88\x01\x01\"c\n\x16PeerConnectAddressType\x12\x10\n\x0cLOCAL_SOCKET\x10\x00\x12\x08\n\x04IPV4\x10\x01\x12\x08\n\x04IPV6\x10\x02\x12\t\n\x05TORV2\x10\x03\x12\t\n\x05TORV3\x10\x04\x12\r\n\tWEBSOCKET\x10\x05\x42\t\n\x07_socketB\n\n\x08_addressB\x07\n\x05_port\"\x18\n\x16StreamCustomMsgRequest\"9\n\x15\x43ustomMsgNotification\x12\x0f\n\x07peer_id\x18\x01 \x01(\x0c\x12\x0f\n\x07payload\x18\x02 \x01(\x0c\"\"\n StreamChannelStateChangedRequest\"\xb0\x03\n\x1f\x43hannelStateChangedNotification\x12\x0f\n\x07peer_id\x18\x01 \x01(\x0c\x12\x12\n\nchannel_id\x18\x02 \x01(\x0c\x12\x1d\n\x10short_channel_id\x18\x03 \x01(\tH\x00\x88\x01\x01\x12\x11\n\ttimestamp\x18\x04 \x01(\t\x12)\n\told_state\x18\x05 \x01(\x0e\x32\x11.cln.ChannelStateH\x01\x88\x01\x01\x12$\n\tnew_state\x18\x06 \x01(\x0e\x32\x11.cln.ChannelState\x12L\n\x05\x63\x61use\x18\x07 \x01(\x0e\x32=.cln.ChannelStateChangedNotification.ChannelStateChangedCause\x12\x0f\n\x07message\x18\x08 \x01(\t\"c\n\x18\x43hannelStateChangedCause\x12\x0b\n\x07UNKNOWN\x10\x00\x12\t\n\x05LOCAL\x10\x01\x12\x08\n\x04USER\x10\x02\x12\n\n\x06REMOTE\x10\x03\x12\x0c\n\x08PROTOCOL\x10\x04\x12\x0b\n\x07ONCHAIN\x10\x05\x42\x13\n\x11_short_channel_idB\x0c\n\n_old_state2\xe9P\n\x04Node\x12\x36\n\x07Getinfo\x12\x13.cln.GetinfoRequest\x1a\x14.cln.GetinfoResponse\"\x00\x12<\n\tListPeers\x12\x15.cln.ListpeersRequest\x1a\x16.cln.ListpeersResponse\"\x00\x12<\n\tListFunds\x12\x15.cln.ListfundsRequest\x1a\x16.cln.ListfundsResponse\"\x00\x12\x36\n\x07SendPay\x12\x13.cln.SendpayRequest\x1a\x14.cln.SendpayResponse\"\x00\x12\x45\n\x0cListChannels\x12\x18.cln.ListchannelsRequest\x1a\x19.cln.ListchannelsResponse\"\x00\x12<\n\tAddGossip\x12\x15.cln.AddgossipRequest\x1a\x16.cln.AddgossipResponse\"\x00\x12H\n\rAddPsbtOutput\x12\x19.cln.AddpsbtoutputRequest\x1a\x1a.cln.AddpsbtoutputResponse\"\x00\x12H\n\rAutoCleanOnce\x12\x19.cln.AutocleanonceRequest\x1a\x1a.cln.AutocleanonceResponse\"\x00\x12N\n\x0f\x41utoCleanStatus\x12\x1b.cln.AutocleanstatusRequest\x1a\x1c.cln.AutocleanstatusResponse\"\x00\x12\x45\n\x0c\x43heckMessage\x12\x18.cln.CheckmessageRequest\x1a\x19.cln.CheckmessageResponse\"\x00\x12\x30\n\x05\x43lose\x12\x11.cln.CloseRequest\x1a\x12.cln.CloseResponse\"\x00\x12:\n\x0b\x43onnectPeer\x12\x13.cln.ConnectRequest\x1a\x14.cln.ConnectResponse\"\x00\x12H\n\rCreateInvoice\x12\x19.cln.CreateinvoiceRequest\x1a\x1a.cln.CreateinvoiceResponse\"\x00\x12<\n\tDatastore\x12\x15.cln.DatastoreRequest\x1a\x16.cln.DatastoreResponse\"\x00\x12K\n\x0e\x44\x61tastoreUsage\x12\x1a.cln.DatastoreusageRequest\x1a\x1b.cln.DatastoreusageResponse\"\x00\x12\x42\n\x0b\x43reateOnion\x12\x17.cln.CreateonionRequest\x1a\x18.cln.CreateonionResponse\"\x00\x12\x45\n\x0c\x44\x65lDatastore\x12\x18.cln.DeldatastoreRequest\x1a\x19.cln.DeldatastoreResponse\"\x00\x12?\n\nDelInvoice\x12\x16.cln.DelinvoiceRequest\x1a\x17.cln.DelinvoiceResponse\"\x00\x12Q\n\x10\x44\x65vForgetChannel\x12\x1c.cln.DevforgetchannelRequest\x1a\x1d.cln.DevforgetchannelResponse\"\x00\x12Q\n\x10\x45mergencyRecover\x12\x1c.cln.EmergencyrecoverRequest\x1a\x1d.cln.EmergencyrecoverResponse\"\x00\x12\x66\n\x17GetEmergencyRecoverData\x12#.cln.GetemergencyrecoverdataRequest\x1a$.cln.GetemergencyrecoverdataResponse\"\x00\x12\x45\n\x0c\x45xposeSecret\x12\x18.cln.ExposesecretRequest\x1a\x19.cln.ExposesecretResponse\"\x00\x12\x36\n\x07Recover\x12\x13.cln.RecoverRequest\x1a\x14.cln.RecoverResponse\"\x00\x12K\n\x0eRecoverChannel\x12\x1a.cln.RecoverchannelRequest\x1a\x1b.cln.RecoverchannelResponse\"\x00\x12\x36\n\x07Invoice\x12\x13.cln.InvoiceRequest\x1a\x14.cln.InvoiceResponse\"\x00\x12Q\n\x14\x43reateInvoiceRequest\x12\x1a.cln.InvoicerequestRequest\x1a\x1b.cln.InvoicerequestResponse\"\x00\x12`\n\x15\x44isableInvoiceRequest\x12!.cln.DisableinvoicerequestRequest\x1a\".cln.DisableinvoicerequestResponse\"\x00\x12Z\n\x13ListInvoiceRequests\x12\x1f.cln.ListinvoicerequestsRequest\x1a .cln.ListinvoicerequestsResponse\"\x00\x12H\n\rListDatastore\x12\x19.cln.ListdatastoreRequest\x1a\x1a.cln.ListdatastoreResponse\"\x00\x12\x45\n\x0cListInvoices\x12\x18.cln.ListinvoicesRequest\x1a\x19.cln.ListinvoicesResponse\"\x00\x12<\n\tSendOnion\x12\x15.cln.SendonionRequest\x1a\x16.cln.SendonionResponse\"\x00\x12\x45\n\x0cListSendPays\x12\x18.cln.ListsendpaysRequest\x1a\x19.cln.ListsendpaysResponse\"\x00\x12Q\n\x10ListTransactions\x12\x1c.cln.ListtransactionsRequest\x1a\x1d.cln.ListtransactionsResponse\"\x00\x12?\n\nMakeSecret\x12\x16.cln.MakesecretRequest\x1a\x17.cln.MakesecretResponse\"\x00\x12*\n\x03Pay\x12\x0f.cln.PayRequest\x1a\x10.cln.PayResponse\"\x00\x12<\n\tListNodes\x12\x15.cln.ListnodesRequest\x1a\x16.cln.ListnodesResponse\"\x00\x12K\n\x0eWaitAnyInvoice\x12\x1a.cln.WaitanyinvoiceRequest\x1a\x1b.cln.WaitanyinvoiceResponse\"\x00\x12\x42\n\x0bWaitInvoice\x12\x17.cln.WaitinvoiceRequest\x1a\x18.cln.WaitinvoiceResponse\"\x00\x12\x42\n\x0bWaitSendPay\x12\x17.cln.WaitsendpayRequest\x1a\x18.cln.WaitsendpayResponse\"\x00\x12\x36\n\x07NewAddr\x12\x13.cln.NewaddrRequest\x1a\x14.cln.NewaddrResponse\"\x00\x12\x39\n\x08Withdraw\x12\x14.cln.WithdrawRequest\x1a\x15.cln.WithdrawResponse\"\x00\x12\x36\n\x07KeySend\x12\x13.cln.KeysendRequest\x1a\x14.cln.KeysendResponse\"\x00\x12\x39\n\x08\x46undPsbt\x12\x14.cln.FundpsbtRequest\x1a\x15.cln.FundpsbtResponse\"\x00\x12\x39\n\x08SendPsbt\x12\x14.cln.SendpsbtRequest\x1a\x15.cln.SendpsbtResponse\"\x00\x12\x39\n\x08SignPsbt\x12\x14.cln.SignpsbtRequest\x1a\x15.cln.SignpsbtResponse\"\x00\x12\x39\n\x08UtxoPsbt\x12\x14.cln.UtxopsbtRequest\x1a\x15.cln.UtxopsbtResponse\"\x00\x12<\n\tTxDiscard\x12\x15.cln.TxdiscardRequest\x1a\x16.cln.TxdiscardResponse\"\x00\x12<\n\tTxPrepare\x12\x15.cln.TxprepareRequest\x1a\x16.cln.TxprepareResponse\"\x00\x12\x33\n\x06TxSend\x12\x12.cln.TxsendRequest\x1a\x13.cln.TxsendResponse\"\x00\x12Q\n\x10ListPeerChannels\x12\x1c.cln.ListpeerchannelsRequest\x1a\x1d.cln.ListpeerchannelsResponse\"\x00\x12W\n\x12ListClosedChannels\x12\x1e.cln.ListclosedchannelsRequest\x1a\x1f.cln.ListclosedchannelsResponse\"\x00\x12<\n\tDecodePay\x12\x15.cln.DecodepayRequest\x1a\x16.cln.DecodepayResponse\"\x00\x12\x33\n\x06\x44\x65\x63ode\x12\x12.cln.DecodeRequest\x1a\x13.cln.DecodeResponse\"\x00\x12\x33\n\x06\x44\x65lPay\x12\x12.cln.DelpayRequest\x1a\x13.cln.DelpayResponse\"\x00\x12?\n\nDelForward\x12\x16.cln.DelforwardRequest\x1a\x17.cln.DelforwardResponse\"\x00\x12\x45\n\x0c\x44isableOffer\x12\x18.cln.DisableofferRequest\x1a\x19.cln.DisableofferResponse\"\x00\x12\x42\n\x0b\x45nableOffer\x12\x17.cln.EnableofferRequest\x1a\x18.cln.EnableofferResponse\"\x00\x12?\n\nDisconnect\x12\x16.cln.DisconnectRequest\x1a\x17.cln.DisconnectResponse\"\x00\x12\x39\n\x08\x46\x65\x65rates\x12\x14.cln.FeeratesRequest\x1a\x15.cln.FeeratesResponse\"\x00\x12\x42\n\x0b\x46\x65tchBip353\x12\x17.cln.Fetchbip353Request\x1a\x18.cln.Fetchbip353Response\"\x00\x12\x45\n\x0c\x46\x65tchInvoice\x12\x18.cln.FetchinvoiceRequest\x1a\x19.cln.FetchinvoiceResponse\"\x00\x12T\n\x11\x46undChannelCancel\x12\x1d.cln.FundchannelCancelRequest\x1a\x1e.cln.FundchannelCancelResponse\"\x00\x12Z\n\x13\x46undChannelComplete\x12\x1f.cln.FundchannelCompleteRequest\x1a .cln.FundchannelCompleteResponse\"\x00\x12\x42\n\x0b\x46undChannel\x12\x17.cln.FundchannelRequest\x1a\x18.cln.FundchannelResponse\"\x00\x12Q\n\x10\x46undChannelStart\x12\x1c.cln.FundchannelStartRequest\x1a\x1d.cln.FundchannelStartResponse\"\x00\x12\x33\n\x06GetLog\x12\x12.cln.GetlogRequest\x1a\x13.cln.GetlogResponse\"\x00\x12\x45\n\x0c\x46underUpdate\x12\x18.cln.FunderupdateRequest\x1a\x19.cln.FunderupdateResponse\"\x00\x12\x39\n\x08GetRoute\x12\x14.cln.GetrouteRequest\x1a\x15.cln.GetrouteResponse\"\x00\x12H\n\rListAddresses\x12\x19.cln.ListaddressesRequest\x1a\x1a.cln.ListaddressesResponse\"\x00\x12\x45\n\x0cListForwards\x12\x18.cln.ListforwardsRequest\x1a\x19.cln.ListforwardsResponse\"\x00\x12?\n\nListOffers\x12\x16.cln.ListoffersRequest\x1a\x17.cln.ListoffersResponse\"\x00\x12\x39\n\x08ListPays\x12\x14.cln.ListpaysRequest\x1a\x15.cln.ListpaysResponse\"\x00\x12<\n\tListHtlcs\x12\x15.cln.ListhtlcsRequest\x1a\x16.cln.ListhtlcsResponse\"\x00\x12Q\n\x10MultiFundChannel\x12\x1c.cln.MultifundchannelRequest\x1a\x1d.cln.MultifundchannelResponse\"\x00\x12H\n\rMultiWithdraw\x12\x19.cln.MultiwithdrawRequest\x1a\x1a.cln.MultiwithdrawResponse\"\x00\x12\x30\n\x05Offer\x12\x11.cln.OfferRequest\x1a\x12.cln.OfferResponse\"\x00\x12Q\n\x10OpenChannelAbort\x12\x1c.cln.OpenchannelAbortRequest\x1a\x1d.cln.OpenchannelAbortResponse\"\x00\x12N\n\x0fOpenChannelBump\x12\x1b.cln.OpenchannelBumpRequest\x1a\x1c.cln.OpenchannelBumpResponse\"\x00\x12N\n\x0fOpenChannelInit\x12\x1b.cln.OpenchannelInitRequest\x1a\x1c.cln.OpenchannelInitResponse\"\x00\x12T\n\x11OpenChannelSigned\x12\x1d.cln.OpenchannelSignedRequest\x1a\x1e.cln.OpenchannelSignedResponse\"\x00\x12T\n\x11OpenChannelUpdate\x12\x1d.cln.OpenchannelUpdateRequest\x1a\x1e.cln.OpenchannelUpdateResponse\"\x00\x12-\n\x04Ping\x12\x10.cln.PingRequest\x1a\x11.cln.PingResponse\"\x00\x12\x33\n\x06Plugin\x12\x12.cln.PluginRequest\x1a\x13.cln.PluginResponse\"\x00\x12H\n\rRenePayStatus\x12\x19.cln.RenepaystatusRequest\x1a\x1a.cln.RenepaystatusResponse\"\x00\x12\x36\n\x07RenePay\x12\x13.cln.RenepayRequest\x1a\x14.cln.RenepayResponse\"\x00\x12H\n\rReserveInputs\x12\x19.cln.ReserveinputsRequest\x1a\x1a.cln.ReserveinputsResponse\"\x00\x12H\n\rSendCustomMsg\x12\x19.cln.SendcustommsgRequest\x1a\x1a.cln.SendcustommsgResponse\"\x00\x12\x42\n\x0bSendInvoice\x12\x17.cln.SendinvoiceRequest\x1a\x18.cln.SendinvoiceResponse\"\x00\x12?\n\nSetChannel\x12\x16.cln.SetchannelRequest\x1a\x17.cln.SetchannelResponse\"\x00\x12<\n\tSetConfig\x12\x15.cln.SetconfigRequest\x1a\x16.cln.SetconfigResponse\"\x00\x12K\n\x0eSetPsbtVersion\x12\x1a.cln.SetpsbtversionRequest\x1a\x1b.cln.SetpsbtversionResponse\"\x00\x12\x42\n\x0bSignInvoice\x12\x17.cln.SigninvoiceRequest\x1a\x18.cln.SigninvoiceResponse\"\x00\x12\x42\n\x0bSignMessage\x12\x17.cln.SignmessageRequest\x1a\x18.cln.SignmessageResponse\"\x00\x12?\n\nSpliceInit\x12\x16.cln.SpliceInitRequest\x1a\x17.cln.SpliceInitResponse\"\x00\x12\x45\n\x0cSpliceSigned\x12\x18.cln.SpliceSignedRequest\x1a\x19.cln.SpliceSignedResponse\"\x00\x12\x45\n\x0cSpliceUpdate\x12\x18.cln.SpliceUpdateRequest\x1a\x19.cln.SpliceUpdateResponse\"\x00\x12<\n\tDevSplice\x12\x15.cln.DevspliceRequest\x1a\x16.cln.DevspliceResponse\"\x00\x12N\n\x0fUnreserveInputs\x12\x1b.cln.UnreserveinputsRequest\x1a\x1c.cln.UnreserveinputsResponse\"\x00\x12H\n\rUpgradeWallet\x12\x19.cln.UpgradewalletRequest\x1a\x1a.cln.UpgradewalletResponse\"\x00\x12N\n\x0fWaitBlockHeight\x12\x1b.cln.WaitblockheightRequest\x1a\x1c.cln.WaitblockheightResponse\"\x00\x12-\n\x04Wait\x12\x10.cln.WaitRequest\x1a\x11.cln.WaitResponse\"\x00\x12\x42\n\x0bListConfigs\x12\x17.cln.ListconfigsRequest\x1a\x18.cln.ListconfigsResponse\"\x00\x12-\n\x04Stop\x12\x10.cln.StopRequest\x1a\x11.cln.StopResponse\"\x00\x12-\n\x04Help\x12\x10.cln.HelpRequest\x1a\x11.cln.HelpResponse\"\x00\x12T\n\x11PreApproveKeysend\x12\x1d.cln.PreapprovekeysendRequest\x1a\x1e.cln.PreapprovekeysendResponse\"\x00\x12T\n\x11PreApproveInvoice\x12\x1d.cln.PreapproveinvoiceRequest\x1a\x1e.cln.PreapproveinvoiceResponse\"\x00\x12\x45\n\x0cStaticBackup\x12\x18.cln.StaticbackupRequest\x1a\x19.cln.StaticbackupResponse\"\x00\x12N\n\x0f\x42kprChannelsApy\x12\x1b.cln.BkprchannelsapyRequest\x1a\x1c.cln.BkprchannelsapyResponse\"\x00\x12T\n\x11\x42kprDumpIncomeCsv\x12\x1d.cln.BkprdumpincomecsvRequest\x1a\x1e.cln.BkprdumpincomecsvResponse\"\x00\x12\x42\n\x0b\x42kprInspect\x12\x17.cln.BkprinspectRequest\x1a\x18.cln.BkprinspectResponse\"\x00\x12`\n\x15\x42kprListAccountEvents\x12!.cln.BkprlistaccounteventsRequest\x1a\".cln.BkprlistaccounteventsResponse\"\x00\x12Q\n\x10\x42kprListBalances\x12\x1c.cln.BkprlistbalancesRequest\x1a\x1d.cln.BkprlistbalancesResponse\"\x00\x12K\n\x0e\x42kprListIncome\x12\x1a.cln.BkprlistincomeRequest\x1a\x1b.cln.BkprlistincomeResponse\"\x00\x12{\n\x1e\x42kprEditDescriptionByPaymentId\x12*.cln.BkpreditdescriptionbypaymentidRequest\x1a+.cln.BkpreditdescriptionbypaymentidResponse\"\x00\x12x\n\x1d\x42kprEditDescriptionByOutpoint\x12).cln.BkpreditdescriptionbyoutpointRequest\x1a*.cln.BkpreditdescriptionbyoutpointResponse\"\x00\x12H\n\rBlacklistRune\x12\x19.cln.BlacklistruneRequest\x1a\x1a.cln.BlacklistruneResponse\"\x00\x12<\n\tCheckRune\x12\x15.cln.CheckruneRequest\x1a\x16.cln.CheckruneResponse\"\x00\x12?\n\nCreateRune\x12\x16.cln.CreateruneRequest\x1a\x17.cln.CreateruneResponse\"\x00\x12<\n\tShowRunes\x12\x15.cln.ShowrunesRequest\x1a\x16.cln.ShowrunesResponse\"\x00\x12Q\n\x10\x41skReneUnreserve\x12\x1c.cln.AskreneunreserveRequest\x1a\x1d.cln.AskreneunreserveResponse\"\x00\x12T\n\x11\x41skReneListLayers\x12\x1d.cln.AskrenelistlayersRequest\x1a\x1e.cln.AskrenelistlayersResponse\"\x00\x12W\n\x12\x41skReneCreateLayer\x12\x1e.cln.AskrenecreatelayerRequest\x1a\x1f.cln.AskrenecreatelayerResponse\"\x00\x12W\n\x12\x41skReneRemoveLayer\x12\x1e.cln.AskreneremovelayerRequest\x1a\x1f.cln.AskreneremovelayerResponse\"\x00\x12K\n\x0e\x41skReneReserve\x12\x1a.cln.AskrenereserveRequest\x1a\x1b.cln.AskrenereserveResponse\"\x00\x12?\n\nAskReneAge\x12\x16.cln.AskreneageRequest\x1a\x17.cln.AskreneageResponse\"\x00\x12<\n\tGetRoutes\x12\x15.cln.GetroutesRequest\x1a\x16.cln.GetroutesResponse\"\x00\x12W\n\x12\x41skReneDisableNode\x12\x1e.cln.AskrenedisablenodeRequest\x1a\x1f.cln.AskrenedisablenodeResponse\"\x00\x12]\n\x14\x41skReneInformChannel\x12 .cln.AskreneinformchannelRequest\x1a!.cln.AskreneinformchannelResponse\"\x00\x12]\n\x14\x41skReneCreateChannel\x12 .cln.AskrenecreatechannelRequest\x1a!.cln.AskrenecreatechannelResponse\"\x00\x12]\n\x14\x41skReneUpdateChannel\x12 .cln.AskreneupdatechannelRequest\x1a!.cln.AskreneupdatechannelResponse\"\x00\x12W\n\x12\x41skReneBiasChannel\x12\x1e.cln.AskrenebiaschannelRequest\x1a\x1f.cln.AskrenebiaschannelResponse\"\x00\x12\x66\n\x17\x41skReneListReservations\x12#.cln.AskrenelistreservationsRequest\x1a$.cln.AskrenelistreservationsResponse\"\x00\x12W\n\x12InjectPaymentOnion\x12\x1e.cln.InjectpaymentonionRequest\x1a\x1f.cln.InjectpaymentonionResponse\"\x00\x12W\n\x12InjectOnionMessage\x12\x1e.cln.InjectonionmessageRequest\x1a\x1f.cln.InjectonionmessageResponse\"\x00\x12-\n\x04Xpay\x12\x10.cln.XpayRequest\x1a\x11.cln.XpayResponse\"\x00\x12W\n\x12SignMessageWithKey\x12\x1e.cln.SignmessagewithkeyRequest\x1a\x1f.cln.SignmessagewithkeyResponse\"\x00\x12T\n\x13SubscribeBlockAdded\x12\x1c.cln.StreamBlockAddedRequest\x1a\x1b.cln.BlockAddedNotification\"\x00\x30\x01\x12i\n\x1aSubscribeChannelOpenFailed\x12#.cln.StreamChannelOpenFailedRequest\x1a\".cln.ChannelOpenFailedNotification\"\x00\x30\x01\x12]\n\x16SubscribeChannelOpened\x12\x1f.cln.StreamChannelOpenedRequest\x1a\x1e.cln.ChannelOpenedNotification\"\x00\x30\x01\x12O\n\x10SubscribeConnect\x12\x19.cln.StreamConnectRequest\x1a\x1c.cln.PeerConnectNotification\"\x00\x30\x01\x12Q\n\x12SubscribeCustomMsg\x12\x1b.cln.StreamCustomMsgRequest\x1a\x1a.cln.CustomMsgNotification\"\x00\x30\x01\x12o\n\x1cSubscribeChannelStateChanged\x12%.cln.StreamChannelStateChangedRequest\x1a$.cln.ChannelStateChangedNotification\"\x00\x30\x01\x62\x06proto3') _globals = globals() _builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals) @@ -737,431 +737,435 @@ _globals['_WAITBLOCKHEIGHTRESPONSE']._serialized_start=64925 _globals['_WAITBLOCKHEIGHTRESPONSE']._serialized_end=64971 _globals['_WAITREQUEST']._serialized_start=64974 - _globals['_WAITREQUEST']._serialized_end=65234 + _globals['_WAITREQUEST']._serialized_end=65268 _globals['_WAITREQUEST_WAITSUBSYSTEM']._serialized_start=65110 - _globals['_WAITREQUEST_WAITSUBSYSTEM']._serialized_end=65178 - _globals['_WAITREQUEST_WAITINDEXNAME']._serialized_start=65180 - _globals['_WAITREQUEST_WAITINDEXNAME']._serialized_end=65234 - _globals['_WAITRESPONSE']._serialized_start=65237 - _globals['_WAITRESPONSE']._serialized_end=65738 + _globals['_WAITREQUEST_WAITSUBSYSTEM']._serialized_end=65212 + _globals['_WAITREQUEST_WAITINDEXNAME']._serialized_start=65214 + _globals['_WAITREQUEST_WAITINDEXNAME']._serialized_end=65268 + _globals['_WAITRESPONSE']._serialized_start=65271 + _globals['_WAITRESPONSE']._serialized_end=65934 _globals['_WAITRESPONSE_WAITSUBSYSTEM']._serialized_start=65110 - _globals['_WAITRESPONSE_WAITSUBSYSTEM']._serialized_end=65178 - _globals['_WAITFORWARDS']._serialized_start=65741 - _globals['_WAITFORWARDS']._serialized_end=66072 - _globals['_WAITFORWARDS_WAITFORWARDSSTATUS']._serialized_start=65927 - _globals['_WAITFORWARDS_WAITFORWARDSSTATUS']._serialized_end=66003 - _globals['_WAITINVOICES']._serialized_start=66075 - _globals['_WAITINVOICES']._serialized_end=66352 - _globals['_WAITINVOICES_WAITINVOICESSTATUS']._serialized_start=66238 - _globals['_WAITINVOICES_WAITINVOICESSTATUS']._serialized_end=66293 - _globals['_WAITSENDPAYS']._serialized_start=66355 - _globals['_WAITSENDPAYS']._serialized_end=66610 - _globals['_WAITSENDPAYS_WAITSENDPAYSSTATUS']._serialized_start=66500 - _globals['_WAITSENDPAYS_WAITSENDPAYSSTATUS']._serialized_end=66559 - _globals['_WAITHTLCS']._serialized_start=66613 - _globals['_WAITHTLCS']._serialized_end=67549 - _globals['_WAITHTLCS_WAITHTLCSSTATE']._serialized_start=66882 - _globals['_WAITHTLCS_WAITHTLCSSTATE']._serialized_end=67404 - _globals['_WAITHTLCS_WAITHTLCSDIRECTION']._serialized_start=67406 - _globals['_WAITHTLCS_WAITHTLCSDIRECTION']._serialized_end=67443 - _globals['_WAITDETAILS']._serialized_start=67552 - _globals['_WAITDETAILS']._serialized_end=68188 - _globals['_WAITDETAILS_WAITDETAILSSTATUS']._serialized_start=67894 - _globals['_WAITDETAILS_WAITDETAILSSTATUS']._serialized_end=68031 - _globals['_LISTCONFIGSREQUEST']._serialized_start=68190 - _globals['_LISTCONFIGSREQUEST']._serialized_end=68242 - _globals['_LISTCONFIGSRESPONSE']._serialized_start=68244 - _globals['_LISTCONFIGSRESPONSE']._serialized_end=68324 - _globals['_LISTCONFIGSCONFIGS']._serialized_start=68327 - _globals['_LISTCONFIGSCONFIGS']._serialized_end=74320 - _globals['_LISTCONFIGSCONFIGSCONF']._serialized_start=74323 - _globals['_LISTCONFIGSCONFIGSCONF']._serialized_end=74485 - _globals['_LISTCONFIGSCONFIGSCONF_LISTCONFIGSCONFIGSCONFSOURCE']._serialized_start=74442 - _globals['_LISTCONFIGSCONFIGSCONF_LISTCONFIGSCONFIGSCONFSOURCE']._serialized_end=74485 - _globals['_LISTCONFIGSCONFIGSDEVELOPER']._serialized_start=74487 - _globals['_LISTCONFIGSCONFIGSDEVELOPER']._serialized_end=74545 - _globals['_LISTCONFIGSCONFIGSCLEARPLUGINS']._serialized_start=74547 - _globals['_LISTCONFIGSCONFIGSCLEARPLUGINS']._serialized_end=74608 - _globals['_LISTCONFIGSCONFIGSDISABLEMPP']._serialized_start=74610 - _globals['_LISTCONFIGSCONFIGSDISABLEMPP']._serialized_end=74701 - _globals['_LISTCONFIGSCONFIGSMAINNET']._serialized_start=74703 - _globals['_LISTCONFIGSCONFIGSMAINNET']._serialized_end=74759 - _globals['_LISTCONFIGSCONFIGSREGTEST']._serialized_start=74761 - _globals['_LISTCONFIGSCONFIGSREGTEST']._serialized_end=74817 - _globals['_LISTCONFIGSCONFIGSSIGNET']._serialized_start=74819 - _globals['_LISTCONFIGSCONFIGSSIGNET']._serialized_end=74874 - _globals['_LISTCONFIGSCONFIGSTESTNET']._serialized_start=74876 - _globals['_LISTCONFIGSCONFIGSTESTNET']._serialized_end=74932 - _globals['_LISTCONFIGSCONFIGSIMPORTANTPLUGIN']._serialized_start=74934 - _globals['_LISTCONFIGSCONFIGSIMPORTANTPLUGIN']._serialized_end=75006 - _globals['_LISTCONFIGSCONFIGSPLUGIN']._serialized_start=75008 - _globals['_LISTCONFIGSCONFIGSPLUGIN']._serialized_end=75071 - _globals['_LISTCONFIGSCONFIGSPLUGINDIR']._serialized_start=75073 - _globals['_LISTCONFIGSCONFIGSPLUGINDIR']._serialized_end=75139 - _globals['_LISTCONFIGSCONFIGSLIGHTNINGDIR']._serialized_start=75141 - _globals['_LISTCONFIGSCONFIGSLIGHTNINGDIR']._serialized_end=75208 - _globals['_LISTCONFIGSCONFIGSNETWORK']._serialized_start=75210 - _globals['_LISTCONFIGSCONFIGSNETWORK']._serialized_end=75272 - _globals['_LISTCONFIGSCONFIGSALLOWDEPRECATEDAPIS']._serialized_start=75274 - _globals['_LISTCONFIGSCONFIGSALLOWDEPRECATEDAPIS']._serialized_end=75349 - _globals['_LISTCONFIGSCONFIGSRPCFILE']._serialized_start=75351 - _globals['_LISTCONFIGSCONFIGSRPCFILE']._serialized_end=75413 - _globals['_LISTCONFIGSCONFIGSDISABLEPLUGIN']._serialized_start=75415 - _globals['_LISTCONFIGSCONFIGSDISABLEPLUGIN']._serialized_end=75485 - _globals['_LISTCONFIGSCONFIGSALWAYSUSEPROXY']._serialized_start=75487 - _globals['_LISTCONFIGSCONFIGSALWAYSUSEPROXY']._serialized_end=75557 - _globals['_LISTCONFIGSCONFIGSDAEMON']._serialized_start=75559 - _globals['_LISTCONFIGSCONFIGSDAEMON']._serialized_end=75614 - _globals['_LISTCONFIGSCONFIGSWALLET']._serialized_start=75616 - _globals['_LISTCONFIGSCONFIGSWALLET']._serialized_end=75677 - _globals['_LISTCONFIGSCONFIGSLARGECHANNELS']._serialized_start=75679 - _globals['_LISTCONFIGSCONFIGSLARGECHANNELS']._serialized_end=75741 - _globals['_LISTCONFIGSCONFIGSEXPERIMENTALDUALFUND']._serialized_start=75743 - _globals['_LISTCONFIGSCONFIGSEXPERIMENTALDUALFUND']._serialized_end=75812 - _globals['_LISTCONFIGSCONFIGSEXPERIMENTALSPLICING']._serialized_start=75814 - _globals['_LISTCONFIGSCONFIGSEXPERIMENTALSPLICING']._serialized_end=75883 - _globals['_LISTCONFIGSCONFIGSEXPERIMENTALONIONMESSAGES']._serialized_start=75885 - _globals['_LISTCONFIGSCONFIGSEXPERIMENTALONIONMESSAGES']._serialized_end=75959 - _globals['_LISTCONFIGSCONFIGSEXPERIMENTALOFFERS']._serialized_start=75961 - _globals['_LISTCONFIGSCONFIGSEXPERIMENTALOFFERS']._serialized_end=76028 - _globals['_LISTCONFIGSCONFIGSEXPERIMENTALSHUTDOWNWRONGFUNDING']._serialized_start=76030 - _globals['_LISTCONFIGSCONFIGSEXPERIMENTALSHUTDOWNWRONGFUNDING']._serialized_end=76111 - _globals['_LISTCONFIGSCONFIGSEXPERIMENTALPEERSTORAGE']._serialized_start=76113 - _globals['_LISTCONFIGSCONFIGSEXPERIMENTALPEERSTORAGE']._serialized_end=76185 - _globals['_LISTCONFIGSCONFIGSEXPERIMENTALANCHORS']._serialized_start=76187 - _globals['_LISTCONFIGSCONFIGSEXPERIMENTALANCHORS']._serialized_end=76255 - _globals['_LISTCONFIGSCONFIGSDATABASEUPGRADE']._serialized_start=76257 - _globals['_LISTCONFIGSCONFIGSDATABASEUPGRADE']._serialized_end=76328 - _globals['_LISTCONFIGSCONFIGSRGB']._serialized_start=76330 - _globals['_LISTCONFIGSCONFIGSRGB']._serialized_end=76388 - _globals['_LISTCONFIGSCONFIGSALIAS']._serialized_start=76390 - _globals['_LISTCONFIGSCONFIGSALIAS']._serialized_end=76450 - _globals['_LISTCONFIGSCONFIGSPIDFILE']._serialized_start=76452 - _globals['_LISTCONFIGSCONFIGSPIDFILE']._serialized_end=76514 - _globals['_LISTCONFIGSCONFIGSIGNOREFEELIMITS']._serialized_start=76516 - _globals['_LISTCONFIGSCONFIGSIGNOREFEELIMITS']._serialized_end=76587 - _globals['_LISTCONFIGSCONFIGSWATCHTIMEBLOCKS']._serialized_start=76589 - _globals['_LISTCONFIGSCONFIGSWATCHTIMEBLOCKS']._serialized_end=76659 - _globals['_LISTCONFIGSCONFIGSMAXLOCKTIMEBLOCKS']._serialized_start=76661 - _globals['_LISTCONFIGSCONFIGSMAXLOCKTIMEBLOCKS']._serialized_end=76733 - _globals['_LISTCONFIGSCONFIGSFUNDINGCONFIRMS']._serialized_start=76735 - _globals['_LISTCONFIGSCONFIGSFUNDINGCONFIRMS']._serialized_end=76805 - _globals['_LISTCONFIGSCONFIGSCLTVDELTA']._serialized_start=76807 - _globals['_LISTCONFIGSCONFIGSCLTVDELTA']._serialized_end=76871 - _globals['_LISTCONFIGSCONFIGSCLTVFINAL']._serialized_start=76873 - _globals['_LISTCONFIGSCONFIGSCLTVFINAL']._serialized_end=76937 - _globals['_LISTCONFIGSCONFIGSCOMMITTIME']._serialized_start=76939 - _globals['_LISTCONFIGSCONFIGSCOMMITTIME']._serialized_end=77004 - _globals['_LISTCONFIGSCONFIGSFEEBASE']._serialized_start=77006 - _globals['_LISTCONFIGSCONFIGSFEEBASE']._serialized_end=77068 - _globals['_LISTCONFIGSCONFIGSRESCAN']._serialized_start=77070 - _globals['_LISTCONFIGSCONFIGSRESCAN']._serialized_end=77131 - _globals['_LISTCONFIGSCONFIGSFEEPERSATOSHI']._serialized_start=77133 - _globals['_LISTCONFIGSCONFIGSFEEPERSATOSHI']._serialized_end=77201 - _globals['_LISTCONFIGSCONFIGSMAXCONCURRENTHTLCS']._serialized_start=77203 - _globals['_LISTCONFIGSCONFIGSMAXCONCURRENTHTLCS']._serialized_end=77276 - _globals['_LISTCONFIGSCONFIGSHTLCMINIMUMMSAT']._serialized_start=77278 - _globals['_LISTCONFIGSCONFIGSHTLCMINIMUMMSAT']._serialized_end=77362 - _globals['_LISTCONFIGSCONFIGSHTLCMAXIMUMMSAT']._serialized_start=77364 - _globals['_LISTCONFIGSCONFIGSHTLCMAXIMUMMSAT']._serialized_end=77448 - _globals['_LISTCONFIGSCONFIGSMAXDUSTHTLCEXPOSUREMSAT']._serialized_start=77450 - _globals['_LISTCONFIGSCONFIGSMAXDUSTHTLCEXPOSUREMSAT']._serialized_end=77542 - _globals['_LISTCONFIGSCONFIGSMINCAPACITYSAT']._serialized_start=77544 - _globals['_LISTCONFIGSCONFIGSMINCAPACITYSAT']._serialized_end=77647 - _globals['_LISTCONFIGSCONFIGSADDR']._serialized_start=77649 - _globals['_LISTCONFIGSCONFIGSADDR']._serialized_end=77710 - _globals['_LISTCONFIGSCONFIGSANNOUNCEADDR']._serialized_start=77712 - _globals['_LISTCONFIGSCONFIGSANNOUNCEADDR']._serialized_end=77781 - _globals['_LISTCONFIGSCONFIGSBINDADDR']._serialized_start=77783 - _globals['_LISTCONFIGSCONFIGSBINDADDR']._serialized_end=77848 - _globals['_LISTCONFIGSCONFIGSOFFLINE']._serialized_start=77850 - _globals['_LISTCONFIGSCONFIGSOFFLINE']._serialized_end=77906 - _globals['_LISTCONFIGSCONFIGSAUTOLISTEN']._serialized_start=77908 - _globals['_LISTCONFIGSCONFIGSAUTOLISTEN']._serialized_end=77974 - _globals['_LISTCONFIGSCONFIGSPROXY']._serialized_start=77976 - _globals['_LISTCONFIGSCONFIGSPROXY']._serialized_end=78036 - _globals['_LISTCONFIGSCONFIGSDISABLEDNS']._serialized_start=78038 - _globals['_LISTCONFIGSCONFIGSDISABLEDNS']._serialized_end=78097 - _globals['_LISTCONFIGSCONFIGSANNOUNCEADDRDISCOVERED']._serialized_start=78100 - _globals['_LISTCONFIGSCONFIGSANNOUNCEADDRDISCOVERED']._serialized_end=78356 - _globals['_LISTCONFIGSCONFIGSANNOUNCEADDRDISCOVERED_LISTCONFIGSCONFIGSANNOUNCEADDRDISCOVEREDVALUESTR']._serialized_start=78275 - _globals['_LISTCONFIGSCONFIGSANNOUNCEADDRDISCOVERED_LISTCONFIGSCONFIGSANNOUNCEADDRDISCOVEREDVALUESTR']._serialized_end=78356 - _globals['_LISTCONFIGSCONFIGSANNOUNCEADDRDISCOVEREDPORT']._serialized_start=78358 - _globals['_LISTCONFIGSCONFIGSANNOUNCEADDRDISCOVEREDPORT']._serialized_end=78439 - _globals['_LISTCONFIGSCONFIGSENCRYPTEDHSM']._serialized_start=78441 - _globals['_LISTCONFIGSCONFIGSENCRYPTEDHSM']._serialized_end=78502 - _globals['_LISTCONFIGSCONFIGSRPCFILEMODE']._serialized_start=78504 - _globals['_LISTCONFIGSCONFIGSRPCFILEMODE']._serialized_end=78570 - _globals['_LISTCONFIGSCONFIGSLOGLEVEL']._serialized_start=78572 - _globals['_LISTCONFIGSCONFIGSLOGLEVEL']._serialized_end=78635 - _globals['_LISTCONFIGSCONFIGSLOGPREFIX']._serialized_start=78637 - _globals['_LISTCONFIGSCONFIGSLOGPREFIX']._serialized_end=78701 - _globals['_LISTCONFIGSCONFIGSLOGFILE']._serialized_start=78703 - _globals['_LISTCONFIGSCONFIGSLOGFILE']._serialized_end=78767 - _globals['_LISTCONFIGSCONFIGSLOGTIMESTAMPS']._serialized_start=78769 - _globals['_LISTCONFIGSCONFIGSLOGTIMESTAMPS']._serialized_end=78838 - _globals['_LISTCONFIGSCONFIGSFORCEFEERATES']._serialized_start=78840 - _globals['_LISTCONFIGSCONFIGSFORCEFEERATES']._serialized_end=78908 - _globals['_LISTCONFIGSCONFIGSSUBDAEMON']._serialized_start=78910 - _globals['_LISTCONFIGSCONFIGSSUBDAEMON']._serialized_end=78976 - _globals['_LISTCONFIGSCONFIGSFETCHINVOICENOCONNECT']._serialized_start=78978 - _globals['_LISTCONFIGSCONFIGSFETCHINVOICENOCONNECT']._serialized_end=79080 - _globals['_LISTCONFIGSCONFIGSTORSERVICEPASSWORD']._serialized_start=79082 - _globals['_LISTCONFIGSCONFIGSTORSERVICEPASSWORD']._serialized_end=79155 - _globals['_LISTCONFIGSCONFIGSANNOUNCEADDRDNS']._serialized_start=79157 - _globals['_LISTCONFIGSCONFIGSANNOUNCEADDRDNS']._serialized_end=79228 - _globals['_LISTCONFIGSCONFIGSREQUIRECONFIRMEDINPUTS']._serialized_start=79230 - _globals['_LISTCONFIGSCONFIGSREQUIRECONFIRMEDINPUTS']._serialized_end=79308 - _globals['_LISTCONFIGSCONFIGSCOMMITFEE']._serialized_start=79310 - _globals['_LISTCONFIGSCONFIGSCOMMITFEE']._serialized_end=79374 - _globals['_LISTCONFIGSCONFIGSCOMMITFEERATEOFFSET']._serialized_start=79376 - _globals['_LISTCONFIGSCONFIGSCOMMITFEERATEOFFSET']._serialized_end=79450 - _globals['_LISTCONFIGSCONFIGSAUTOCONNECTSEEKERPEERS']._serialized_start=79452 - _globals['_LISTCONFIGSCONFIGSAUTOCONNECTSEEKERPEERS']._serialized_end=79529 - _globals['_STOPREQUEST']._serialized_start=79531 - _globals['_STOPREQUEST']._serialized_end=79544 - _globals['_STOPRESPONSE']._serialized_start=79546 - _globals['_STOPRESPONSE']._serialized_end=79659 - _globals['_STOPRESPONSE_STOPRESULT']._serialized_start=79613 - _globals['_STOPRESPONSE_STOPRESULT']._serialized_end=79648 - _globals['_HELPREQUEST']._serialized_start=79661 - _globals['_HELPREQUEST']._serialized_end=79708 - _globals['_HELPRESPONSE']._serialized_start=79711 - _globals['_HELPRESPONSE']._serialized_end=79860 - _globals['_HELPRESPONSE_HELPFORMATHINT']._serialized_start=79816 - _globals['_HELPRESPONSE_HELPFORMATHINT']._serialized_end=79844 - _globals['_HELPHELP']._serialized_start=79862 - _globals['_HELPHELP']._serialized_end=79889 - _globals['_PREAPPROVEKEYSENDREQUEST']._serialized_start=79891 - _globals['_PREAPPROVEKEYSENDREQUEST']._serialized_end=79994 - _globals['_PREAPPROVEKEYSENDRESPONSE']._serialized_start=79996 - _globals['_PREAPPROVEKEYSENDRESPONSE']._serialized_end=80023 - _globals['_PREAPPROVEINVOICEREQUEST']._serialized_start=80025 - _globals['_PREAPPROVEINVOICEREQUEST']._serialized_end=80067 - _globals['_PREAPPROVEINVOICERESPONSE']._serialized_start=80069 - _globals['_PREAPPROVEINVOICERESPONSE']._serialized_end=80096 - _globals['_STATICBACKUPREQUEST']._serialized_start=80098 - _globals['_STATICBACKUPREQUEST']._serialized_end=80119 - _globals['_STATICBACKUPRESPONSE']._serialized_start=80121 - _globals['_STATICBACKUPRESPONSE']._serialized_end=80156 - _globals['_BKPRCHANNELSAPYREQUEST']._serialized_start=80158 - _globals['_BKPRCHANNELSAPYREQUEST']._serialized_end=80258 - _globals['_BKPRCHANNELSAPYRESPONSE']._serialized_start=80260 - _globals['_BKPRCHANNELSAPYRESPONSE']._serialized_end=80340 - _globals['_BKPRCHANNELSAPYCHANNELSAPY']._serialized_start=80343 - _globals['_BKPRCHANNELSAPYCHANNELSAPY']._serialized_end=81232 - _globals['_BKPRDUMPINCOMECSVREQUEST']._serialized_start=81235 - _globals['_BKPRDUMPINCOMECSVREQUEST']._serialized_end=81445 - _globals['_BKPRDUMPINCOMECSVRESPONSE']._serialized_start=81448 - _globals['_BKPRDUMPINCOMECSVRESPONSE']._serialized_end=81660 - _globals['_BKPRDUMPINCOMECSVRESPONSE_BKPRDUMPINCOMECSVCSVFORMAT']._serialized_start=81574 - _globals['_BKPRDUMPINCOMECSVRESPONSE_BKPRDUMPINCOMECSVCSVFORMAT']._serialized_end=81660 - _globals['_BKPRINSPECTREQUEST']._serialized_start=81662 - _globals['_BKPRINSPECTREQUEST']._serialized_end=81699 - _globals['_BKPRINSPECTRESPONSE']._serialized_start=81701 - _globals['_BKPRINSPECTRESPONSE']._serialized_end=81756 - _globals['_BKPRINSPECTTXS']._serialized_start=81759 - _globals['_BKPRINSPECTTXS']._serialized_end=81913 - _globals['_BKPRINSPECTTXSOUTPUTS']._serialized_start=81916 - _globals['_BKPRINSPECTTXSOUTPUTS']._serialized_end=82360 - _globals['_BKPRLISTACCOUNTEVENTSREQUEST']._serialized_start=82362 - _globals['_BKPRLISTACCOUNTEVENTSREQUEST']._serialized_end=82466 - _globals['_BKPRLISTACCOUNTEVENTSRESPONSE']._serialized_start=82468 - _globals['_BKPRLISTACCOUNTEVENTSRESPONSE']._serialized_end=82549 - _globals['_BKPRLISTACCOUNTEVENTSEVENTS']._serialized_start=82552 - _globals['_BKPRLISTACCOUNTEVENTSEVENTS']._serialized_end=83225 - _globals['_BKPRLISTACCOUNTEVENTSEVENTS_BKPRLISTACCOUNTEVENTSEVENTSTYPE']._serialized_start=83028 - _globals['_BKPRLISTACCOUNTEVENTSEVENTS_BKPRLISTACCOUNTEVENTSEVENTSTYPE']._serialized_end=83102 - _globals['_BKPRLISTBALANCESREQUEST']._serialized_start=83227 - _globals['_BKPRLISTBALANCESREQUEST']._serialized_end=83252 - _globals['_BKPRLISTBALANCESRESPONSE']._serialized_start=83254 - _globals['_BKPRLISTBALANCESRESPONSE']._serialized_end=83329 - _globals['_BKPRLISTBALANCESACCOUNTS']._serialized_start=83332 - _globals['_BKPRLISTBALANCESACCOUNTS']._serialized_end=83658 - _globals['_BKPRLISTBALANCESACCOUNTSBALANCES']._serialized_start=83660 - _globals['_BKPRLISTBALANCESACCOUNTSBALANCES']._serialized_end=83748 - _globals['_BKPRLISTINCOMEREQUEST']._serialized_start=83751 - _globals['_BKPRLISTINCOMEREQUEST']._serialized_end=83902 - _globals['_BKPRLISTINCOMERESPONSE']._serialized_start=83904 - _globals['_BKPRLISTINCOMERESPONSE']._serialized_end=83984 - _globals['_BKPRLISTINCOMEINCOMEEVENTS']._serialized_start=83987 - _globals['_BKPRLISTINCOMEINCOMEEVENTS']._serialized_end=84295 - _globals['_BKPREDITDESCRIPTIONBYPAYMENTIDREQUEST']._serialized_start=84297 - _globals['_BKPREDITDESCRIPTIONBYPAYMENTIDREQUEST']._serialized_end=84377 - _globals['_BKPREDITDESCRIPTIONBYPAYMENTIDRESPONSE']._serialized_start=84379 - _globals['_BKPREDITDESCRIPTIONBYPAYMENTIDRESPONSE']._serialized_end=84480 - _globals['_BKPREDITDESCRIPTIONBYPAYMENTIDUPDATED']._serialized_start=84483 - _globals['_BKPREDITDESCRIPTIONBYPAYMENTIDUPDATED']._serialized_end=85158 - _globals['_BKPREDITDESCRIPTIONBYPAYMENTIDUPDATED_BKPREDITDESCRIPTIONBYPAYMENTIDUPDATEDTYPE']._serialized_start=84984 - _globals['_BKPREDITDESCRIPTIONBYPAYMENTIDUPDATED_BKPREDITDESCRIPTIONBYPAYMENTIDUPDATEDTYPE']._serialized_end=85051 - _globals['_BKPREDITDESCRIPTIONBYOUTPOINTREQUEST']._serialized_start=85160 - _globals['_BKPREDITDESCRIPTIONBYOUTPOINTREQUEST']._serialized_end=85237 - _globals['_BKPREDITDESCRIPTIONBYOUTPOINTRESPONSE']._serialized_start=85239 - _globals['_BKPREDITDESCRIPTIONBYOUTPOINTRESPONSE']._serialized_end=85338 - _globals['_BKPREDITDESCRIPTIONBYOUTPOINTUPDATED']._serialized_start=85341 - _globals['_BKPREDITDESCRIPTIONBYOUTPOINTUPDATED']._serialized_end=86012 - _globals['_BKPREDITDESCRIPTIONBYOUTPOINTUPDATED_BKPREDITDESCRIPTIONBYOUTPOINTUPDATEDTYPE']._serialized_start=85839 - _globals['_BKPREDITDESCRIPTIONBYOUTPOINTUPDATED_BKPREDITDESCRIPTIONBYOUTPOINTUPDATEDTYPE']._serialized_end=85905 - _globals['_BLACKLISTRUNEREQUEST']._serialized_start=86014 - _globals['_BLACKLISTRUNEREQUEST']._serialized_end=86124 - _globals['_BLACKLISTRUNERESPONSE']._serialized_start=86126 - _globals['_BLACKLISTRUNERESPONSE']._serialized_end=86197 - _globals['_BLACKLISTRUNEBLACKLIST']._serialized_start=86199 - _globals['_BLACKLISTRUNEBLACKLIST']._serialized_end=86251 - _globals['_CHECKRUNEREQUEST']._serialized_start=86253 - _globals['_CHECKRUNEREQUEST']._serialized_end=86365 - _globals['_CHECKRUNERESPONSE']._serialized_start=86367 - _globals['_CHECKRUNERESPONSE']._serialized_end=86401 - _globals['_CREATERUNEREQUEST']._serialized_start=86403 - _globals['_CREATERUNEREQUEST']._serialized_end=86472 - _globals['_CREATERUNERESPONSE']._serialized_start=86474 - _globals['_CREATERUNERESPONSE']._serialized_end=86597 - _globals['_SHOWRUNESREQUEST']._serialized_start=86599 - _globals['_SHOWRUNESREQUEST']._serialized_end=86645 - _globals['_SHOWRUNESRESPONSE']._serialized_start=86647 - _globals['_SHOWRUNESRESPONSE']._serialized_end=86702 - _globals['_SHOWRUNESRUNES']._serialized_start=86705 - _globals['_SHOWRUNESRUNES']._serialized_end=86990 - _globals['_SHOWRUNESRUNESRESTRICTIONS']._serialized_start=86992 - _globals['_SHOWRUNESRUNESRESTRICTIONS']._serialized_end=87104 - _globals['_SHOWRUNESRUNESRESTRICTIONSALTERNATIVES']._serialized_start=87106 - _globals['_SHOWRUNESRUNESRESTRICTIONSALTERNATIVES']._serialized_end=87216 - _globals['_ASKRENEUNRESERVEREQUEST']._serialized_start=87218 - _globals['_ASKRENEUNRESERVEREQUEST']._serialized_end=87284 - _globals['_ASKRENEUNRESERVERESPONSE']._serialized_start=87286 - _globals['_ASKRENEUNRESERVERESPONSE']._serialized_end=87312 - _globals['_ASKRENEUNRESERVEPATH']._serialized_start=87314 - _globals['_ASKRENEUNRESERVEPATH']._serialized_end=87430 - _globals['_ASKRENELISTLAYERSREQUEST']._serialized_start=87432 - _globals['_ASKRENELISTLAYERSREQUEST']._serialized_end=87488 - _globals['_ASKRENELISTLAYERSRESPONSE']._serialized_start=87490 - _globals['_ASKRENELISTLAYERSRESPONSE']._serialized_end=87563 - _globals['_ASKRENELISTLAYERSLAYERS']._serialized_start=87566 - _globals['_ASKRENELISTLAYERSLAYERS']._serialized_end=87951 - _globals['_ASKRENELISTLAYERSLAYERSCREATEDCHANNELS']._serialized_start=87954 - _globals['_ASKRENELISTLAYERSLAYERSCREATEDCHANNELS']._serialized_end=88093 - _globals['_ASKRENELISTLAYERSLAYERSCHANNELUPDATES']._serialized_start=88096 - _globals['_ASKRENELISTLAYERSLAYERSCHANNELUPDATES']._serialized_end=88520 - _globals['_ASKRENELISTLAYERSLAYERSCONSTRAINTS']._serialized_start=88523 - _globals['_ASKRENELISTLAYERSLAYERSCONSTRAINTS']._serialized_end=88771 - _globals['_ASKRENELISTLAYERSLAYERSBIASES']._serialized_start=88773 - _globals['_ASKRENELISTLAYERSLAYERSBIASES']._serialized_end=88890 - _globals['_ASKRENECREATELAYERREQUEST']._serialized_start=88892 - _globals['_ASKRENECREATELAYERREQUEST']._serialized_end=88974 - _globals['_ASKRENECREATELAYERRESPONSE']._serialized_start=88976 - _globals['_ASKRENECREATELAYERRESPONSE']._serialized_end=89051 - _globals['_ASKRENECREATELAYERLAYERS']._serialized_start=89054 - _globals['_ASKRENECREATELAYERLAYERS']._serialized_end=89424 - _globals['_ASKRENECREATELAYERLAYERSCREATEDCHANNELS']._serialized_start=89427 - _globals['_ASKRENECREATELAYERLAYERSCREATEDCHANNELS']._serialized_end=89567 - _globals['_ASKRENECREATELAYERLAYERSCHANNELUPDATES']._serialized_start=89570 - _globals['_ASKRENECREATELAYERLAYERSCHANNELUPDATES']._serialized_end=89907 - _globals['_ASKRENECREATELAYERLAYERSCONSTRAINTS']._serialized_start=89910 - _globals['_ASKRENECREATELAYERLAYERSCONSTRAINTS']._serialized_end=90106 - _globals['_ASKRENECREATELAYERLAYERSBIASES']._serialized_start=90108 - _globals['_ASKRENECREATELAYERLAYERSBIASES']._serialized_end=90226 - _globals['_ASKRENEREMOVELAYERREQUEST']._serialized_start=90228 - _globals['_ASKRENEREMOVELAYERREQUEST']._serialized_end=90270 - _globals['_ASKRENEREMOVELAYERRESPONSE']._serialized_start=90272 - _globals['_ASKRENEREMOVELAYERRESPONSE']._serialized_end=90300 - _globals['_ASKRENERESERVEREQUEST']._serialized_start=90302 - _globals['_ASKRENERESERVEREQUEST']._serialized_end=90364 - _globals['_ASKRENERESERVERESPONSE']._serialized_start=90366 - _globals['_ASKRENERESERVERESPONSE']._serialized_end=90390 - _globals['_ASKRENERESERVEPATH']._serialized_start=90392 - _globals['_ASKRENERESERVEPATH']._serialized_end=90506 - _globals['_ASKRENEAGEREQUEST']._serialized_start=90508 - _globals['_ASKRENEAGEREQUEST']._serialized_end=90558 - _globals['_ASKRENEAGERESPONSE']._serialized_start=90560 - _globals['_ASKRENEAGERESPONSE']._serialized_end=90616 - _globals['_GETROUTESREQUEST']._serialized_start=90619 - _globals['_GETROUTESREQUEST']._serialized_end=90870 - _globals['_GETROUTESRESPONSE']._serialized_start=90872 - _globals['_GETROUTESRESPONSE']._serialized_end=90954 - _globals['_GETROUTESROUTES']._serialized_start=90957 - _globals['_GETROUTESROUTES']._serialized_end=91113 - _globals['_GETROUTESROUTESPATH']._serialized_start=91116 - _globals['_GETROUTESROUTESPATH']._serialized_end=91268 - _globals['_ASKRENEDISABLENODEREQUEST']._serialized_start=91270 - _globals['_ASKRENEDISABLENODEREQUEST']._serialized_end=91326 - _globals['_ASKRENEDISABLENODERESPONSE']._serialized_start=91328 - _globals['_ASKRENEDISABLENODERESPONSE']._serialized_end=91356 - _globals['_ASKRENEINFORMCHANNELREQUEST']._serialized_start=91359 - _globals['_ASKRENEINFORMCHANNELREQUEST']._serialized_end=91692 - _globals['_ASKRENEINFORMCHANNELREQUEST_ASKRENEINFORMCHANNELINFORM']._serialized_start=91561 - _globals['_ASKRENEINFORMCHANNELREQUEST_ASKRENEINFORMCHANNELINFORM']._serialized_end=91640 - _globals['_ASKRENEINFORMCHANNELRESPONSE']._serialized_start=91694 - _globals['_ASKRENEINFORMCHANNELRESPONSE']._serialized_end=91783 - _globals['_ASKRENEINFORMCHANNELCONSTRAINTS']._serialized_start=91786 - _globals['_ASKRENEINFORMCHANNELCONSTRAINTS']._serialized_end=91997 - _globals['_ASKRENECREATECHANNELREQUEST']._serialized_start=92000 - _globals['_ASKRENECREATECHANNELREQUEST']._serialized_end=92143 - _globals['_ASKRENECREATECHANNELRESPONSE']._serialized_start=92145 - _globals['_ASKRENECREATECHANNELRESPONSE']._serialized_end=92175 - _globals['_ASKRENEUPDATECHANNELREQUEST']._serialized_start=92178 - _globals['_ASKRENEUPDATECHANNELREQUEST']._serialized_end=92607 - _globals['_ASKRENEUPDATECHANNELRESPONSE']._serialized_start=92609 - _globals['_ASKRENEUPDATECHANNELRESPONSE']._serialized_end=92639 - _globals['_ASKRENEBIASCHANNELREQUEST']._serialized_start=92642 - _globals['_ASKRENEBIASCHANNELREQUEST']._serialized_end=92806 - _globals['_ASKRENEBIASCHANNELRESPONSE']._serialized_start=92808 - _globals['_ASKRENEBIASCHANNELRESPONSE']._serialized_end=92883 - _globals['_ASKRENEBIASCHANNELBIASES']._serialized_start=92885 - _globals['_ASKRENEBIASCHANNELBIASES']._serialized_end=93012 - _globals['_ASKRENELISTRESERVATIONSREQUEST']._serialized_start=93014 - _globals['_ASKRENELISTRESERVATIONSREQUEST']._serialized_end=93046 - _globals['_ASKRENELISTRESERVATIONSRESPONSE']._serialized_start=93048 - _globals['_ASKRENELISTRESERVATIONSRESPONSE']._serialized_end=93145 - _globals['_ASKRENELISTRESERVATIONSRESERVATIONS']._serialized_start=93148 - _globals['_ASKRENELISTRESERVATIONSRESERVATIONS']._serialized_end=93293 - _globals['_INJECTPAYMENTONIONREQUEST']._serialized_start=93296 - _globals['_INJECTPAYMENTONIONREQUEST']._serialized_end=93627 - _globals['_INJECTPAYMENTONIONRESPONSE']._serialized_start=93629 - _globals['_INJECTPAYMENTONIONRESPONSE']._serialized_end=93748 - _globals['_INJECTONIONMESSAGEREQUEST']._serialized_start=93750 - _globals['_INJECTONIONMESSAGEREQUEST']._serialized_end=93812 - _globals['_INJECTONIONMESSAGERESPONSE']._serialized_start=93814 - _globals['_INJECTONIONMESSAGERESPONSE']._serialized_end=93842 - _globals['_XPAYREQUEST']._serialized_start=93845 - _globals['_XPAYREQUEST']._serialized_end=94124 - _globals['_XPAYRESPONSE']._serialized_start=94127 - _globals['_XPAYRESPONSE']._serialized_end=94288 - _globals['_SIGNMESSAGEWITHKEYREQUEST']._serialized_start=94290 - _globals['_SIGNMESSAGEWITHKEYREQUEST']._serialized_end=94351 - _globals['_SIGNMESSAGEWITHKEYRESPONSE']._serialized_start=94353 - _globals['_SIGNMESSAGEWITHKEYRESPONSE']._serialized_end=94449 - _globals['_STREAMBLOCKADDEDREQUEST']._serialized_start=94451 - _globals['_STREAMBLOCKADDEDREQUEST']._serialized_end=94476 - _globals['_BLOCKADDEDNOTIFICATION']._serialized_start=94478 - _globals['_BLOCKADDEDNOTIFICATION']._serialized_end=94532 - _globals['_STREAMCHANNELOPENFAILEDREQUEST']._serialized_start=94534 - _globals['_STREAMCHANNELOPENFAILEDREQUEST']._serialized_end=94566 - _globals['_CHANNELOPENFAILEDNOTIFICATION']._serialized_start=94568 - _globals['_CHANNELOPENFAILEDNOTIFICATION']._serialized_end=94619 - _globals['_STREAMCHANNELOPENEDREQUEST']._serialized_start=94621 - _globals['_STREAMCHANNELOPENEDREQUEST']._serialized_end=94649 - _globals['_CHANNELOPENEDNOTIFICATION']._serialized_start=94651 - _globals['_CHANNELOPENEDNOTIFICATION']._serialized_end=94770 - _globals['_STREAMCONNECTREQUEST']._serialized_start=94772 - _globals['_STREAMCONNECTREQUEST']._serialized_end=94794 - _globals['_PEERCONNECTNOTIFICATION']._serialized_start=94797 - _globals['_PEERCONNECTNOTIFICATION']._serialized_end=94987 - _globals['_PEERCONNECTNOTIFICATION_PEERCONNECTDIRECTION']._serialized_start=94948 - _globals['_PEERCONNECTNOTIFICATION_PEERCONNECTDIRECTION']._serialized_end=94987 - _globals['_PEERCONNECTADDRESS']._serialized_start=94990 - _globals['_PEERCONNECTADDRESS']._serialized_end=95272 - _globals['_PEERCONNECTADDRESS_PEERCONNECTADDRESSTYPE']._serialized_start=95141 - _globals['_PEERCONNECTADDRESS_PEERCONNECTADDRESSTYPE']._serialized_end=95240 - _globals['_STREAMCUSTOMMSGREQUEST']._serialized_start=95274 - _globals['_STREAMCUSTOMMSGREQUEST']._serialized_end=95298 - _globals['_CUSTOMMSGNOTIFICATION']._serialized_start=95300 - _globals['_CUSTOMMSGNOTIFICATION']._serialized_end=95357 - _globals['_STREAMCHANNELSTATECHANGEDREQUEST']._serialized_start=95359 - _globals['_STREAMCHANNELSTATECHANGEDREQUEST']._serialized_end=95393 - _globals['_CHANNELSTATECHANGEDNOTIFICATION']._serialized_start=95396 - _globals['_CHANNELSTATECHANGEDNOTIFICATION']._serialized_end=95828 - _globals['_CHANNELSTATECHANGEDNOTIFICATION_CHANNELSTATECHANGEDCAUSE']._serialized_start=95694 - _globals['_CHANNELSTATECHANGEDNOTIFICATION_CHANNELSTATECHANGEDCAUSE']._serialized_end=95793 - _globals['_NODE']._serialized_start=95831 - _globals['_NODE']._serialized_end=106176 + _globals['_WAITRESPONSE_WAITSUBSYSTEM']._serialized_end=65212 + _globals['_WAITFORWARDS']._serialized_start=65937 + _globals['_WAITFORWARDS']._serialized_end=66268 + _globals['_WAITFORWARDS_WAITFORWARDSSTATUS']._serialized_start=66123 + _globals['_WAITFORWARDS_WAITFORWARDSSTATUS']._serialized_end=66199 + _globals['_WAITINVOICES']._serialized_start=66271 + _globals['_WAITINVOICES']._serialized_end=66548 + _globals['_WAITINVOICES_WAITINVOICESSTATUS']._serialized_start=66434 + _globals['_WAITINVOICES_WAITINVOICESSTATUS']._serialized_end=66489 + _globals['_WAITSENDPAYS']._serialized_start=66551 + _globals['_WAITSENDPAYS']._serialized_end=66806 + _globals['_WAITSENDPAYS_WAITSENDPAYSSTATUS']._serialized_start=66696 + _globals['_WAITSENDPAYS_WAITSENDPAYSSTATUS']._serialized_end=66755 + _globals['_WAITHTLCS']._serialized_start=66809 + _globals['_WAITHTLCS']._serialized_end=67745 + _globals['_WAITHTLCS_WAITHTLCSSTATE']._serialized_start=67078 + _globals['_WAITHTLCS_WAITHTLCSSTATE']._serialized_end=67600 + _globals['_WAITHTLCS_WAITHTLCSDIRECTION']._serialized_start=67602 + _globals['_WAITHTLCS_WAITHTLCSDIRECTION']._serialized_end=67639 + _globals['_WAITCHAINMOVES']._serialized_start=67747 + _globals['_WAITCHAINMOVES']._serialized_end=67847 + _globals['_WAITCHANNELMOVES']._serialized_start=67849 + _globals['_WAITCHANNELMOVES']._serialized_end=67951 + _globals['_WAITDETAILS']._serialized_start=67954 + _globals['_WAITDETAILS']._serialized_end=68590 + _globals['_WAITDETAILS_WAITDETAILSSTATUS']._serialized_start=68296 + _globals['_WAITDETAILS_WAITDETAILSSTATUS']._serialized_end=68433 + _globals['_LISTCONFIGSREQUEST']._serialized_start=68592 + _globals['_LISTCONFIGSREQUEST']._serialized_end=68644 + _globals['_LISTCONFIGSRESPONSE']._serialized_start=68646 + _globals['_LISTCONFIGSRESPONSE']._serialized_end=68726 + _globals['_LISTCONFIGSCONFIGS']._serialized_start=68729 + _globals['_LISTCONFIGSCONFIGS']._serialized_end=74722 + _globals['_LISTCONFIGSCONFIGSCONF']._serialized_start=74725 + _globals['_LISTCONFIGSCONFIGSCONF']._serialized_end=74887 + _globals['_LISTCONFIGSCONFIGSCONF_LISTCONFIGSCONFIGSCONFSOURCE']._serialized_start=74844 + _globals['_LISTCONFIGSCONFIGSCONF_LISTCONFIGSCONFIGSCONFSOURCE']._serialized_end=74887 + _globals['_LISTCONFIGSCONFIGSDEVELOPER']._serialized_start=74889 + _globals['_LISTCONFIGSCONFIGSDEVELOPER']._serialized_end=74947 + _globals['_LISTCONFIGSCONFIGSCLEARPLUGINS']._serialized_start=74949 + _globals['_LISTCONFIGSCONFIGSCLEARPLUGINS']._serialized_end=75010 + _globals['_LISTCONFIGSCONFIGSDISABLEMPP']._serialized_start=75012 + _globals['_LISTCONFIGSCONFIGSDISABLEMPP']._serialized_end=75103 + _globals['_LISTCONFIGSCONFIGSMAINNET']._serialized_start=75105 + _globals['_LISTCONFIGSCONFIGSMAINNET']._serialized_end=75161 + _globals['_LISTCONFIGSCONFIGSREGTEST']._serialized_start=75163 + _globals['_LISTCONFIGSCONFIGSREGTEST']._serialized_end=75219 + _globals['_LISTCONFIGSCONFIGSSIGNET']._serialized_start=75221 + _globals['_LISTCONFIGSCONFIGSSIGNET']._serialized_end=75276 + _globals['_LISTCONFIGSCONFIGSTESTNET']._serialized_start=75278 + _globals['_LISTCONFIGSCONFIGSTESTNET']._serialized_end=75334 + _globals['_LISTCONFIGSCONFIGSIMPORTANTPLUGIN']._serialized_start=75336 + _globals['_LISTCONFIGSCONFIGSIMPORTANTPLUGIN']._serialized_end=75408 + _globals['_LISTCONFIGSCONFIGSPLUGIN']._serialized_start=75410 + _globals['_LISTCONFIGSCONFIGSPLUGIN']._serialized_end=75473 + _globals['_LISTCONFIGSCONFIGSPLUGINDIR']._serialized_start=75475 + _globals['_LISTCONFIGSCONFIGSPLUGINDIR']._serialized_end=75541 + _globals['_LISTCONFIGSCONFIGSLIGHTNINGDIR']._serialized_start=75543 + _globals['_LISTCONFIGSCONFIGSLIGHTNINGDIR']._serialized_end=75610 + _globals['_LISTCONFIGSCONFIGSNETWORK']._serialized_start=75612 + _globals['_LISTCONFIGSCONFIGSNETWORK']._serialized_end=75674 + _globals['_LISTCONFIGSCONFIGSALLOWDEPRECATEDAPIS']._serialized_start=75676 + _globals['_LISTCONFIGSCONFIGSALLOWDEPRECATEDAPIS']._serialized_end=75751 + _globals['_LISTCONFIGSCONFIGSRPCFILE']._serialized_start=75753 + _globals['_LISTCONFIGSCONFIGSRPCFILE']._serialized_end=75815 + _globals['_LISTCONFIGSCONFIGSDISABLEPLUGIN']._serialized_start=75817 + _globals['_LISTCONFIGSCONFIGSDISABLEPLUGIN']._serialized_end=75887 + _globals['_LISTCONFIGSCONFIGSALWAYSUSEPROXY']._serialized_start=75889 + _globals['_LISTCONFIGSCONFIGSALWAYSUSEPROXY']._serialized_end=75959 + _globals['_LISTCONFIGSCONFIGSDAEMON']._serialized_start=75961 + _globals['_LISTCONFIGSCONFIGSDAEMON']._serialized_end=76016 + _globals['_LISTCONFIGSCONFIGSWALLET']._serialized_start=76018 + _globals['_LISTCONFIGSCONFIGSWALLET']._serialized_end=76079 + _globals['_LISTCONFIGSCONFIGSLARGECHANNELS']._serialized_start=76081 + _globals['_LISTCONFIGSCONFIGSLARGECHANNELS']._serialized_end=76143 + _globals['_LISTCONFIGSCONFIGSEXPERIMENTALDUALFUND']._serialized_start=76145 + _globals['_LISTCONFIGSCONFIGSEXPERIMENTALDUALFUND']._serialized_end=76214 + _globals['_LISTCONFIGSCONFIGSEXPERIMENTALSPLICING']._serialized_start=76216 + _globals['_LISTCONFIGSCONFIGSEXPERIMENTALSPLICING']._serialized_end=76285 + _globals['_LISTCONFIGSCONFIGSEXPERIMENTALONIONMESSAGES']._serialized_start=76287 + _globals['_LISTCONFIGSCONFIGSEXPERIMENTALONIONMESSAGES']._serialized_end=76361 + _globals['_LISTCONFIGSCONFIGSEXPERIMENTALOFFERS']._serialized_start=76363 + _globals['_LISTCONFIGSCONFIGSEXPERIMENTALOFFERS']._serialized_end=76430 + _globals['_LISTCONFIGSCONFIGSEXPERIMENTALSHUTDOWNWRONGFUNDING']._serialized_start=76432 + _globals['_LISTCONFIGSCONFIGSEXPERIMENTALSHUTDOWNWRONGFUNDING']._serialized_end=76513 + _globals['_LISTCONFIGSCONFIGSEXPERIMENTALPEERSTORAGE']._serialized_start=76515 + _globals['_LISTCONFIGSCONFIGSEXPERIMENTALPEERSTORAGE']._serialized_end=76587 + _globals['_LISTCONFIGSCONFIGSEXPERIMENTALANCHORS']._serialized_start=76589 + _globals['_LISTCONFIGSCONFIGSEXPERIMENTALANCHORS']._serialized_end=76657 + _globals['_LISTCONFIGSCONFIGSDATABASEUPGRADE']._serialized_start=76659 + _globals['_LISTCONFIGSCONFIGSDATABASEUPGRADE']._serialized_end=76730 + _globals['_LISTCONFIGSCONFIGSRGB']._serialized_start=76732 + _globals['_LISTCONFIGSCONFIGSRGB']._serialized_end=76790 + _globals['_LISTCONFIGSCONFIGSALIAS']._serialized_start=76792 + _globals['_LISTCONFIGSCONFIGSALIAS']._serialized_end=76852 + _globals['_LISTCONFIGSCONFIGSPIDFILE']._serialized_start=76854 + _globals['_LISTCONFIGSCONFIGSPIDFILE']._serialized_end=76916 + _globals['_LISTCONFIGSCONFIGSIGNOREFEELIMITS']._serialized_start=76918 + _globals['_LISTCONFIGSCONFIGSIGNOREFEELIMITS']._serialized_end=76989 + _globals['_LISTCONFIGSCONFIGSWATCHTIMEBLOCKS']._serialized_start=76991 + _globals['_LISTCONFIGSCONFIGSWATCHTIMEBLOCKS']._serialized_end=77061 + _globals['_LISTCONFIGSCONFIGSMAXLOCKTIMEBLOCKS']._serialized_start=77063 + _globals['_LISTCONFIGSCONFIGSMAXLOCKTIMEBLOCKS']._serialized_end=77135 + _globals['_LISTCONFIGSCONFIGSFUNDINGCONFIRMS']._serialized_start=77137 + _globals['_LISTCONFIGSCONFIGSFUNDINGCONFIRMS']._serialized_end=77207 + _globals['_LISTCONFIGSCONFIGSCLTVDELTA']._serialized_start=77209 + _globals['_LISTCONFIGSCONFIGSCLTVDELTA']._serialized_end=77273 + _globals['_LISTCONFIGSCONFIGSCLTVFINAL']._serialized_start=77275 + _globals['_LISTCONFIGSCONFIGSCLTVFINAL']._serialized_end=77339 + _globals['_LISTCONFIGSCONFIGSCOMMITTIME']._serialized_start=77341 + _globals['_LISTCONFIGSCONFIGSCOMMITTIME']._serialized_end=77406 + _globals['_LISTCONFIGSCONFIGSFEEBASE']._serialized_start=77408 + _globals['_LISTCONFIGSCONFIGSFEEBASE']._serialized_end=77470 + _globals['_LISTCONFIGSCONFIGSRESCAN']._serialized_start=77472 + _globals['_LISTCONFIGSCONFIGSRESCAN']._serialized_end=77533 + _globals['_LISTCONFIGSCONFIGSFEEPERSATOSHI']._serialized_start=77535 + _globals['_LISTCONFIGSCONFIGSFEEPERSATOSHI']._serialized_end=77603 + _globals['_LISTCONFIGSCONFIGSMAXCONCURRENTHTLCS']._serialized_start=77605 + _globals['_LISTCONFIGSCONFIGSMAXCONCURRENTHTLCS']._serialized_end=77678 + _globals['_LISTCONFIGSCONFIGSHTLCMINIMUMMSAT']._serialized_start=77680 + _globals['_LISTCONFIGSCONFIGSHTLCMINIMUMMSAT']._serialized_end=77764 + _globals['_LISTCONFIGSCONFIGSHTLCMAXIMUMMSAT']._serialized_start=77766 + _globals['_LISTCONFIGSCONFIGSHTLCMAXIMUMMSAT']._serialized_end=77850 + _globals['_LISTCONFIGSCONFIGSMAXDUSTHTLCEXPOSUREMSAT']._serialized_start=77852 + _globals['_LISTCONFIGSCONFIGSMAXDUSTHTLCEXPOSUREMSAT']._serialized_end=77944 + _globals['_LISTCONFIGSCONFIGSMINCAPACITYSAT']._serialized_start=77946 + _globals['_LISTCONFIGSCONFIGSMINCAPACITYSAT']._serialized_end=78049 + _globals['_LISTCONFIGSCONFIGSADDR']._serialized_start=78051 + _globals['_LISTCONFIGSCONFIGSADDR']._serialized_end=78112 + _globals['_LISTCONFIGSCONFIGSANNOUNCEADDR']._serialized_start=78114 + _globals['_LISTCONFIGSCONFIGSANNOUNCEADDR']._serialized_end=78183 + _globals['_LISTCONFIGSCONFIGSBINDADDR']._serialized_start=78185 + _globals['_LISTCONFIGSCONFIGSBINDADDR']._serialized_end=78250 + _globals['_LISTCONFIGSCONFIGSOFFLINE']._serialized_start=78252 + _globals['_LISTCONFIGSCONFIGSOFFLINE']._serialized_end=78308 + _globals['_LISTCONFIGSCONFIGSAUTOLISTEN']._serialized_start=78310 + _globals['_LISTCONFIGSCONFIGSAUTOLISTEN']._serialized_end=78376 + _globals['_LISTCONFIGSCONFIGSPROXY']._serialized_start=78378 + _globals['_LISTCONFIGSCONFIGSPROXY']._serialized_end=78438 + _globals['_LISTCONFIGSCONFIGSDISABLEDNS']._serialized_start=78440 + _globals['_LISTCONFIGSCONFIGSDISABLEDNS']._serialized_end=78499 + _globals['_LISTCONFIGSCONFIGSANNOUNCEADDRDISCOVERED']._serialized_start=78502 + _globals['_LISTCONFIGSCONFIGSANNOUNCEADDRDISCOVERED']._serialized_end=78758 + _globals['_LISTCONFIGSCONFIGSANNOUNCEADDRDISCOVERED_LISTCONFIGSCONFIGSANNOUNCEADDRDISCOVEREDVALUESTR']._serialized_start=78677 + _globals['_LISTCONFIGSCONFIGSANNOUNCEADDRDISCOVERED_LISTCONFIGSCONFIGSANNOUNCEADDRDISCOVEREDVALUESTR']._serialized_end=78758 + _globals['_LISTCONFIGSCONFIGSANNOUNCEADDRDISCOVEREDPORT']._serialized_start=78760 + _globals['_LISTCONFIGSCONFIGSANNOUNCEADDRDISCOVEREDPORT']._serialized_end=78841 + _globals['_LISTCONFIGSCONFIGSENCRYPTEDHSM']._serialized_start=78843 + _globals['_LISTCONFIGSCONFIGSENCRYPTEDHSM']._serialized_end=78904 + _globals['_LISTCONFIGSCONFIGSRPCFILEMODE']._serialized_start=78906 + _globals['_LISTCONFIGSCONFIGSRPCFILEMODE']._serialized_end=78972 + _globals['_LISTCONFIGSCONFIGSLOGLEVEL']._serialized_start=78974 + _globals['_LISTCONFIGSCONFIGSLOGLEVEL']._serialized_end=79037 + _globals['_LISTCONFIGSCONFIGSLOGPREFIX']._serialized_start=79039 + _globals['_LISTCONFIGSCONFIGSLOGPREFIX']._serialized_end=79103 + _globals['_LISTCONFIGSCONFIGSLOGFILE']._serialized_start=79105 + _globals['_LISTCONFIGSCONFIGSLOGFILE']._serialized_end=79169 + _globals['_LISTCONFIGSCONFIGSLOGTIMESTAMPS']._serialized_start=79171 + _globals['_LISTCONFIGSCONFIGSLOGTIMESTAMPS']._serialized_end=79240 + _globals['_LISTCONFIGSCONFIGSFORCEFEERATES']._serialized_start=79242 + _globals['_LISTCONFIGSCONFIGSFORCEFEERATES']._serialized_end=79310 + _globals['_LISTCONFIGSCONFIGSSUBDAEMON']._serialized_start=79312 + _globals['_LISTCONFIGSCONFIGSSUBDAEMON']._serialized_end=79378 + _globals['_LISTCONFIGSCONFIGSFETCHINVOICENOCONNECT']._serialized_start=79380 + _globals['_LISTCONFIGSCONFIGSFETCHINVOICENOCONNECT']._serialized_end=79482 + _globals['_LISTCONFIGSCONFIGSTORSERVICEPASSWORD']._serialized_start=79484 + _globals['_LISTCONFIGSCONFIGSTORSERVICEPASSWORD']._serialized_end=79557 + _globals['_LISTCONFIGSCONFIGSANNOUNCEADDRDNS']._serialized_start=79559 + _globals['_LISTCONFIGSCONFIGSANNOUNCEADDRDNS']._serialized_end=79630 + _globals['_LISTCONFIGSCONFIGSREQUIRECONFIRMEDINPUTS']._serialized_start=79632 + _globals['_LISTCONFIGSCONFIGSREQUIRECONFIRMEDINPUTS']._serialized_end=79710 + _globals['_LISTCONFIGSCONFIGSCOMMITFEE']._serialized_start=79712 + _globals['_LISTCONFIGSCONFIGSCOMMITFEE']._serialized_end=79776 + _globals['_LISTCONFIGSCONFIGSCOMMITFEERATEOFFSET']._serialized_start=79778 + _globals['_LISTCONFIGSCONFIGSCOMMITFEERATEOFFSET']._serialized_end=79852 + _globals['_LISTCONFIGSCONFIGSAUTOCONNECTSEEKERPEERS']._serialized_start=79854 + _globals['_LISTCONFIGSCONFIGSAUTOCONNECTSEEKERPEERS']._serialized_end=79931 + _globals['_STOPREQUEST']._serialized_start=79933 + _globals['_STOPREQUEST']._serialized_end=79946 + _globals['_STOPRESPONSE']._serialized_start=79948 + _globals['_STOPRESPONSE']._serialized_end=80061 + _globals['_STOPRESPONSE_STOPRESULT']._serialized_start=80015 + _globals['_STOPRESPONSE_STOPRESULT']._serialized_end=80050 + _globals['_HELPREQUEST']._serialized_start=80063 + _globals['_HELPREQUEST']._serialized_end=80110 + _globals['_HELPRESPONSE']._serialized_start=80113 + _globals['_HELPRESPONSE']._serialized_end=80262 + _globals['_HELPRESPONSE_HELPFORMATHINT']._serialized_start=80218 + _globals['_HELPRESPONSE_HELPFORMATHINT']._serialized_end=80246 + _globals['_HELPHELP']._serialized_start=80264 + _globals['_HELPHELP']._serialized_end=80291 + _globals['_PREAPPROVEKEYSENDREQUEST']._serialized_start=80293 + _globals['_PREAPPROVEKEYSENDREQUEST']._serialized_end=80396 + _globals['_PREAPPROVEKEYSENDRESPONSE']._serialized_start=80398 + _globals['_PREAPPROVEKEYSENDRESPONSE']._serialized_end=80425 + _globals['_PREAPPROVEINVOICEREQUEST']._serialized_start=80427 + _globals['_PREAPPROVEINVOICEREQUEST']._serialized_end=80469 + _globals['_PREAPPROVEINVOICERESPONSE']._serialized_start=80471 + _globals['_PREAPPROVEINVOICERESPONSE']._serialized_end=80498 + _globals['_STATICBACKUPREQUEST']._serialized_start=80500 + _globals['_STATICBACKUPREQUEST']._serialized_end=80521 + _globals['_STATICBACKUPRESPONSE']._serialized_start=80523 + _globals['_STATICBACKUPRESPONSE']._serialized_end=80558 + _globals['_BKPRCHANNELSAPYREQUEST']._serialized_start=80560 + _globals['_BKPRCHANNELSAPYREQUEST']._serialized_end=80660 + _globals['_BKPRCHANNELSAPYRESPONSE']._serialized_start=80662 + _globals['_BKPRCHANNELSAPYRESPONSE']._serialized_end=80742 + _globals['_BKPRCHANNELSAPYCHANNELSAPY']._serialized_start=80745 + _globals['_BKPRCHANNELSAPYCHANNELSAPY']._serialized_end=81634 + _globals['_BKPRDUMPINCOMECSVREQUEST']._serialized_start=81637 + _globals['_BKPRDUMPINCOMECSVREQUEST']._serialized_end=81847 + _globals['_BKPRDUMPINCOMECSVRESPONSE']._serialized_start=81850 + _globals['_BKPRDUMPINCOMECSVRESPONSE']._serialized_end=82062 + _globals['_BKPRDUMPINCOMECSVRESPONSE_BKPRDUMPINCOMECSVCSVFORMAT']._serialized_start=81976 + _globals['_BKPRDUMPINCOMECSVRESPONSE_BKPRDUMPINCOMECSVCSVFORMAT']._serialized_end=82062 + _globals['_BKPRINSPECTREQUEST']._serialized_start=82064 + _globals['_BKPRINSPECTREQUEST']._serialized_end=82101 + _globals['_BKPRINSPECTRESPONSE']._serialized_start=82103 + _globals['_BKPRINSPECTRESPONSE']._serialized_end=82158 + _globals['_BKPRINSPECTTXS']._serialized_start=82161 + _globals['_BKPRINSPECTTXS']._serialized_end=82315 + _globals['_BKPRINSPECTTXSOUTPUTS']._serialized_start=82318 + _globals['_BKPRINSPECTTXSOUTPUTS']._serialized_end=82762 + _globals['_BKPRLISTACCOUNTEVENTSREQUEST']._serialized_start=82764 + _globals['_BKPRLISTACCOUNTEVENTSREQUEST']._serialized_end=82868 + _globals['_BKPRLISTACCOUNTEVENTSRESPONSE']._serialized_start=82870 + _globals['_BKPRLISTACCOUNTEVENTSRESPONSE']._serialized_end=82951 + _globals['_BKPRLISTACCOUNTEVENTSEVENTS']._serialized_start=82954 + _globals['_BKPRLISTACCOUNTEVENTSEVENTS']._serialized_end=83627 + _globals['_BKPRLISTACCOUNTEVENTSEVENTS_BKPRLISTACCOUNTEVENTSEVENTSTYPE']._serialized_start=83430 + _globals['_BKPRLISTACCOUNTEVENTSEVENTS_BKPRLISTACCOUNTEVENTSEVENTSTYPE']._serialized_end=83504 + _globals['_BKPRLISTBALANCESREQUEST']._serialized_start=83629 + _globals['_BKPRLISTBALANCESREQUEST']._serialized_end=83654 + _globals['_BKPRLISTBALANCESRESPONSE']._serialized_start=83656 + _globals['_BKPRLISTBALANCESRESPONSE']._serialized_end=83731 + _globals['_BKPRLISTBALANCESACCOUNTS']._serialized_start=83734 + _globals['_BKPRLISTBALANCESACCOUNTS']._serialized_end=84060 + _globals['_BKPRLISTBALANCESACCOUNTSBALANCES']._serialized_start=84062 + _globals['_BKPRLISTBALANCESACCOUNTSBALANCES']._serialized_end=84150 + _globals['_BKPRLISTINCOMEREQUEST']._serialized_start=84153 + _globals['_BKPRLISTINCOMEREQUEST']._serialized_end=84304 + _globals['_BKPRLISTINCOMERESPONSE']._serialized_start=84306 + _globals['_BKPRLISTINCOMERESPONSE']._serialized_end=84386 + _globals['_BKPRLISTINCOMEINCOMEEVENTS']._serialized_start=84389 + _globals['_BKPRLISTINCOMEINCOMEEVENTS']._serialized_end=84697 + _globals['_BKPREDITDESCRIPTIONBYPAYMENTIDREQUEST']._serialized_start=84699 + _globals['_BKPREDITDESCRIPTIONBYPAYMENTIDREQUEST']._serialized_end=84779 + _globals['_BKPREDITDESCRIPTIONBYPAYMENTIDRESPONSE']._serialized_start=84781 + _globals['_BKPREDITDESCRIPTIONBYPAYMENTIDRESPONSE']._serialized_end=84882 + _globals['_BKPREDITDESCRIPTIONBYPAYMENTIDUPDATED']._serialized_start=84885 + _globals['_BKPREDITDESCRIPTIONBYPAYMENTIDUPDATED']._serialized_end=85560 + _globals['_BKPREDITDESCRIPTIONBYPAYMENTIDUPDATED_BKPREDITDESCRIPTIONBYPAYMENTIDUPDATEDTYPE']._serialized_start=85386 + _globals['_BKPREDITDESCRIPTIONBYPAYMENTIDUPDATED_BKPREDITDESCRIPTIONBYPAYMENTIDUPDATEDTYPE']._serialized_end=85453 + _globals['_BKPREDITDESCRIPTIONBYOUTPOINTREQUEST']._serialized_start=85562 + _globals['_BKPREDITDESCRIPTIONBYOUTPOINTREQUEST']._serialized_end=85639 + _globals['_BKPREDITDESCRIPTIONBYOUTPOINTRESPONSE']._serialized_start=85641 + _globals['_BKPREDITDESCRIPTIONBYOUTPOINTRESPONSE']._serialized_end=85740 + _globals['_BKPREDITDESCRIPTIONBYOUTPOINTUPDATED']._serialized_start=85743 + _globals['_BKPREDITDESCRIPTIONBYOUTPOINTUPDATED']._serialized_end=86414 + _globals['_BKPREDITDESCRIPTIONBYOUTPOINTUPDATED_BKPREDITDESCRIPTIONBYOUTPOINTUPDATEDTYPE']._serialized_start=86241 + _globals['_BKPREDITDESCRIPTIONBYOUTPOINTUPDATED_BKPREDITDESCRIPTIONBYOUTPOINTUPDATEDTYPE']._serialized_end=86307 + _globals['_BLACKLISTRUNEREQUEST']._serialized_start=86416 + _globals['_BLACKLISTRUNEREQUEST']._serialized_end=86526 + _globals['_BLACKLISTRUNERESPONSE']._serialized_start=86528 + _globals['_BLACKLISTRUNERESPONSE']._serialized_end=86599 + _globals['_BLACKLISTRUNEBLACKLIST']._serialized_start=86601 + _globals['_BLACKLISTRUNEBLACKLIST']._serialized_end=86653 + _globals['_CHECKRUNEREQUEST']._serialized_start=86655 + _globals['_CHECKRUNEREQUEST']._serialized_end=86767 + _globals['_CHECKRUNERESPONSE']._serialized_start=86769 + _globals['_CHECKRUNERESPONSE']._serialized_end=86803 + _globals['_CREATERUNEREQUEST']._serialized_start=86805 + _globals['_CREATERUNEREQUEST']._serialized_end=86874 + _globals['_CREATERUNERESPONSE']._serialized_start=86876 + _globals['_CREATERUNERESPONSE']._serialized_end=86999 + _globals['_SHOWRUNESREQUEST']._serialized_start=87001 + _globals['_SHOWRUNESREQUEST']._serialized_end=87047 + _globals['_SHOWRUNESRESPONSE']._serialized_start=87049 + _globals['_SHOWRUNESRESPONSE']._serialized_end=87104 + _globals['_SHOWRUNESRUNES']._serialized_start=87107 + _globals['_SHOWRUNESRUNES']._serialized_end=87392 + _globals['_SHOWRUNESRUNESRESTRICTIONS']._serialized_start=87394 + _globals['_SHOWRUNESRUNESRESTRICTIONS']._serialized_end=87506 + _globals['_SHOWRUNESRUNESRESTRICTIONSALTERNATIVES']._serialized_start=87508 + _globals['_SHOWRUNESRUNESRESTRICTIONSALTERNATIVES']._serialized_end=87618 + _globals['_ASKRENEUNRESERVEREQUEST']._serialized_start=87620 + _globals['_ASKRENEUNRESERVEREQUEST']._serialized_end=87686 + _globals['_ASKRENEUNRESERVERESPONSE']._serialized_start=87688 + _globals['_ASKRENEUNRESERVERESPONSE']._serialized_end=87714 + _globals['_ASKRENEUNRESERVEPATH']._serialized_start=87716 + _globals['_ASKRENEUNRESERVEPATH']._serialized_end=87832 + _globals['_ASKRENELISTLAYERSREQUEST']._serialized_start=87834 + _globals['_ASKRENELISTLAYERSREQUEST']._serialized_end=87890 + _globals['_ASKRENELISTLAYERSRESPONSE']._serialized_start=87892 + _globals['_ASKRENELISTLAYERSRESPONSE']._serialized_end=87965 + _globals['_ASKRENELISTLAYERSLAYERS']._serialized_start=87968 + _globals['_ASKRENELISTLAYERSLAYERS']._serialized_end=88353 + _globals['_ASKRENELISTLAYERSLAYERSCREATEDCHANNELS']._serialized_start=88356 + _globals['_ASKRENELISTLAYERSLAYERSCREATEDCHANNELS']._serialized_end=88495 + _globals['_ASKRENELISTLAYERSLAYERSCHANNELUPDATES']._serialized_start=88498 + _globals['_ASKRENELISTLAYERSLAYERSCHANNELUPDATES']._serialized_end=88922 + _globals['_ASKRENELISTLAYERSLAYERSCONSTRAINTS']._serialized_start=88925 + _globals['_ASKRENELISTLAYERSLAYERSCONSTRAINTS']._serialized_end=89173 + _globals['_ASKRENELISTLAYERSLAYERSBIASES']._serialized_start=89175 + _globals['_ASKRENELISTLAYERSLAYERSBIASES']._serialized_end=89292 + _globals['_ASKRENECREATELAYERREQUEST']._serialized_start=89294 + _globals['_ASKRENECREATELAYERREQUEST']._serialized_end=89376 + _globals['_ASKRENECREATELAYERRESPONSE']._serialized_start=89378 + _globals['_ASKRENECREATELAYERRESPONSE']._serialized_end=89453 + _globals['_ASKRENECREATELAYERLAYERS']._serialized_start=89456 + _globals['_ASKRENECREATELAYERLAYERS']._serialized_end=89826 + _globals['_ASKRENECREATELAYERLAYERSCREATEDCHANNELS']._serialized_start=89829 + _globals['_ASKRENECREATELAYERLAYERSCREATEDCHANNELS']._serialized_end=89969 + _globals['_ASKRENECREATELAYERLAYERSCHANNELUPDATES']._serialized_start=89972 + _globals['_ASKRENECREATELAYERLAYERSCHANNELUPDATES']._serialized_end=90309 + _globals['_ASKRENECREATELAYERLAYERSCONSTRAINTS']._serialized_start=90312 + _globals['_ASKRENECREATELAYERLAYERSCONSTRAINTS']._serialized_end=90508 + _globals['_ASKRENECREATELAYERLAYERSBIASES']._serialized_start=90510 + _globals['_ASKRENECREATELAYERLAYERSBIASES']._serialized_end=90628 + _globals['_ASKRENEREMOVELAYERREQUEST']._serialized_start=90630 + _globals['_ASKRENEREMOVELAYERREQUEST']._serialized_end=90672 + _globals['_ASKRENEREMOVELAYERRESPONSE']._serialized_start=90674 + _globals['_ASKRENEREMOVELAYERRESPONSE']._serialized_end=90702 + _globals['_ASKRENERESERVEREQUEST']._serialized_start=90704 + _globals['_ASKRENERESERVEREQUEST']._serialized_end=90766 + _globals['_ASKRENERESERVERESPONSE']._serialized_start=90768 + _globals['_ASKRENERESERVERESPONSE']._serialized_end=90792 + _globals['_ASKRENERESERVEPATH']._serialized_start=90794 + _globals['_ASKRENERESERVEPATH']._serialized_end=90908 + _globals['_ASKRENEAGEREQUEST']._serialized_start=90910 + _globals['_ASKRENEAGEREQUEST']._serialized_end=90960 + _globals['_ASKRENEAGERESPONSE']._serialized_start=90962 + _globals['_ASKRENEAGERESPONSE']._serialized_end=91018 + _globals['_GETROUTESREQUEST']._serialized_start=91021 + _globals['_GETROUTESREQUEST']._serialized_end=91272 + _globals['_GETROUTESRESPONSE']._serialized_start=91274 + _globals['_GETROUTESRESPONSE']._serialized_end=91356 + _globals['_GETROUTESROUTES']._serialized_start=91359 + _globals['_GETROUTESROUTES']._serialized_end=91515 + _globals['_GETROUTESROUTESPATH']._serialized_start=91518 + _globals['_GETROUTESROUTESPATH']._serialized_end=91670 + _globals['_ASKRENEDISABLENODEREQUEST']._serialized_start=91672 + _globals['_ASKRENEDISABLENODEREQUEST']._serialized_end=91728 + _globals['_ASKRENEDISABLENODERESPONSE']._serialized_start=91730 + _globals['_ASKRENEDISABLENODERESPONSE']._serialized_end=91758 + _globals['_ASKRENEINFORMCHANNELREQUEST']._serialized_start=91761 + _globals['_ASKRENEINFORMCHANNELREQUEST']._serialized_end=92094 + _globals['_ASKRENEINFORMCHANNELREQUEST_ASKRENEINFORMCHANNELINFORM']._serialized_start=91963 + _globals['_ASKRENEINFORMCHANNELREQUEST_ASKRENEINFORMCHANNELINFORM']._serialized_end=92042 + _globals['_ASKRENEINFORMCHANNELRESPONSE']._serialized_start=92096 + _globals['_ASKRENEINFORMCHANNELRESPONSE']._serialized_end=92185 + _globals['_ASKRENEINFORMCHANNELCONSTRAINTS']._serialized_start=92188 + _globals['_ASKRENEINFORMCHANNELCONSTRAINTS']._serialized_end=92399 + _globals['_ASKRENECREATECHANNELREQUEST']._serialized_start=92402 + _globals['_ASKRENECREATECHANNELREQUEST']._serialized_end=92545 + _globals['_ASKRENECREATECHANNELRESPONSE']._serialized_start=92547 + _globals['_ASKRENECREATECHANNELRESPONSE']._serialized_end=92577 + _globals['_ASKRENEUPDATECHANNELREQUEST']._serialized_start=92580 + _globals['_ASKRENEUPDATECHANNELREQUEST']._serialized_end=93009 + _globals['_ASKRENEUPDATECHANNELRESPONSE']._serialized_start=93011 + _globals['_ASKRENEUPDATECHANNELRESPONSE']._serialized_end=93041 + _globals['_ASKRENEBIASCHANNELREQUEST']._serialized_start=93044 + _globals['_ASKRENEBIASCHANNELREQUEST']._serialized_end=93208 + _globals['_ASKRENEBIASCHANNELRESPONSE']._serialized_start=93210 + _globals['_ASKRENEBIASCHANNELRESPONSE']._serialized_end=93285 + _globals['_ASKRENEBIASCHANNELBIASES']._serialized_start=93287 + _globals['_ASKRENEBIASCHANNELBIASES']._serialized_end=93414 + _globals['_ASKRENELISTRESERVATIONSREQUEST']._serialized_start=93416 + _globals['_ASKRENELISTRESERVATIONSREQUEST']._serialized_end=93448 + _globals['_ASKRENELISTRESERVATIONSRESPONSE']._serialized_start=93450 + _globals['_ASKRENELISTRESERVATIONSRESPONSE']._serialized_end=93547 + _globals['_ASKRENELISTRESERVATIONSRESERVATIONS']._serialized_start=93550 + _globals['_ASKRENELISTRESERVATIONSRESERVATIONS']._serialized_end=93695 + _globals['_INJECTPAYMENTONIONREQUEST']._serialized_start=93698 + _globals['_INJECTPAYMENTONIONREQUEST']._serialized_end=94029 + _globals['_INJECTPAYMENTONIONRESPONSE']._serialized_start=94031 + _globals['_INJECTPAYMENTONIONRESPONSE']._serialized_end=94150 + _globals['_INJECTONIONMESSAGEREQUEST']._serialized_start=94152 + _globals['_INJECTONIONMESSAGEREQUEST']._serialized_end=94214 + _globals['_INJECTONIONMESSAGERESPONSE']._serialized_start=94216 + _globals['_INJECTONIONMESSAGERESPONSE']._serialized_end=94244 + _globals['_XPAYREQUEST']._serialized_start=94247 + _globals['_XPAYREQUEST']._serialized_end=94526 + _globals['_XPAYRESPONSE']._serialized_start=94529 + _globals['_XPAYRESPONSE']._serialized_end=94690 + _globals['_SIGNMESSAGEWITHKEYREQUEST']._serialized_start=94692 + _globals['_SIGNMESSAGEWITHKEYREQUEST']._serialized_end=94753 + _globals['_SIGNMESSAGEWITHKEYRESPONSE']._serialized_start=94755 + _globals['_SIGNMESSAGEWITHKEYRESPONSE']._serialized_end=94851 + _globals['_STREAMBLOCKADDEDREQUEST']._serialized_start=94853 + _globals['_STREAMBLOCKADDEDREQUEST']._serialized_end=94878 + _globals['_BLOCKADDEDNOTIFICATION']._serialized_start=94880 + _globals['_BLOCKADDEDNOTIFICATION']._serialized_end=94934 + _globals['_STREAMCHANNELOPENFAILEDREQUEST']._serialized_start=94936 + _globals['_STREAMCHANNELOPENFAILEDREQUEST']._serialized_end=94968 + _globals['_CHANNELOPENFAILEDNOTIFICATION']._serialized_start=94970 + _globals['_CHANNELOPENFAILEDNOTIFICATION']._serialized_end=95021 + _globals['_STREAMCHANNELOPENEDREQUEST']._serialized_start=95023 + _globals['_STREAMCHANNELOPENEDREQUEST']._serialized_end=95051 + _globals['_CHANNELOPENEDNOTIFICATION']._serialized_start=95053 + _globals['_CHANNELOPENEDNOTIFICATION']._serialized_end=95172 + _globals['_STREAMCONNECTREQUEST']._serialized_start=95174 + _globals['_STREAMCONNECTREQUEST']._serialized_end=95196 + _globals['_PEERCONNECTNOTIFICATION']._serialized_start=95199 + _globals['_PEERCONNECTNOTIFICATION']._serialized_end=95389 + _globals['_PEERCONNECTNOTIFICATION_PEERCONNECTDIRECTION']._serialized_start=95350 + _globals['_PEERCONNECTNOTIFICATION_PEERCONNECTDIRECTION']._serialized_end=95389 + _globals['_PEERCONNECTADDRESS']._serialized_start=95392 + _globals['_PEERCONNECTADDRESS']._serialized_end=95674 + _globals['_PEERCONNECTADDRESS_PEERCONNECTADDRESSTYPE']._serialized_start=95543 + _globals['_PEERCONNECTADDRESS_PEERCONNECTADDRESSTYPE']._serialized_end=95642 + _globals['_STREAMCUSTOMMSGREQUEST']._serialized_start=95676 + _globals['_STREAMCUSTOMMSGREQUEST']._serialized_end=95700 + _globals['_CUSTOMMSGNOTIFICATION']._serialized_start=95702 + _globals['_CUSTOMMSGNOTIFICATION']._serialized_end=95759 + _globals['_STREAMCHANNELSTATECHANGEDREQUEST']._serialized_start=95761 + _globals['_STREAMCHANNELSTATECHANGEDREQUEST']._serialized_end=95795 + _globals['_CHANNELSTATECHANGEDNOTIFICATION']._serialized_start=95798 + _globals['_CHANNELSTATECHANGEDNOTIFICATION']._serialized_end=96230 + _globals['_CHANNELSTATECHANGEDNOTIFICATION_CHANNELSTATECHANGEDCAUSE']._serialized_start=96096 + _globals['_CHANNELSTATECHANGEDNOTIFICATION_CHANNELSTATECHANGEDCAUSE']._serialized_end=96195 + _globals['_NODE']._serialized_start=96233 + _globals['_NODE']._serialized_end=106578 # @@protoc_insertion_point(module_scope) diff --git a/contrib/pyln-testing/pyln/testing/grpc2py.py b/contrib/pyln-testing/pyln/testing/grpc2py.py index b1c3adce39ff..1b40a4c14014 100644 --- a/contrib/pyln-testing/pyln/testing/grpc2py.py +++ b/contrib/pyln-testing/pyln/testing/grpc2py.py @@ -2097,6 +2097,22 @@ def waitblockheight2py(m): }) +def wait_chainmoves2py(m): + return remove_default({ + "account": m.account, # PrimitiveField in generate_composite + "credit_msat": amount2msat(m.credit_msat), # PrimitiveField in generate_composite + "debit_msat": amount2msat(m.debit_msat), # PrimitiveField in generate_composite + }) + + +def wait_channelmoves2py(m): + return remove_default({ + "account": m.account, # PrimitiveField in generate_composite + "credit_msat": amount2msat(m.credit_msat), # PrimitiveField in generate_composite + "debit_msat": amount2msat(m.debit_msat), # PrimitiveField in generate_composite + }) + + def wait_details2py(m): return remove_default({ "status": str(m.status), # EnumField in generate_composite diff --git a/db/bindings.c b/db/bindings.c index 63a224587d86..ceb251f304c8 100644 --- a/db/bindings.c +++ b/db/bindings.c @@ -140,6 +140,15 @@ void db_bind_txid(struct db_stmt *stmt, const struct bitcoin_txid *t) db_bind_sha256d(stmt, &t->shad); } +void db_bind_outpoint(struct db_stmt *stmt, const struct bitcoin_outpoint *o) +{ + u8 *ser = tal_arr(stmt, u8, sizeof(o->txid.shad.sha.u.u8) + sizeof(be32)); + be32 be_vout = cpu_to_be32(o->n); + memcpy(ser, o->txid.shad.sha.u.u8, sizeof(o->txid.shad.sha.u.u8)); + memcpy(ser + sizeof(o->txid.shad.sha.u.u8), &be_vout, sizeof(be32)); + db_bind_blob(stmt, ser, tal_bytelen(ser)); +} + void db_bind_channel_id(struct db_stmt *stmt, const struct channel_id *id) { db_bind_blob(stmt, id->id, sizeof(id->id)); @@ -618,6 +627,18 @@ void db_col_txid(struct db_stmt *stmt, const char *colname, struct bitcoin_txid db_col_sha256d(stmt, colname, &t->shad); } +void db_col_outpoint(struct db_stmt *stmt, const char *colname, struct bitcoin_outpoint *o) +{ + be32 be_vout; + size_t col = db_query_colnum(stmt, colname); + const u8 *raw; + assert(db_column_bytes(stmt, col) == sizeof(o->txid.shad.sha.u.u8) + sizeof(be32)); + raw = db_column_blob(stmt, col); + memcpy(o->txid.shad.sha.u.u8, raw, sizeof(o->txid.shad.sha.u.u8)); + memcpy(&be_vout, raw + sizeof(o->txid.shad.sha.u.u8), sizeof(be_vout)); + o->n = be32_to_cpu(be_vout); +} + struct onionreply *db_col_onionreply(const tal_t *ctx, struct db_stmt *stmt, const char *colname) { diff --git a/db/bindings.h b/db/bindings.h index 910b07a319e0..d6b02fcfea41 100644 --- a/db/bindings.h +++ b/db/bindings.h @@ -30,6 +30,7 @@ void db_bind_sha256d(struct db_stmt *stmt, const struct sha256_double *s); void db_bind_secret(struct db_stmt *stmt, const struct secret *s); void db_bind_secret_arr(struct db_stmt *stmt, const struct secret *s); void db_bind_txid(struct db_stmt *stmt, const struct bitcoin_txid *t); +void db_bind_outpoint(struct db_stmt *stmt, const struct bitcoin_outpoint *o); void db_bind_channel_id(struct db_stmt *stmt, const struct channel_id *id); void db_bind_channel_type(struct db_stmt *stmt, const struct channel_type *type); void db_bind_node_id(struct db_stmt *stmt, const struct node_id *ni); @@ -100,6 +101,7 @@ struct timeabs db_col_timeabs(struct db_stmt *stmt, const char *colname); struct bitcoin_tx *db_col_tx(const tal_t *ctx, struct db_stmt *stmt, const char *colname); struct wally_psbt *db_col_psbt(const tal_t *ctx, struct db_stmt *stmt, const char *colname); struct bitcoin_tx *db_col_psbt_to_tx(const tal_t *ctx, struct db_stmt *stmt, const char *colname); +void db_col_outpoint(struct db_stmt *stmt, const char *colname, struct bitcoin_outpoint *o); struct onionreply *db_col_onionreply(const tal_t *ctx, struct db_stmt *stmt, const char *colname); diff --git a/db/common.h b/db/common.h index 05436ef605a3..1ffd26344e4f 100644 --- a/db/common.h +++ b/db/common.h @@ -183,7 +183,7 @@ struct db_config { u64 (*last_insert_id_fn)(struct db_stmt *stmt); size_t (*count_changes_fn)(struct db_stmt *stmt); - bool (*setup_fn)(struct db *db); + bool (*setup_fn)(struct db *db, bool create); void (*teardown_fn)(struct db *db); bool (*vacuum_fn)(struct db *db); diff --git a/db/db_postgres.c b/db/db_postgres.c index 68cfd956187b..ae9785c6da85 100644 --- a/db/db_postgres.c +++ b/db/db_postgres.c @@ -14,7 +14,7 @@ #define INT4OID 23 #define TEXTOID 25 -static bool db_postgres_setup(struct db *db) +static bool db_postgres_setup(struct db *db, bool create) { size_t prefix_len = strlen("postgres://"); diff --git a/db/db_sqlite3.c b/db/db_sqlite3.c index 07ded41f6ea3..bf2b60b775df 100644 --- a/db/db_sqlite3.c +++ b/db/db_sqlite3.c @@ -98,20 +98,23 @@ static const char *db_sqlite3_fmt_error(struct db_stmt *stmt) sqlite3_errmsg(conn2sql(stmt->db->conn))); } -static bool db_sqlite3_setup(struct db *db) +static bool db_sqlite3_setup(struct db *db, bool create) { char *filename; char *sep; char *backup_filename = NULL; sqlite3_stmt *stmt; sqlite3 *sql; - int err, flags = SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE; - + int err, flags; struct db_sqlite3 *wrapper; if (!strstarts(db->filename, "sqlite3://") || strlen(db->filename) < 10) db_fatal(db, "Could not parse the wallet DSN: %s", db->filename); + flags = SQLITE_OPEN_READWRITE; + if (create) + flags |= SQLITE_OPEN_CREATE; + /* Strip the scheme from the dsn. */ filename = db->filename + strlen("sqlite3://"); /* Look for a replica specification. */ @@ -126,8 +129,11 @@ static bool db_sqlite3_setup(struct db *db) db->conn = wrapper; err = sqlite3_open_v2(filename, &sql, flags, NULL); - if (err != SQLITE_OK) { + /* Note: even on error, the sql connection is allocated! */ + sqlite3_close(sql); + if (!create) + return false; db_fatal(db, "failed to open database %s: %s", filename, sqlite3_errstr(err)); } diff --git a/db/utils.c b/db/utils.c index 70389907a251..155efbd44cc7 100644 --- a/db/utils.c +++ b/db/utils.c @@ -333,6 +333,7 @@ void db_warn(const struct db *db, const char *fmt, ...) struct db *db_open_(const tal_t *ctx, const char *filename, bool developer, + bool create, void (*errorfn)(void *arg, bool fatal, const char *fmt, va_list ap), void *arg) { @@ -356,7 +357,6 @@ struct db *db_open_(const tal_t *ctx, const char *filename, if (!db->queries) db_fatal(db, "Unable to find DB queries for %s", db->config->name); - tal_add_destructor(db, destroy_db); db->in_transaction = NULL; db->changes = NULL; @@ -365,10 +365,15 @@ struct db *db_open_(const tal_t *ctx, const char *filename, trace_span_start("db_setup", db); db_prepare_for_changes(db); - if (db->config->setup_fn && !db->config->setup_fn(db)) - db_fatal(db, "Error calling DB setup: %s", db->error); + if (!db->config->setup_fn(db, create)) { + if (create) + db_fatal(db, "Error calling DB setup: %s", db->error); + trace_span_end(db); + return tal_free(db); + } db_report_changes(db, NULL, 0); trace_span_end(db); + tal_add_destructor(db, destroy_db); return db; } diff --git a/db/utils.h b/db/utils.h index 0cb699ee89b6..859741f82d92 100644 --- a/db/utils.h +++ b/db/utils.h @@ -83,15 +83,16 @@ struct db_stmt *db_prepare_v2_(const char *location, struct db *db, const char *query_id); /** - * db_open - Open or create a database + * db_open - Open or create (if create set) a database */ -#define db_open(ctx, filename, developer, errfn, arg) \ - db_open_((ctx), (filename), (developer), \ +#define db_open(ctx, filename, developer, create, errfn, arg) \ + db_open_((ctx), (filename), (developer), (create), \ typesafe_cb_postargs(void, void *, (errfn), (arg), \ bool, const char *, va_list), \ (arg)) struct db *db_open_(const tal_t *ctx, const char *filename, bool developer, + bool create, void (*errorfn)(void *arg, bool fatal, const char *fmt, va_list ap), void *arg); diff --git a/doc/Makefile b/doc/Makefile index e4707f7fcd64..f6f52a1c5efa 100644 --- a/doc/Makefile +++ b/doc/Makefile @@ -76,6 +76,8 @@ MARKDOWNPAGES := doc/addgossip.7 \ doc/invoicerequest.7 \ doc/keysend.7 \ doc/listaddresses.7 \ + doc/listchainmoves.7 \ + doc/listchannelmoves.7 \ doc/listchannels.7 \ doc/listclosedchannels.7 \ doc/listconfigs.7 \ diff --git a/doc/index.rst b/doc/index.rst index 9ef9149fcfde..900821b6c566 100644 --- a/doc/index.rst +++ b/doc/index.rst @@ -90,6 +90,8 @@ Core Lightning Documentation lightningd-config lightningd-rpc listaddresses + listchainmoves + listchannelmoves listchannels listclosedchannels listconfigs diff --git a/doc/lightningd-config.5.md b/doc/lightningd-config.5.md index b57e9cb19a1e..7a6d9fcd8f31 100644 --- a/doc/lightningd-config.5.md +++ b/doc/lightningd-config.5.md @@ -293,18 +293,6 @@ authenticate with username `user` and password `pass`, and then use the database `db_name`. The database must exist, but the schema will be managed automatically by `lightningd`. -* **bookkeeper-dir**=*DIR* [plugin `bookkeeper`] - - Directory to keep the accounts.sqlite3 database file in. -Defaults to lightning-dir. - -* **bookkeeper-db**=*DSN* [plugin `bookkeeper`] - - Identify the location of the bookkeeper data. This is a fully qualified data source -name, including a scheme such as `sqlite3` or `postgres` followed by the -connection parameters. -Defaults to `sqlite3://accounts.sqlite3` in the `bookkeeper-dir`. - * **encrypted-hsm** If set, you will be prompted to enter a password used to encrypt the `hsm_secret`. diff --git a/doc/schemas/listchainmoves.json b/doc/schemas/listchainmoves.json new file mode 100644 index 000000000000..d718b0b8fa24 --- /dev/null +++ b/doc/schemas/listchainmoves.json @@ -0,0 +1,200 @@ +{ + "$schema": "../rpc-schema-draft.json", + "type": "object", + "rpc": "listchainmoves", + "title": "Command to get the audit list of all onchain coin movements.", + "added": "v25.09", + "description": [ + "The **listchainmoves** command returns the confirmed balance changes onchain over time." + ], + "categories": [ + "readonly" + ], + "request": { + "required": [], + "additionalProperties": false, + "properties": { + "index": { + "type": "string", + "enum": [ + "created" + ], + "description": [ + "How to interpret `start` and `limit`" + ] + }, + "start": { + "type": "u64", + "description": [ + "If `index` is specified, `start` may be specified to start from that value, which is generally returned from lightning-wait(7)." + ] + }, + "limit": { + "type": "u32", + "description": [ + "If `index` is specified, `limit` can be used to specify the maximum number of entries to return." + ] + } + }, + "dependentUpon": { + "index": [ + "start", + "limit" + ] + } + }, + "response": { + "required": [ + "chainmoves" + ], + "additionalProperties": false, + "properties": { + "chainmoves": { + "type": "array", + "items": { + "type": "object", + "additionalProperties": false, + "required": [ + "created_index", + "account_id", + "primary_tag", + "extra_tags", + "credit_msat", + "debit_msat", + "timestamp", + "utxo", + "output_msat", + "blockheight" + ], + "properties": { + "created_index": { + "type": "u64", + "description": [ + "1-based index indicating order this move was created in." + ] + }, + "account_id": { + "type": "string", + "description": [ + "This is either the channel_id corresponding to the channel involved, or a string such as `wallet` for the internal wallet, or `external` for some other external source." + ] + }, + "credit_msat": { + "type": "msat", + "description": [ + "Amount credited (one of this or debit_msat is zero)" + ] + }, + "debit_msat": { + "type": "msat", + "description": [ + "Amount debited (one of this or credit_msat is zero)" + ] + }, + "timestamp": { + "type": "u64", + "description": [ + "Time of this event in seconds since January 1 1970 UTC" + ] + }, + "primary_tag": { + "type": "string", + "enum": [ + "deposit", + "withdrawal", + "penalty", + "channel_open", + "channel_close", + "delayed_to_us", + "htlc_tx", + "htlc_timeout", + "htlc_fulfill", + "to_wallet", + "anchor", + "to_them", + "penalized", + "stolen", + "to_miner" + ], + "description": [ + "A set of one or more tags defining the nature of the change" + ] + }, + "extra_tags": { + "type": "array", + "items": { + "type": "string", + "enum": [ + "opener", + "leased", + "stealable", + "splice", + "foreign" + ] + }, + "description": [ + "A set of additional tags expanding on the details" + ] + }, + "peer_id": { + "type": "pubkey", + "description": [ + "The lightning peer associated with this onchain event" + ] + }, + "originating_account": { + "type": "string", + "description": [ + "This is either a channel_id corresponding to the source channel involved, or a string such as `wallet` for the internal wallet." + ] + }, + "spending_txid": { + "type": "txid", + "description": [ + "The transaction ID which did the spending." + ] + }, + "utxo": { + "type": "outpoint", + "description": [ + "The txid and outpoint number spent for this balance change." + ] + }, + "payment_hash": { + "type": "hash", + "description": [ + "The payment hash associated with this balance change." + ] + }, + "output_msat": { + "type": "msat", + "description": [ + "The output amount (always a whole number of sats). Note that in some cases (e.g. channel opens), not all these belong to us." + ] + }, + "output_count": { + "type": "u32", + "description": [ + "The number of outputs in the `txid` (so you can tell once you've seen events for all of them)." + ] + }, + "blockheight": { + "type": "u32", + "description": [ + "The block number where `txid` appeared (alternately, where `utxo` was spent)." + ] + } + } + } + } + } + }, + "errors": [ + "On failure, one of the following error codes may be returned:", + "", + "- -32602: Error in given parameters." + ], + "resources": [ + "Main web site: " + ] +} diff --git a/doc/schemas/listchannelmoves.json b/doc/schemas/listchannelmoves.json new file mode 100644 index 000000000000..a803136c5391 --- /dev/null +++ b/doc/schemas/listchannelmoves.json @@ -0,0 +1,149 @@ +{ + "$schema": "../rpc-schema-draft.json", + "type": "object", + "rpc": "listchannelmoves", + "title": "Command to get the audit list of all channel coin movements.", + "added": "v25.09", + "description": [ + "The **listchannelmoves** command returns the confirmed balance changes within lightning channels over time." + ], + "categories": [ + "readonly" + ], + "request": { + "required": [], + "additionalProperties": false, + "properties": { + "index": { + "type": "string", + "enum": [ + "created" + ], + "description": [ + "How to interpret `start` and `limit`" + ] + }, + "start": { + "type": "u64", + "description": [ + "If `index` is specified, `start` may be specified to start from that value, which is generally returned from lightning-wait(7)." + ] + }, + "limit": { + "type": "u32", + "description": [ + "If `index` is specified, `limit` can be used to specify the maximum number of entries to return." + ] + } + }, + "dependentUpon": { + "index": [ + "start", + "limit" + ] + } + }, + "response": { + "required": [ + "channelmoves" + ], + "additionalProperties": false, + "properties": { + "channelmoves": { + "type": "array", + "items": { + "type": "object", + "additionalProperties": false, + "required": [ + "created_index", + "account_id", + "primary_tag", + "credit_msat", + "debit_msat", + "timestamp", + "fees_msat" + ], + "properties": { + "created_index": { + "type": "u64", + "description": [ + "1-based index indicating order this move was created in." + ] + }, + "account_id": { + "type": "string", + "description": [ + "The channel_id corresponding to the channel involved." + ] + }, + "credit_msat": { + "type": "msat", + "description": [ + "Amount credited (one of this or debit_msat is zero)" + ] + }, + "debit_msat": { + "type": "msat", + "description": [ + "Amount debited (one of this or credit_msat is zero)" + ] + }, + "timestamp": { + "type": "u64", + "description": [ + "Time of this event in seconds since January 1 1970 UTC" + ] + }, + "primary_tag": { + "type": "string", + "enum": [ + "invoice", + "routed", + "pushed", + "lease_fee", + "channel_proposed", + "penalty_adj", + "journal" + ], + "description": [ + "A set of one or more tags defining the nature of the change" + ] + }, + "payment_hash": { + "type": "hash", + "description": [ + "The hash associated with this payment (not present for leases or push funding)" + ] + }, + "part_id": { + "type": "u64", + "description": [ + "The part_id for the payment (the `payment_hash`, `group_id`, `part_id` tuple will be unique)" + ] + }, + "group_id": { + "type": "u64", + "description": [ + "The group_id for the payment (the `payment_hash`, `group_id`, `part_id` tuple will be unique)" + ] + }, + "fees_msat": { + "type": "msat", + "description": [ + "The fees paid for this payment" + ] + } + } + } + } + } + }, + "errors": [ + "On failure, one of the following error codes may be returned:", + "", + "- -32602: Error in given parameters." + ], + "resources": [ + "Main web site: " + ] +} diff --git a/doc/schemas/sql-template.json b/doc/schemas/sql-template.json index eb3327466228..9a1107b85683 100644 --- a/doc/schemas/sql-template.json +++ b/doc/schemas/sql-template.json @@ -112,7 +112,7 @@ "* json_group_array" ], "tables": [ - "Note that the first column of every table is a unique integer called `rowid`: this is used for related tables to refer to specific rows in their parent. sqlite3 usually has this as an implicit column, but we make it explicit as the implicit version is not allowed to be used as a foreign key.", + "Note that tables which have a `created_index` field use that as the primary key (and `rowid` is an alias to this), otherwise an explicit `rowid` integer primary key is generated, whose value changes on each refresh. This field is used for related tables to refer to specific rows in their parent. (sqlite3 usually has this as an implicit column, but we make it explicit as the implicit version is not allowed to be used as a foreign key).", "" ], "errors": [ @@ -123,8 +123,17 @@ ], "see_also": [ "lightning-listtransactions(7)", + "lightning-listhtlcs(7)", + "lightning-listinvoices(7)", + "lightning-listoffers(7)", "lightning-listchannels(7)", "lightning-listpeers(7)", + "lightning-listpeerchannels(7)", + "lightning-listsendpays(7)", + "lightning-listchainmoves(7)", + "lightning-listchannelmoves(7)", + "lightning-bkpr-listaccountevents(7)", + "lightning-bkpr-listincome(7)", "lightning-listnodes(7)", "lightning-listforwards(7)" ], diff --git a/doc/schemas/wait.json b/doc/schemas/wait.json index 3f59ad8a1b69..f5bb18b6dbf3 100644 --- a/doc/schemas/wait.json +++ b/doc/schemas/wait.json @@ -22,13 +22,17 @@ " `invoices`: corresponding to `listinvoices` (added in *v23.08*).", " `sendpays`: corresponding to `listsendpays` (added in *v23.11*).", " `forwards`: corresponding to `listforwards` (added in *v23.11*).", - " `htlcs`: corresponding to `listhtlcs` (added in *v25.05*)." + " `htlcs`: corresponding to `listhtlcs` (added in *v25.05*).", + " `chainmoves`: corresponding to `listchainmoves` (added in *v25.09*).", + " `channelmoves`: corresponding to `listchannelmoves` (added in *v25.09*)." ], "enum": [ "invoices", "forwards", "sendpays", - "htlcs" + "htlcs", + "chainmoves", + "channelmoves" ] }, "indexname": { @@ -65,7 +69,9 @@ "invoices", "forwards", "sendpays", - "htlcs" + "htlcs", + "chainmoves", + "channelmoves" ] }, "created": { @@ -90,6 +96,8 @@ "invoices": {}, "sendpays": {}, "htlcs": {}, + "chainmoves": {}, + "channelmoves": {}, "details": {} }, "allOf": [ @@ -527,6 +535,118 @@ } } } + }, + { + "if": { + "additionalProperties": true, + "properties": { + "subsystem": { + "type": "string", + "enum": [ + "chainmoves" + ] + } + } + }, + "then": { + "additionalProperties": false, + "properties": { + "subsystem": {}, + "created": {}, + "updated": {}, + "deleted": {}, + "channelmoves": {}, + "chainmoves": { + "added": "v25.09", + "type": "object", + "additionalProperties": false, + "required": [ + "account", + "credit_msat", + "debit_msat" + ], + "properties": { + "account": { + "added": "v25.09", + "type": "string", + "description": [ + "The account (e.g. channel id) associated with this movement." + ] + }, + "credit_msat": { + "added": "v25.09", + "type": "msat", + "description": [ + "The amount incoming" + ] + }, + "debit_msat": { + "added": "v25.09", + "type": "msat", + "description": [ + "The amount outgoing" + ] + } + } + } + } + } + }, + { + "if": { + "additionalProperties": true, + "properties": { + "subsystem": { + "type": "string", + "enum": [ + "channelmoves" + ] + } + } + }, + "then": { + "additionalProperties": false, + "properties": { + "subsystem": {}, + "created": {}, + "updated": {}, + "deleted": {}, + "chainmoves": {}, + "channelmoves": { + "added": "v25.09", + "type": "object", + "additionalProperties": false, + "required": [ + "account", + "credit_msat", + "debit_msat" + ], + "properties": { + "account": { + "added": "v25.09", + "type": "string", + "description": [ + "The account (e.g. channel id) associated with this movement." + ] + }, + "credit_msat": { + "added": "v25.09", + "type": "msat", + "description": [ + "The amount incoming" + ] + }, + "debit_msat": { + "added": "v25.09", + "type": "msat", + "description": [ + "The amount outgoing" + ] + } + } + } + } + } } ] }, @@ -564,7 +684,9 @@ "lightning-listinvoices(7)", "lightning-listforwards(7)", "lightning-listsendpays(7)", - "lightning-listhtlcs(7)" + "lightning-listhtlcs(7)", + "lightning-listchainmoves(7)", + "lightning-listchannelmoves(7)" ], "resources": [ "Main web site: " diff --git a/lightningd/chaintopology.c b/lightningd/chaintopology.c index dd22ccadbd64..59f004f732a7 100644 --- a/lightningd/chaintopology.c +++ b/lightningd/chaintopology.c @@ -26,6 +26,7 @@ #include #include #include +#include /* Mutual recursion via timer. */ static void try_extend_tip(struct chain_topology *topo); @@ -880,7 +881,7 @@ static void record_wallet_spend(struct lightningd *ld, return; } - notify_chain_mvt(ld, new_coin_wallet_withdraw(tmpctx, txid, outpoint, + wallet_save_chain_mvt(ld, new_coin_wallet_withdraw(tmpctx, txid, outpoint, tx_blockheight, utxo->amount, mk_mvt_tags(MVT_WITHDRAWAL))); } diff --git a/lightningd/channel.c b/lightningd/channel.c index d69865b295b3..354249a4c24c 100644 --- a/lightningd/channel.c +++ b/lightningd/channel.c @@ -312,6 +312,20 @@ void channel_add_old_scid(struct channel *channel, chanmap_add(channel->peer->ld, channel, old_scid); } +static void remove_from_dbid_map(struct channel *channel) +{ + if (!channel_dbid_map_del(channel->peer->ld->channels_by_dbid, channel)) + abort(); +} + +void add_channel_to_dbid_map(struct lightningd *ld, + struct channel *channel) +{ + assert(channel->dbid != 0); + channel_dbid_map_add(ld->channels_by_dbid, channel); + tal_add_destructor(channel, remove_from_dbid_map); +} + struct channel *new_unsaved_channel(struct peer *peer, u32 feerate_base, u32 feerate_ppm) @@ -698,6 +712,7 @@ struct channel *new_channel(struct peer *peer, u64 dbid, channel->rr_number = peer->ld->rr_counter++; tal_add_destructor(channel, destroy_channel); + add_channel_to_dbid_map(peer->ld, channel); list_head_init(&channel->inflights); channel->closer = closer; @@ -842,20 +857,7 @@ struct channel *any_channel_by_scid(struct lightningd *ld, struct channel *channel_by_dbid(struct lightningd *ld, const u64 dbid) { - struct peer *p; - struct channel *chan; - struct peer_node_id_map_iter it; - - /* FIXME: Support lookup by id directly! */ - for (p = peer_node_id_map_first(ld->peers, &it); - p; - p = peer_node_id_map_next(ld->peers, &it)) { - list_for_each(&p->channels, chan, list) { - if (chan->dbid == dbid) - return chan; - } - } - return NULL; + return channel_dbid_map_get(ld->channels_by_dbid, dbid); } struct channel *channel_by_cid(struct lightningd *ld, diff --git a/lightningd/channel.h b/lightningd/channel.h index 0e4959e831a2..af2f18a54b8a 100644 --- a/lightningd/channel.h +++ b/lightningd/channel.h @@ -784,6 +784,27 @@ static inline bool channel_state_open_uncommitted(enum channel_state state) abort(); } +static inline size_t channel_dbid_hash(u64 dbid) +{ + return siphash24(siphash_seed(), &dbid, sizeof(dbid)); +} + +static u64 channel_dbid(const struct channel *channel) +{ + assert(channel->dbid); + return channel->dbid; +} + +static bool channel_dbid_eq(const struct channel *channel, u64 dbid) +{ + return channel->dbid == dbid; +} +/* Defines struct channel_dbid_map */ +HTABLE_DEFINE_NODUPS_TYPE(struct channel, + channel_dbid, channel_dbid_hash, channel_dbid_eq, + channel_dbid_map); + +void add_channel_to_dbid_map(struct lightningd *ld, struct channel *channel); void channel_set_owner(struct channel *channel, struct subd *owner); diff --git a/lightningd/channel_control.c b/lightningd/channel_control.c index 93fb13ec16da..a72f959837ae 100644 --- a/lightningd/channel_control.c +++ b/lightningd/channel_control.c @@ -976,7 +976,7 @@ static void channel_record_splice(struct channel *channel, orig_funding_sats, output_count, /* is_splice = */true); - notify_chain_mvt(channel->peer->ld, mvt); + wallet_save_chain_mvt(channel->peer->ld, mvt); } void channel_record_open(struct channel *channel, u32 blockheight, bool record_push) @@ -1022,11 +1022,11 @@ void channel_record_open(struct channel *channel, u32 blockheight, bool record_p channel->opener == LOCAL, is_leased); - notify_chain_mvt(channel->peer->ld, mvt); + wallet_save_chain_mvt(channel->peer->ld, mvt); /* If we pushed sats, *now* record them */ if (is_pushed && record_push) - notify_channel_mvt(channel->peer->ld, + wallet_save_channel_mvt(channel->peer->ld, new_coin_channel_push(tmpctx, channel, channel->opener == REMOTE ? COIN_CREDIT : COIN_DEBIT, channel->push, diff --git a/lightningd/closing_control.c b/lightningd/closing_control.c index bd91a79a4b7e..36fe90f696bb 100644 --- a/lightningd/closing_control.c +++ b/lightningd/closing_control.c @@ -529,19 +529,6 @@ void peer_start_closingd(struct channel *channel, struct peer_fd *peer_fd) subd_send_msg(channel->owner, take(initmsg)); } -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"); -} - static struct command_result *param_feerate_range(struct command *cmd, const char *name, const char *buffer, diff --git a/lightningd/coin_mvts.c b/lightningd/coin_mvts.c index 415a8b9349a2..cc206b62b1bb 100644 --- a/lightningd/coin_mvts.c +++ b/lightningd/coin_mvts.c @@ -1,5 +1,7 @@ #include "config.h" #include +#include +#include #include #include #include @@ -188,11 +190,20 @@ void json_add_chain_mvt_fields(struct json_stream *stream, bool include_tags_arr, bool include_old_utxo_fields, bool include_old_txid_field, - const struct chain_coin_mvt *chain_mvt) + const struct chain_coin_mvt *chain_mvt, + u64 id) { + /* Fields in common with channel moves go first */ + json_add_u64(stream, "created_index", id); + json_add_mvt_account_id(stream, "account_id", &chain_mvt->account); + json_add_amount_msat(stream, "credit_msat", chain_mvt->credit); + json_add_amount_msat(stream, "debit_msat", chain_mvt->debit); + json_add_u64(stream, "timestamp", chain_mvt->timestamp); + add_movement_tags(stream, include_tags_arr, chain_mvt->tags, true); + + json_add_outpoint(stream, "utxo", &chain_mvt->outpoint); if (chain_mvt->peer_id) json_add_node_id(stream, "peer_id", chain_mvt->peer_id); - json_add_mvt_account_id(stream, "account_id", &chain_mvt->account); if (chain_mvt->originating_acct) json_add_mvt_account_id(stream, "originating_account", chain_mvt->originating_acct); @@ -210,30 +221,32 @@ void json_add_chain_mvt_fields(struct json_stream *stream, &chain_mvt->outpoint.txid)); json_add_u32(stream, "vout", chain_mvt->outpoint.n); } - json_add_outpoint(stream, "utxo", &chain_mvt->outpoint); /* on-chain htlcs include a payment hash */ if (chain_mvt->payment_hash) json_add_sha256(stream, "payment_hash", chain_mvt->payment_hash); - json_add_amount_msat(stream, "credit_msat", chain_mvt->credit); - json_add_amount_msat(stream, "debit_msat", chain_mvt->debit); - json_add_amount_sat_msat(stream, "output_msat", chain_mvt->output_val); if (chain_mvt->output_count > 0) json_add_num(stream, "output_count", chain_mvt->output_count); - add_movement_tags(stream, include_tags_arr, chain_mvt->tags, true); json_add_u32(stream, "blockheight", chain_mvt->blockheight); - json_add_u64(stream, "timestamp", chain_mvt->timestamp); } void json_add_channel_mvt_fields(struct json_stream *stream, bool include_tags_arr, const struct channel_coin_mvt *chan_mvt, + u64 id, bool extra_tags_field) { + /* Fields in common with chain moves go first */ + json_add_u64(stream, "created_index", id); json_add_mvt_account_id(stream, "account_id", &chan_mvt->account); + json_add_amount_msat(stream, "credit_msat", chan_mvt->credit); + json_add_amount_msat(stream, "debit_msat", chan_mvt->debit); + json_add_u64(stream, "timestamp", chan_mvt->timestamp); + add_movement_tags(stream, include_tags_arr, chan_mvt->tags, extra_tags_field); + /* push funding / leases don't have a payment_hash */ if (chan_mvt->payment_hash) json_add_sha256(stream, "payment_hash", chan_mvt->payment_hash); @@ -241,10 +254,244 @@ void json_add_channel_mvt_fields(struct json_stream *stream, json_add_u64(stream, "part_id", chan_mvt->part_and_group->part_id); json_add_u64(stream, "group_id", chan_mvt->part_and_group->group_id); } - json_add_amount_msat(stream, "credit_msat", chan_mvt->credit); - json_add_amount_msat(stream, "debit_msat", chan_mvt->debit); json_add_amount_msat(stream, "fees_msat", chan_mvt->fees); +} - add_movement_tags(stream, include_tags_arr, chan_mvt->tags, extra_tags_field); - json_add_u64(stream, "timestamp", chan_mvt->timestamp); +static u64 coinmvt_index_inc(struct lightningd *ld, + struct db *db, + enum wait_subsystem subsys, + const struct mvt_account_id *account, + struct amount_msat credit, + struct amount_msat debit, + enum wait_index idx) +{ + return wait_index_increment(ld, db, subsys, idx, + "account", account->channel ? fmt_channel_id(tmpctx, &account->channel->cid) : account->alt_account, + "=credit_msat", tal_fmt(tmpctx, "%"PRIu64, credit.millisatoshis), /* Raw: JSON output */ + "=debit_msat", tal_fmt(tmpctx, "%"PRIu64, debit.millisatoshis), /* Raw: JSON output */ + NULL); +} + +u64 chain_mvt_index_created(struct lightningd *ld, + struct db *db, + const struct mvt_account_id *account, + struct amount_msat credit, + struct amount_msat debit) +{ + return coinmvt_index_inc(ld, db, WAIT_SUBSYSTEM_CHAINMOVES, + account, credit, debit, + WAIT_INDEX_CREATED); +} + +u64 channel_mvt_index_created(struct lightningd *ld, + struct db *db, + const struct mvt_account_id *account, + struct amount_msat credit, + struct amount_msat debit) +{ + return coinmvt_index_inc(ld, db, WAIT_SUBSYSTEM_CHANNELMOVES, + account, credit, debit, + WAIT_INDEX_CREATED); +} + +static struct command_result *json_listchainmoves(struct command *cmd, + const char *buffer, + const jsmntok_t *obj UNNEEDED, + const jsmntok_t *params) +{ + struct json_stream *response; + struct db_stmt *stmt; + enum wait_index *listindex; + u64 *liststart; + u32 *listlimit; + + if (!param(cmd, buffer, params, + p_opt("index", param_index, &listindex), + p_opt_def("start", param_u64, &liststart, 0), + p_opt("limit", param_u32, &listlimit), + NULL)) + return command_param_failed(); + + if (*liststart != 0 && !listindex) { + return command_fail(cmd, JSONRPC2_INVALID_PARAMS, + "Can only specify {start} with {index}"); + } + if (listlimit && !listindex) { + return command_fail(cmd, JSONRPC2_INVALID_PARAMS, + "Can only specify {limit} with {index}"); + } + if (listindex && *listindex != WAIT_INDEX_CREATED) { + return command_fail(cmd, JSONRPC2_INVALID_PARAMS, + "index must be 'created', since chainmoves are never updated"); + } + response = json_stream_success(cmd); + json_array_start(response, "chainmoves"); + for (stmt = wallet_chain_moves_first(cmd->ld->wallet, *liststart, listlimit); + stmt; + stmt = wallet_chain_moves_next(cmd->ld->wallet, stmt)) { + struct chain_coin_mvt *chain_mvt; + u64 id; + chain_mvt = wallet_chain_move_extract(cmd, stmt, cmd->ld, &id); + json_object_start(response, NULL); + json_add_chain_mvt_fields(response, false, false, false, chain_mvt, id); + json_object_end(response); + } + json_array_end(response); + + return command_success(cmd, response); +} +static const struct json_command listchainmoves_command = { + "listchainmoves", + json_listchainmoves, +}; +AUTODATA(json_command, &listchainmoves_command); + +static struct command_result *json_listchannelmoves(struct command *cmd, + const char *buffer, + const jsmntok_t *obj UNNEEDED, + const jsmntok_t *params) +{ + struct json_stream *response; + struct db_stmt *stmt; + enum wait_index *listindex; + u64 *liststart; + u32 *listlimit; + + if (!param(cmd, buffer, params, + p_opt("index", param_index, &listindex), + p_opt_def("start", param_u64, &liststart, 0), + p_opt("limit", param_u32, &listlimit), + NULL)) + return command_param_failed(); + + if (*liststart != 0 && !listindex) { + return command_fail(cmd, JSONRPC2_INVALID_PARAMS, + "Can only specify {start} with {index}"); + } + if (listlimit && !listindex) { + return command_fail(cmd, JSONRPC2_INVALID_PARAMS, + "Can only specify {limit} with {index}"); + } + if (listindex && *listindex != WAIT_INDEX_CREATED) { + return command_fail(cmd, JSONRPC2_INVALID_PARAMS, + "index must be 'created', since channelmoves are never updated"); + } + + response = json_stream_success(cmd); + json_array_start(response, "channelmoves"); + for (stmt = wallet_channel_moves_first(cmd->ld->wallet, *liststart, listlimit); + stmt; + stmt = wallet_channel_moves_next(cmd->ld->wallet, stmt)) { + struct channel_coin_mvt *chan_mvt; + u64 id; + chan_mvt = wallet_channel_move_extract(cmd, stmt, cmd->ld, &id); + json_object_start(response, NULL); + /* No deprecated tags[], no extra_tags field */ + json_add_channel_mvt_fields(response, false, chan_mvt, id, false); + json_object_end(response); + } + json_array_end(response); + + return command_success(cmd, response); +} +static const struct json_command listchannelmoves_command = { + "listchannelmoves", + json_listchannelmoves, +}; +AUTODATA(json_command, &listchannelmoves_command); + +static struct command_result *param_msat_as_sat(struct command *cmd, + const char *name, + const char *buffer, + const jsmntok_t *tok, + struct amount_sat **sat) +{ + struct amount_msat msat; + + *sat = tal(cmd, struct amount_sat); + if (parse_amount_msat(&msat, buffer + tok->start, tok->end - tok->start) + && amount_msat_to_sat(*sat, msat)) + return NULL; + + return command_fail_badparam(cmd, name, buffer, tok, + "should be a millisatoshi amount"); +} + +/* Internal interfaces for bookkeeper.c. + * FIXME: handle utxo_deposit / utxo_spend notifications directly! */ +static struct command_result *json_injectutxodeposit(struct command *cmd, + const char *buffer, + const jsmntok_t *obj UNNEEDED, + const jsmntok_t *params) +{ + struct chain_coin_mvt *ev; + const char *account, *origin_acct; + struct bitcoin_outpoint *outpoint; + struct amount_sat *amount; + u64 *timestamp; + u32 *blockheight; + + if (!param(cmd, buffer, params, + p_req("account", param_string, &account), + p_req("outpoint", param_outpoint, &outpoint), + p_req("amount_msat", param_msat_as_sat, &amount), + p_req("timestamp", param_u64, ×tamp), + p_req("blockheight", param_u32, &blockheight), + p_opt("transfer_from", param_string, &origin_acct), + NULL)) + return command_param_failed(); + + ev = new_foreign_deposit(cmd, outpoint, *blockheight, *amount, + account, *timestamp); + if (origin_acct) { + /* Need temporary because originating_acct is const */ + struct mvt_account_id *acct; + ev->originating_acct = acct = tal(ev, struct mvt_account_id); + acct->channel = NULL; + acct->alt_account = tal_strdup(acct, origin_acct); + } + wallet_save_chain_mvt(cmd->ld, ev); + + return command_success(cmd, json_stream_success(cmd)); } +static const struct json_command injectutxodeposit_command = { + "injectutxodeposit", + json_injectutxodeposit, +}; +AUTODATA(json_command, &injectutxodeposit_command); + +static struct command_result *json_injectutxospend(struct command *cmd, + const char *buffer, + const jsmntok_t *obj UNNEEDED, + const jsmntok_t *params) +{ + struct chain_coin_mvt *ev; + const char *account; + struct bitcoin_txid *spending_txid; + struct bitcoin_outpoint *outpoint; + struct amount_sat *amount; + u64 *timestamp; + u32 *blockheight; + + if (!param(cmd, buffer, params, + p_req("account", param_string, &account), + p_req("outpoint", param_outpoint, &outpoint), + p_req("spending_txid", param_txid, &spending_txid), + p_req("amount_msat", param_msat_as_sat, &amount), + p_req("timestamp", param_u64, ×tamp), + p_req("blockheight", param_u32, &blockheight), + NULL)) + return command_param_failed(); + + ev = new_foreign_withdrawal(cmd, outpoint, spending_txid, + *amount, *blockheight, account, *timestamp); + wallet_save_chain_mvt(cmd->ld, ev); + + return command_success(cmd, json_stream_success(cmd)); +} +static const struct json_command injectutxospend_command = { + "injectutxospend", + json_injectutxospend, +}; +AUTODATA(json_command, &injectutxospend_command); + diff --git a/lightningd/coin_mvts.h b/lightningd/coin_mvts.h index 3201c2035d37..11f4fd471c20 100644 --- a/lightningd/coin_mvts.h +++ b/lightningd/coin_mvts.h @@ -4,6 +4,9 @@ #include +struct htlc_in; +struct htlc_out; +struct json_stream; struct lightningd; struct account_balance { @@ -44,9 +47,24 @@ void json_add_chain_mvt_fields(struct json_stream *stream, bool include_tags_arr, bool include_old_utxo_fields, bool include_old_txid_field, - const struct chain_coin_mvt *chain_mvt); + const struct chain_coin_mvt *chain_mvt, + u64 id); void json_add_channel_mvt_fields(struct json_stream *stream, bool include_tags_arr, const struct channel_coin_mvt *chan_mvt, + u64 id, bool extra_tags_field); + +/* For db code to get incremental ids */ +u64 chain_mvt_index_created(struct lightningd *ld, + struct db *db, + const struct mvt_account_id *account, + struct amount_msat credit, + struct amount_msat debit); + +u64 channel_mvt_index_created(struct lightningd *ld, + struct db *db, + const struct mvt_account_id *account, + struct amount_msat credit, + struct amount_msat debit); #endif /* LIGHTNING_LIGHTNINGD_COIN_MVTS_H */ diff --git a/lightningd/dual_open_control.c b/lightningd/dual_open_control.c index fe995d3d4bec..3ad5cfb1dbb7 100644 --- a/lightningd/dual_open_control.c +++ b/lightningd/dual_open_control.c @@ -1446,6 +1446,7 @@ wallet_commit_channel(struct lightningd *ld, channel_info->old_remote_per_commit = channel_info->remote_per_commit; /* Promote the unsaved_dbid to the dbid */ + assert(channel->dbid == 0); assert(channel->unsaved_dbid != 0); channel->dbid = channel->unsaved_dbid; channel->unsaved_dbid = 0; @@ -1524,6 +1525,9 @@ wallet_commit_channel(struct lightningd *ld, /* Now we finally put it in the database. */ wallet_channel_insert(ld->wallet, channel); + /* So we can find it by the newly-assigned dbid */ + add_channel_to_dbid_map(ld, channel); + /* Open attempt to channel's inflights */ inflight = new_inflight(channel, NULL, diff --git a/lightningd/forwards.c b/lightningd/forwards.c index 2c001e59e36a..ecb14094c58e 100644 --- a/lightningd/forwards.c +++ b/lightningd/forwards.c @@ -19,7 +19,8 @@ static u64 forward_index_inc(struct lightningd *ld, const struct short_channel_id *out_channel, enum wait_index idx) { - return wait_index_increment(ld, WAIT_SUBSYSTEM_FORWARD, idx, + return wait_index_increment(ld, ld->wallet->db, + WAIT_SUBSYSTEM_FORWARD, idx, "status", forward_status_name(status), "in_channel", fmt_short_channel_id(tmpctx, in_channel), "=in_htlc_id", tal_fmt(tmpctx, "%"PRIu64, in_htlc_id), diff --git a/lightningd/lightningd.c b/lightningd/lightningd.c index d76d5b605470..1c3fb7e4d77a 100644 --- a/lightningd/lightningd.c +++ b/lightningd/lightningd.c @@ -213,6 +213,10 @@ static struct lightningd *new_lightningd(const tal_t *ctx) ld->channels_by_scid = tal(ld, struct channel_scid_map); channel_scid_map_init(ld->channels_by_scid); + /*~ Coin movements in db are indexed by the channel dbid. */ + ld->channels_by_dbid = tal(ld, struct channel_dbid_map); + channel_dbid_map_init(ld->channels_by_dbid); + /*~ For multi-part payments, we need to keep some incoming payments * in limbo until we get all the parts, or we time them out. */ ld->htlc_sets = tal(ld, struct htlc_set_map); @@ -267,6 +271,8 @@ static struct lightningd *new_lightningd(const tal_t *ctx) ld->try_reexec = false; ld->recover_secret = NULL; ld->db_upgrade_ok = NULL; + ld->old_bookkeeper_dir = NULL; + ld->old_bookkeeper_db = NULL; /* --invoices-onchain-fallback */ ld->unified_invoices = false; diff --git a/lightningd/lightningd.h b/lightningd/lightningd.h index 44a76e18d082..ce8b62b017a6 100644 --- a/lightningd/lightningd.h +++ b/lightningd/lightningd.h @@ -218,6 +218,9 @@ struct lightningd { /* Here are all our channels and their aliases */ struct channel_scid_map *channels_by_scid; + /* Open channels by dbid */ + struct channel_dbid_map *channels_by_dbid; + /* Outstanding connect commands. */ struct list_head connects; @@ -380,6 +383,10 @@ struct lightningd { char *wallet_dsn; + /* For migration from old accounts.db */ + char *old_bookkeeper_dir; + char *old_bookkeeper_db; + bool encrypted_hsm; /* What (additional) messages the HSM accepts */ u32 *hsm_capabilities; diff --git a/lightningd/notification.c b/lightningd/notification.c index 88bff300cb32..22761aabd396 100644 --- a/lightningd/notification.c +++ b/lightningd/notification.c @@ -461,7 +461,8 @@ static void json_add_standard_notify_mvt_fields(struct json_stream *stream, REGISTER_NOTIFICATION(coin_movement); void notify_channel_mvt(struct lightningd *ld, - const struct channel_coin_mvt *chan_mvt) + const struct channel_coin_mvt *chan_mvt, + u64 id) { bool include_tags_arr; struct jsonrpc_notification *n = notify_start(ld, "coin_movement"); @@ -473,12 +474,13 @@ void notify_channel_mvt(struct lightningd *ld, json_add_standard_notify_mvt_fields(n->stream, ld, "channel_mvt"); /* Adding (empty) extra_tags field unifies this with notify_chain_mvt */ - json_add_channel_mvt_fields(n->stream, include_tags_arr, chan_mvt, true); + json_add_channel_mvt_fields(n->stream, include_tags_arr, chan_mvt, id, true); notify_send(ld, n); } void notify_chain_mvt(struct lightningd *ld, - const struct chain_coin_mvt *chain_mvt) + const struct chain_coin_mvt *chain_mvt, + u64 id) { bool include_tags_arr, include_old_utxo_fields, include_old_txid_field; struct jsonrpc_notification *n = notify_start(ld, "coin_movement"); @@ -500,7 +502,7 @@ void notify_chain_mvt(struct lightningd *ld, include_tags_arr, include_old_utxo_fields, include_old_txid_field, - chain_mvt); + chain_mvt, id); notify_send(ld, n); } diff --git a/lightningd/notification.h b/lightningd/notification.h index c90f01605069..20794a93a845 100644 --- a/lightningd/notification.h +++ b/lightningd/notification.h @@ -84,10 +84,12 @@ void notify_sendpay_failure(struct lightningd *ld, const char *errmsg); void notify_channel_mvt(struct lightningd *ld, - const struct channel_coin_mvt *chan_mvt); + const struct channel_coin_mvt *chan_mvt, + u64 id); void notify_chain_mvt(struct lightningd *ld, - const struct chain_coin_mvt *chain_mvt); + const struct chain_coin_mvt *chain_mvt, + u64 id); void notify_balance_snapshot(struct lightningd *ld, const struct balance_snapshot *snap); diff --git a/lightningd/onchain_control.c b/lightningd/onchain_control.c index a82665c2ec5a..b82523ab1b38 100644 --- a/lightningd/onchain_control.c +++ b/lightningd/onchain_control.c @@ -17,13 +17,13 @@ #include #include #include -#include #include #include #include #include #include #include +#include #include #include #include @@ -354,8 +354,7 @@ static void handle_onchain_log_coin_move(struct channel *channel, const u8 *msg) mvt->originating_acct = new_mvt_account_id(mvt, channel, NULL); } - notify_chain_mvt(channel->peer->ld, mvt); - tal_free(mvt); + wallet_save_chain_mvt(channel->peer->ld, take(mvt)); } static void handle_onchain_log_penalty_adj(struct channel *channel, const u8 *msg) @@ -369,9 +368,9 @@ static void handle_onchain_log_penalty_adj(struct channel *channel, const u8 *ms } mvt = new_channel_mvt_penalty_adj(tmpctx, channel, msat, COIN_CREDIT); - notify_channel_mvt(channel->peer->ld, mvt); + wallet_save_channel_mvt(channel->peer->ld, mvt); mvt = new_channel_mvt_penalty_adj(tmpctx, channel, msat, COIN_DEBIT); - notify_channel_mvt(channel->peer->ld, mvt); + wallet_save_channel_mvt(channel->peer->ld, mvt); } static void replay_watch_tx(struct channel *channel, @@ -597,7 +596,7 @@ static void onchain_add_utxo(struct channel *channel, const u8 *msg) amount, mk_mvt_tags(MVT_DEPOSIT)); mvt->originating_acct = new_mvt_account_id(mvt, channel, NULL); - notify_chain_mvt(channel->peer->ld, mvt); + wallet_save_chain_mvt(channel->peer->ld, mvt); } static void onchain_annotate_txout(struct channel *channel, const u8 *msg) diff --git a/lightningd/options.c b/lightningd/options.c index 252fcb54e45b..d746c0476e71 100644 --- a/lightningd/options.c +++ b/lightningd/options.c @@ -1603,6 +1603,16 @@ static void register_opts(struct lightningd *ld) "Re-enable a long-deprecated API (which will be removed entirely next version!)"); opt_register_logging(ld); + /* Old bookkeeper migration flags. */ + opt_register_early_arg("--bookkeeper-dir", + opt_set_talstr, NULL, + &ld->old_bookkeeper_dir, + opt_hidden); + opt_register_early_arg("--bookkeeper-db", + opt_set_talstr, NULL, + &ld->old_bookkeeper_db, + opt_hidden); + dev_register_opts(ld); } diff --git a/lightningd/pay.c b/lightningd/pay.c index f7a267813c6e..44931d700ea3 100644 --- a/lightningd/pay.c +++ b/lightningd/pay.c @@ -2118,7 +2118,8 @@ static u64 sendpay_index_inc(struct lightningd *ld, enum payment_status status, enum wait_index idx) { - return wait_index_increment(ld, WAIT_SUBSYSTEM_SENDPAY, idx, + return wait_index_increment(ld, ld->wallet->db, + WAIT_SUBSYSTEM_SENDPAY, idx, "status", payment_status_to_string(status), "=partid", tal_fmt(tmpctx, "%"PRIu64, partid), "=groupid", tal_fmt(tmpctx, "%"PRIu64, groupid), diff --git a/lightningd/peer_htlcs.c b/lightningd/peer_htlcs.c index fef1be92328e..454eb8dcf286 100644 --- a/lightningd/peer_htlcs.c +++ b/lightningd/peer_htlcs.c @@ -1967,7 +1967,7 @@ static void remove_htlc_in(struct channel *channel, struct htlc_in *hin) "Unable to calculate fees collected." " Not logging an inbound HTLC"); else - notify_channel_mvt(channel->peer->ld, mvt); + wallet_save_channel_mvt(channel->peer->ld, mvt); } tal_free(hin); @@ -2018,7 +2018,7 @@ static void remove_htlc_out(struct channel *channel, struct htlc_out *hout) "Unable to calculate fees." " Not logging an outbound HTLC"); else - notify_channel_mvt(channel->peer->ld, mvt); + wallet_save_channel_mvt(channel->peer->ld, mvt); } tal_free(hout); @@ -3043,7 +3043,8 @@ static u64 htlcs_index_inc(struct lightningd *ld, enum htlc_state hstate, enum wait_index idx) { - return wait_index_increment(ld, WAIT_SUBSYSTEM_HTLCS, idx, + return wait_index_increment(ld, ld->wallet->db, + WAIT_SUBSYSTEM_HTLCS, idx, "state", htlc_state_name(hstate), "short_channel_id", fmt_short_channel_id(tmpctx, channel_scid_or_local_alias(channel)), "direction", owner == LOCAL ? "out": "in", diff --git a/lightningd/wait.c b/lightningd/wait.c index 016566a77e6b..4d9066f602a6 100644 --- a/lightningd/wait.c +++ b/lightningd/wait.c @@ -24,6 +24,8 @@ static const char *subsystem_names[] = { "sendpays", "invoices", "htlcs", + "chainmoves", + "channelmoves", }; static const char *index_names[] = { @@ -51,6 +53,8 @@ const char *wait_subsystem_name(enum wait_subsystem subsystem) case WAIT_SUBSYSTEM_SENDPAY: case WAIT_SUBSYSTEM_INVOICE: case WAIT_SUBSYSTEM_HTLCS: + case WAIT_SUBSYSTEM_CHAINMOVES: + case WAIT_SUBSYSTEM_CHANNELMOVES: return subsystem_names[subsystem]; } abort(); @@ -124,6 +128,7 @@ static void json_add_index(struct command *cmd, } static u64 wait_index_bump(struct lightningd *ld, + struct db *db, enum wait_subsystem subsystem, enum wait_index index, u64 num, @@ -138,7 +143,7 @@ static u64 wait_index_bump(struct lightningd *ld, /* FIXME: We can optimize this! It's always the max of the fields in * the table, *unless* we delete one. So we can lazily write this on * delete, and fix it up to MAX() when we startup. */ - db_set_intvar(ld->wallet->db, + db_set_intvar(db, tal_fmt(tmpctx, "last_%s_%s_index", wait_subsystem_name(subsystem), wait_index_name(index)), @@ -168,6 +173,7 @@ static u64 wait_index_bump(struct lightningd *ld, } u64 wait_index_increment(struct lightningd *ld, + struct db *db, enum wait_subsystem subsystem, enum wait_index index, ...) @@ -176,7 +182,7 @@ u64 wait_index_increment(struct lightningd *ld, u64 ret; va_start(ap, index); - ret = wait_index_bump(ld, subsystem, index, 1, ap); + ret = wait_index_bump(ld, db, subsystem, index, 1, ap); va_end(ap); return ret; @@ -194,7 +200,7 @@ void wait_index_increase(struct lightningd *ld, return; va_start(ap, num); - wait_index_bump(ld, subsystem, index, num, ap); + wait_index_bump(ld, ld->wallet->db, subsystem, index, num, ap); va_end(ap); } diff --git a/lightningd/wait.h b/lightningd/wait.h index 69b60c01fe52..d60abfe2c214 100644 --- a/lightningd/wait.h +++ b/lightningd/wait.h @@ -11,8 +11,10 @@ enum wait_subsystem { WAIT_SUBSYSTEM_SENDPAY, WAIT_SUBSYSTEM_INVOICE, WAIT_SUBSYSTEM_HTLCS, + WAIT_SUBSYSTEM_CHAINMOVES, + WAIT_SUBSYSTEM_CHANNELMOVES, }; -#define NUM_WAIT_SUBSYSTEM (WAIT_SUBSYSTEM_HTLCS+1) +#define NUM_WAIT_SUBSYSTEM (WAIT_SUBSYSTEM_CHANNELMOVES+1) enum wait_index { WAIT_INDEX_CREATED, @@ -35,6 +37,7 @@ const char *wait_subsystem_name(enum wait_subsystem subsystem); /** * wait_index_increment - increment an index, tell waiters. * @ld: the lightningd + * @db: the database (usually ld->wallet->db, except really early) * @subsystem: subsystem for index * @index: which index * ...: name/value pairs, followed by NULL. @@ -46,6 +49,7 @@ const char *wait_subsystem_name(enum wait_subsystem subsystem); * Returns the updated index value (always > 0). */ u64 LAST_ARG_NULL wait_index_increment(struct lightningd *ld, + struct db *db, enum wait_subsystem subsystem, enum wait_index index, ...); diff --git a/plugins/Makefile b/plugins/Makefile index fc3f2c8cf6a5..6efb6c2310f1 100644 --- a/plugins/Makefile +++ b/plugins/Makefile @@ -256,7 +256,7 @@ plugins/recover: common/gossmap.o common/sciddir_or_pubkey.o common/fp16.o $(PL plugins/recklessrpc: $(PLUGIN_RECKLESSRPC_OBJS) $(PLUGIN_LIB_OBJS) $(PLUGIN_COMMON_OBJS) $(JSMN_OBJS) # This covers all the low-level list RPCs which return simple arrays -SQL_LISTRPCS := listchannels listforwards listhtlcs listinvoices listnodes listoffers listpeers listpeerchannels listclosedchannels listtransactions listsendpays bkpr-listaccountevents bkpr-listincome +SQL_LISTRPCS := listchannels listforwards listhtlcs listinvoices listnodes listoffers listpeers listpeerchannels listclosedchannels listtransactions listsendpays listchainmoves listchannelmoves bkpr-listaccountevents bkpr-listincome SQL_LISTRPCS_SCHEMAS := $(foreach l,$(SQL_LISTRPCS),doc/schemas/$l.json) SQL_SCHEMA_PARTS := $(foreach l,$(SQL_LISTRPCS), plugins/sql-schema_$l_gen.h) diff --git a/plugins/bkpr/Makefile b/plugins/bkpr/Makefile index 18599631c400..943b8755f0b2 100644 --- a/plugins/bkpr/Makefile +++ b/plugins/bkpr/Makefile @@ -3,58 +3,44 @@ BOOKKEEPER_PLUGIN_SRC := \ plugins/bkpr/account.c \ plugins/bkpr/account_entry.c \ + plugins/bkpr/blockheights.c \ plugins/bkpr/bookkeeper.c \ plugins/bkpr/chain_event.c \ plugins/bkpr/channel_event.c \ plugins/bkpr/channelsapy.c \ - plugins/bkpr/db.c \ + plugins/bkpr/descriptions.c \ plugins/bkpr/incomestmt.c \ plugins/bkpr/onchain_fee.c \ - plugins/bkpr/recorder.c - -BOOKKEEPER_DB_QUERIES := \ - plugins/bkpr/db_sqlite3_sqlgen.c \ - plugins/bkpr/db_postgres_sqlgen.c + plugins/bkpr/rebalances.c \ + plugins/bkpr/recorder.c \ + plugins/bkpr/sql.c BOOKKEEPER_SRC := $(BOOKKEEPER_PLUGIN_SRC) $(BOOKKEEPER_DB_QUERIES) BOOKKEEPER_HEADER := \ plugins/bkpr/account.h \ plugins/bkpr/account_entry.h \ + plugins/bkpr/blockheights.h \ + plugins/bkpr/bookkeeper.h \ plugins/bkpr/chain_event.h \ plugins/bkpr/channel_event.h \ plugins/bkpr/channelsapy.h \ - plugins/bkpr/db.h \ + plugins/bkpr/descriptions.h \ plugins/bkpr/incomestmt.h \ plugins/bkpr/onchain_fee.h \ - plugins/bkpr/recorder.h + plugins/bkpr/rebalances.h \ + plugins/bkpr/recorder.h \ + plugins/bkpr/sql.h BOOKKEEPER_OBJS := $(BOOKKEEPER_SRC:.c=.o) $(BOOKKEEPER_OBJS): $(PLUGIN_LIB_HEADER) $(BOOKKEEPER_HEADER) -PLUGIN_ALL_SRC += $(BOOKKEEPER_SRC) $(BOOKKEEPER_DB_QUERIES) +PLUGIN_ALL_SRC += $(BOOKKEEPER_SRC) PLUGIN_ALL_HEADER += $(BOOKKEEPER_HEADER) C_PLUGINS += plugins/bookkeeper PLUGINS += plugins/bookkeeper -plugins/bookkeeper: common/bolt12.o common/bolt12_merkle.o common/channel_type.o $(BOOKKEEPER_OBJS) $(PLUGIN_LIB_OBJS) $(JSMN_OBJTS) $(PLUGIN_COMMON_OBJS) $(WIRE_OBJS) $(WIRE_BOLT12_OBJS) $(DB_OBJS) - -# The following files contain SQL-annotated statements that we need to extact -BOOKKEEPER_SQL_FILES := \ - $(DB_SQL_FILES) \ - plugins/bkpr/db.c \ - plugins/bkpr/incomestmt.c \ - plugins/bkpr/recorder.c - -plugins/bkpr/statements_gettextgen.po: $(BOOKKEEPER_SQL_FILES) $(FORCE) - @if $(call SHA256STAMP_CHANGED); then \ - $(call VERBOSE,"xgettext $@",xgettext -kNAMED_SQL -kSQL --add-location --no-wrap --omit-header -o $@ $(BOOKKEEPER_SQL_FILES) && $(call SHA256STAMP,# ,)); \ - fi - -plugins/bkpr/db_%_sqlgen.c: plugins/bkpr/statements_gettextgen.po devtools/sql-rewrite.py $(BOOKKEEPER_SQL_FILES) $(FORCE) - @if $(call SHA256STAMP_CHANGED); then \ - $(call VERBOSE,"sql-rewrite $@",devtools/sql-rewrite.py plugins/bkpr/statements_gettextgen.po $* > $@ && $(call SHA256STAMP,//,)); \ - fi +plugins/bookkeeper: common/bolt12.o common/bolt12_merkle.o common/channel_type.o $(BOOKKEEPER_OBJS) $(PLUGIN_LIB_OBJS) $(PLUGIN_COMMON_OBJS) $(WIRE_OBJS) $(WIRE_BOLT12_OBJS) maintainer-clean: clean clean: bkpr-maintainer-clean diff --git a/plugins/bkpr/account.c b/plugins/bkpr/account.c index 877db9005fa8..e5001b88526e 100644 --- a/plugins/bkpr/account.c +++ b/plugins/bkpr/account.c @@ -1,19 +1,57 @@ #include "config.h" +#include +#include #include #include -#include +#include #include #include +#include +#include +#include +#include +#include -struct account *new_account(const tal_t *ctx, - const char *name, - struct node_id *peer_id) +static size_t hash_str(const char *str) { - struct account *a = tal(ctx, struct account); + return siphash24(siphash_seed(), str, strlen(str)); +} + +static const char *account_key(const struct account *account) +{ + return account->name; +} + +static bool account_eq_name(const struct account *account, + const char *name) +{ + return streq(account->name, name); +} + +HTABLE_DEFINE_NODUPS_TYPE(struct account, + account_key, + hash_str, + account_eq_name, + account_htable); + +/* We keep accounts in memory, and for the moment, still in the db */ +struct accounts { + struct account_htable *htable; +}; + +static void destroy_account(struct account *a, struct accounts *accounts) +{ + account_htable_del(accounts->htable, a); +} + +static struct account *new_account(struct accounts *accounts, + const char *name TAKES) +{ + struct account *a = tal(accounts, struct account); a->name = tal_strdup(a, name); - a->peer_id = peer_id; + a->peer_id = NULL; a->is_wallet = is_wallet_account(a->name); a->we_opened = false; a->leased = false; @@ -22,5 +60,269 @@ struct account *new_account(const tal_t *ctx, a->closed_event_db_id = NULL; a->closed_count = 0; + account_htable_add(accounts->htable, a); + tal_add_destructor2(a, destroy_account, accounts); + return a; +} + +static void towire_account(u8 **pptr, const struct account *account) +{ + towire_wirestring(pptr, account->name); + if (account->peer_id) { + towire_bool(pptr, true); + towire_node_id(pptr, account->peer_id); + } else + towire_bool(pptr, false); + towire_bool(pptr, account->we_opened); + towire_bool(pptr, account->leased); + towire_u64(pptr, account->onchain_resolved_block); + if (account->open_event_db_id) { + towire_bool(pptr, true); + towire_u64(pptr, *account->open_event_db_id); + } else + towire_bool(pptr, false); + if (account->closed_event_db_id) { + towire_bool(pptr, true); + towire_u64(pptr, *account->closed_event_db_id); + } else + towire_bool(pptr, false); + towire_u32(pptr, account->closed_count); +} + +static struct account *fromwire_account(struct accounts *accounts, + const u8 **pptr, size_t *max) +{ + const char *name; + struct account *account; + + name = fromwire_wirestring(NULL, pptr, max); + if (!name) + return NULL; + + account = new_account(accounts, take(name)); + if (fromwire_bool(pptr, max)) { + account->peer_id = tal(account, struct node_id); + fromwire_node_id(pptr, max, account->peer_id); + } else + account->peer_id = NULL; + account->we_opened = fromwire_bool(pptr, max); + account->leased = fromwire_bool(pptr, max); + account->onchain_resolved_block = fromwire_u64(pptr, max); + if (fromwire_bool(pptr, max)) { + account->open_event_db_id = tal(account, u64); + *account->open_event_db_id = fromwire_u64(pptr, max); + } else + account->open_event_db_id = NULL; + if (fromwire_bool(pptr, max)) { + account->closed_event_db_id = tal(account, u64); + *account->closed_event_db_id = fromwire_u64(pptr, max); + } else + account->closed_event_db_id = NULL; + account->closed_count = fromwire_u32(pptr, max); + + if (!pptr) + return tal_free(account); + return account; +} + +struct account **list_accounts(const tal_t *ctx, const struct bkpr *bkpr) +{ + struct account **results; + struct account_htable_iter it; + struct account *a; + size_t i; + + results = tal_arr(ctx, + struct account *, + account_htable_count(bkpr->accounts->htable)); + for (i = 0, a = account_htable_first(bkpr->accounts->htable, &it); + a; + i++, a = account_htable_next(bkpr->accounts->htable, &it)) { + results[i] = a; + } + + return results; +} + +static const char *ds_path(const tal_t *ctx, const char *acctname) +{ + return tal_fmt(ctx, "bookkeeper/account/%s", acctname); +} + +static void account_datastore_set(struct command *cmd, + const struct account *acct, + const char *mode) +{ + const char *path = ds_path(tmpctx, acct->name); + u8 *data = tal_arr(tmpctx, u8, 0); + + towire_account(&data, acct); + jsonrpc_set_datastore_binary(cmd, path, data, tal_bytelen(data), mode, + ignore_datastore_reply, NULL, NULL); +} + +void maybe_update_account(struct command *cmd, + struct account *acct, + struct chain_event *e, + const enum mvt_tag *tags, + u32 closed_count, + struct node_id *peer_id) +{ + bool updated = false; + + for (size_t i = 0; i < tal_count(tags); i++) { + switch (tags[i]) { + case MVT_CHANNEL_PROPOSED: + case MVT_CHANNEL_OPEN: + if (!acct->open_event_db_id) { + updated = true; + acct->open_event_db_id = tal(acct, u64); + *acct->open_event_db_id = e->db_id; + } + break; + case MVT_CHANNEL_CLOSE: + /* Splices dont count as closes */ + if (e->splice_close) + break; + updated = true; + acct->closed_event_db_id = tal(acct, u64); + *acct->closed_event_db_id = e->db_id; + break; + case MVT_LEASED: + updated = true; + acct->leased = true; + break; + case MVT_OPENER: + updated = true; + acct->we_opened = true; + break; + case MVT_DEPOSIT: + case MVT_WITHDRAWAL: + case MVT_PENALTY: + case MVT_INVOICE: + case MVT_ROUTED: + case MVT_PUSHED: + case MVT_CHANNEL_TO_US: + case MVT_HTLC_TIMEOUT: + case MVT_HTLC_FULFILL: + case MVT_HTLC_TX: + case MVT_TO_WALLET: + case MVT_ANCHOR: + case MVT_TO_THEM: + case MVT_PENALIZED: + case MVT_STOLEN: + case MVT_TO_MINER: + case MVT_LEASE_FEE: + case MVT_STEALABLE: + case MVT_SPLICE: + case MVT_PENALTY_ADJ: + case MVT_JOURNAL: + case MVT_FOREIGN: + /* Ignored */ + break; + } + } + + if (peer_id && !acct->peer_id) { + updated = true; + acct->peer_id = tal_dup(acct, struct node_id, peer_id); + } + + if (!e->splice_close && closed_count > 0) { + updated = true; + acct->closed_count = closed_count; + } + + /* Nothing new here */ + if (!updated) + return; + + /* Otherwise, we update the account ! */ + account_datastore_set(cmd, acct, "must-replace"); +} + +void account_update_closeheight(struct command *cmd, + struct account *acct, + u64 close_height) +{ + assert(close_height); + acct->onchain_resolved_block = close_height; + + /* Ok, now we update the account with this blockheight */ + account_datastore_set(cmd, acct, "must-replace"); +} + +struct account *find_account(const struct bkpr *bkpr, + const char *name) +{ + return account_htable_get(bkpr->accounts->htable, name); +} + +struct account *find_or_create_account(struct command *cmd, + struct bkpr *bkpr, + const char *name) +{ + struct account *a = find_account(bkpr, name); + + if (a) + return a; + + a = new_account(bkpr->accounts, name); + account_datastore_set(cmd, a, "must-create"); return a; } + +static void memleak_scan_accounts_htable(struct htable *memtable, + struct account_htable *ht) +{ + memleak_scan_htable(memtable, &ht->raw); +} + +struct accounts *init_accounts(const tal_t *ctx, struct command *init_cmd) +{ + struct json_out *params = json_out_new(tmpctx); + const jsmntok_t *result; + const char *buf; + const jsmntok_t *datastore, *t; + size_t i; + struct accounts *accounts = tal(ctx, struct accounts); + + accounts->htable = tal(accounts, struct account_htable); + account_htable_init(accounts->htable); + memleak_add_helper(accounts->htable, memleak_scan_accounts_htable); + + json_out_start(params, NULL, '{'); + json_out_start(params, "key", '['); + json_out_addstr(params, NULL, "bookkeeper"); + json_out_addstr(params, NULL, "account"); + json_out_end(params, ']'); + json_out_end(params, '}'); + result = jsonrpc_request_sync(tmpctx, init_cmd, + "listdatastore", + params, &buf); + + datastore = json_get_member(buf, result, "datastore"); + json_for_each_arr(i, t, datastore) { + size_t datalen; + const jsmntok_t *key, *datatok; + const u8 *data; + + /* Key is an array, first two elements are bookkeeper, account */ + key = json_get_member(buf, t, "key") + 3; + datatok = json_get_member(buf, t, "hex"); + /* In case someone creates a subdir? */ + if (!datatok) + continue; + + data = json_tok_bin_from_hex(tmpctx, buf, datatok); + datalen = tal_bytelen(data); + + if (fromwire_account(accounts, &data, &datalen) == NULL) { + plugin_err(init_cmd->plugin, + "Invalid account %.*s in datastore", + json_tok_full_len(key), + json_tok_full(buf, key)); + } + } + return accounts; +} diff --git a/plugins/bkpr/account.h b/plugins/bkpr/account.h index 148ba3b09b7e..76682aac1872 100644 --- a/plugins/bkpr/account.h +++ b/plugins/bkpr/account.h @@ -3,15 +3,14 @@ #include "config.h" #include +#include struct node_id; +struct chain_event; +struct command; struct account { - - /* Id of this account in the database */ - u64 db_id; - - /* Name of account, typically channel id */ + /* Unique name of account, typically channel id */ const char *name; /* Peer we have this account with (NULL if not a channel) */ @@ -39,9 +38,37 @@ struct account { u32 closed_count; }; -/* Get a new account */ -struct account *new_account(const tal_t *ctx, - const char *name STEALS, - struct node_id *peer_id); +struct bkpr; + +/* Initialize the accounts subsystem */ +struct accounts *init_accounts(const tal_t *ctx, struct command *init_cmd); + +/* Get all accounts */ +struct account **list_accounts(const tal_t *ctx, const struct bkpr *bkpr); + +/* Given an account name, find that account record */ +struct account *find_account(const struct bkpr *bkpr, + const char *name); + +/* Create it if it's not found */ +struct account *find_or_create_account(struct command *cmd, + struct bkpr *bkpr, + const char *name); + +/* Some events update account information */ +void maybe_update_account(struct command *cmd, + struct account *acct, + struct chain_event *e, + const enum mvt_tag *tags, + u32 closed_count, + struct node_id *peer_id); + +/* Update the account with the highest blockheight that has a + * resolving tx in it. + * + * The point of this is to allow us to prune data, eventually */ +void account_update_closeheight(struct command *cmd, + struct account *acct, + u64 close_height); #endif /* LIGHTNING_PLUGINS_BKPR_ACCOUNT_H */ diff --git a/plugins/bkpr/account_entry.c b/plugins/bkpr/account_entry.c index 0606c69b80aa..a199fe424068 100644 --- a/plugins/bkpr/account_entry.c +++ b/plugins/bkpr/account_entry.c @@ -16,7 +16,7 @@ const char *account_entry_tag_str(enum account_entry_tag tag) return tags[tag]; } -bool account_entry_tag_find(char *str, enum account_entry_tag *tag) +bool account_entry_tag_find(const char *str, enum account_entry_tag *tag) { for (size_t i = 0; i < NUM_ACCOUNT_ENTRY_TAGS; i++) { if (streq(str, tags[i])) { diff --git a/plugins/bkpr/account_entry.h b/plugins/bkpr/account_entry.h index 8823f672ea86..ec6b8cca3b53 100644 --- a/plugins/bkpr/account_entry.h +++ b/plugins/bkpr/account_entry.h @@ -14,5 +14,5 @@ enum account_entry_tag { const char *account_entry_tag_str(enum account_entry_tag tag); /* True if entry tag found, false otherwise */ -bool account_entry_tag_find(char *str, enum account_entry_tag *tag); +bool account_entry_tag_find(const char *str, enum account_entry_tag *tag); #endif /* LIGHTNING_PLUGINS_BKPR_ACCOUNT_ENTRY_H */ diff --git a/plugins/bkpr/blockheights.c b/plugins/bkpr/blockheights.c new file mode 100644 index 000000000000..094c8489666c --- /dev/null +++ b/plugins/bkpr/blockheights.c @@ -0,0 +1,162 @@ +#include "config.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +struct blockheight_entry { + struct bitcoin_txid txid; + u32 height; +}; + +static size_t hash_txid(const struct bitcoin_txid *txid) +{ + return siphash24(siphash_seed(), txid->shad.sha.u.u8, + sizeof(txid->shad.sha.u.u8)); +} + +static const struct bitcoin_txid * +blockheight_key(const struct blockheight_entry *e) +{ + return &e->txid; +} + +static bool blockheight_key_eq(const struct blockheight_entry *e, + const struct bitcoin_txid *k) +{ + return bitcoin_txid_eq(&e->txid, k); +} + +HTABLE_DEFINE_NODUPS_TYPE(struct blockheight_entry, + blockheight_key, + hash_txid, + blockheight_key_eq, + blockheight_htable); + +struct blockheights { + struct blockheight_htable *map; +}; + +static void memleak_scan_blockheight_htable(struct htable *memtable, + struct blockheight_htable *ht) +{ + memleak_scan_htable(memtable, &ht->raw); +} + +static const char *ds_blockheight_path(const tal_t *ctx, + const struct bitcoin_txid *txid) +{ + /* Keys like: bookkeeper/blockheights/ */ + return tal_fmt(ctx, "bookkeeper/blockheights/%s", + fmt_bitcoin_txid(tmpctx, txid)); +} + +void add_blockheight(struct command *cmd, + struct bkpr *bkpr, + const struct bitcoin_txid *txid, + u32 blockheight) +{ + struct blockheights *bh = bkpr->blockheights; + struct blockheight_entry *e; + be32 be_blockheight; + const char *path = ds_blockheight_path(tmpctx, txid); + + /* Update in-memory map (replace or insert) */ + e = blockheight_htable_get(bh->map, txid); + if (e) { + e->height = blockheight; + } else { + e = tal(bh->map, struct blockheight_entry); + e->txid = *txid; + e->height = blockheight; + blockheight_htable_add(bh->map, e); + } + + be_blockheight = cpu_to_be32(blockheight); + jsonrpc_set_datastore_binary(cmd, path, + &be_blockheight, sizeof(be_blockheight), + "create-or-replace", + ignore_datastore_reply, NULL, NULL); +} + +u32 find_blockheight(const struct bkpr *bkpr, + const struct bitcoin_txid *txid) +{ + const struct blockheight_entry *e; + + e = blockheight_htable_get(bkpr->blockheights->map, txid); + return e ? e->height : 0; +} + +static bool json_hex_to_be32(const char *buffer, const jsmntok_t *tok, + be32 *val) +{ + return hex_decode(buffer + tok->start, tok->end - tok->start, + val, sizeof(*val)); +} + +struct blockheights *init_blockheights(const tal_t *ctx, + struct command *init_cmd) +{ + struct json_out *params = json_out_new(tmpctx); + const jsmntok_t *result; + const char *buf; + const jsmntok_t *datastore, *t; + size_t i; + + struct blockheights *bh = tal(ctx, struct blockheights); + bh->map = tal(bh, struct blockheight_htable); + blockheight_htable_init(bh->map); + memleak_add_helper(bh->map, memleak_scan_blockheight_htable); + + /* Query all keys under bookkeeper/blockheights */ + json_out_start(params, NULL, '{'); + json_out_start(params, "key", '['); + json_out_addstr(params, NULL, "bookkeeper"); + json_out_addstr(params, NULL, "blockheights"); + json_out_end(params, ']'); + json_out_end(params, '}'); + + result = jsonrpc_request_sync(tmpctx, init_cmd, + "listdatastore", params, &buf); + + datastore = json_get_member(buf, result, "datastore"); + json_for_each_arr(i, t, datastore) { + const jsmntok_t *keytok = json_get_member(buf, t, "key"); + const jsmntok_t *hextok = json_get_member(buf, t, "hex"); + struct blockheight_entry *e; + struct bitcoin_txid txid; + be32 be_blockheight; + + /* Expect: ["bookkeeper","blockheights",""] */ + if (keytok->size != 3) + goto weird; + + if (!json_to_txid(buf, keytok + 2, &txid)) + goto weird; + if (!json_hex_to_be32(buf, hextok, &be_blockheight)) + goto weird; + + /* Insert into map */ + e = tal(bh->map, struct blockheight_entry); + e->txid = txid; + e->height = be32_to_cpu(be_blockheight); + blockheight_htable_add(bh->map, e); + continue; + +weird: + plugin_log(init_cmd->plugin, LOG_BROKEN, + "Unparsable blockheight datastore entry: %.*s", + json_tok_full_len(t), json_tok_full(buf, t)); + } + + return bh; +} diff --git a/plugins/bkpr/blockheights.h b/plugins/bkpr/blockheights.h new file mode 100644 index 000000000000..b4076f75fc95 --- /dev/null +++ b/plugins/bkpr/blockheights.h @@ -0,0 +1,19 @@ +#ifndef LIGHTNING_PLUGINS_BKPR_BLOCKHEIGHTS_H +#define LIGHTNING_PLUGINS_BKPR_BLOCKHEIGHTS_H +#include "config.h" + +struct command; +struct bkpr; +struct bitcoin_txid; + +void add_blockheight(struct command *cmd, + struct bkpr *bkpr, + const struct bitcoin_txid *txid, + u32 blockheight); + +/* Returns blockheight for this txid, or 0 if not found. */ +u32 find_blockheight(const struct bkpr *bkpr, const struct bitcoin_txid *txid); + +struct blockheights *init_blockheights(const tal_t *ctx, + struct command *init_cmd); +#endif /* LIGHTNING_PLUGINS_BKPR_BLOCKHEIGHTS_H */ diff --git a/plugins/bkpr/bookkeeper.c b/plugins/bkpr/bookkeeper.c index 9f2dce9cbb44..1312924de600 100644 --- a/plugins/bkpr/bookkeeper.c +++ b/plugins/bkpr/bookkeeper.c @@ -3,6 +3,7 @@ #include #include #include +#include #include #include #include @@ -11,18 +12,20 @@ #include #include #include -#include #include #include #include #include #include +#include +#include #include #include #include -#include +#include #include #include +#include #include #include #include @@ -31,11 +34,64 @@ #define CHAIN_MOVE "chain_mvt" #define CHANNEL_MOVE "channel_mvt" -/* The database that we store all the accounting data in */ -static struct db *db ; +static struct bkpr *bkpr_of(struct plugin *plugin) +{ + return plugin_get_data(plugin, struct bkpr); +} + +struct refresh_info { + size_t calls_remaining; + struct command_result *(*cb)(struct command *, void *); + void *arg; +}; + +/* Rules: call use_rinfo when handing to a callback. + * Have the callback return rinfo_one_done(). */ +static struct refresh_info *use_rinfo(struct refresh_info *rinfo) +{ + rinfo->calls_remaining++; + return rinfo; +} + +static struct command_result *rinfo_one_done(struct command *cmd, + struct refresh_info *rinfo) +{ + assert(rinfo->calls_remaining > 0); + if (--rinfo->calls_remaining == 0) + return rinfo->cb(cmd, rinfo->arg); + else + return command_still_pending(cmd); +} + +struct command_result *ignore_datastore_reply(struct command *cmd, + const char *method, + const char *buf, + const jsmntok_t *result, + void *arg) +{ + return command_still_pending(cmd); +} -static char *db_dsn; -static char *datadir; +/* FIXME: reorder to avoid fwd decls. */ +static void +parse_and_log_chain_move(struct command *cmd, + const char *buf, + const jsmntok_t *chainmove, + struct refresh_info *rinfo); +static void +parse_and_log_channel_move(struct command *cmd, + const char *buf, + const jsmntok_t *channelmove, + struct refresh_info *rinfo); + +static struct command_result *datastore_done(struct command *cmd, + const char *method, + const char *buf, + const jsmntok_t *result, + struct refresh_info *rinfo) +{ + return rinfo_one_done(cmd, rinfo); +} static struct fee_sum *find_sum_for_txid(struct fee_sum **sums, struct bitcoin_txid *txid) @@ -47,6 +103,90 @@ static struct fee_sum *find_sum_for_txid(struct fee_sum **sums, return NULL; } +static struct command_result *listchannelmoves_done(struct command *cmd, + const char *method, + const char *buf, + const jsmntok_t *result, + struct refresh_info *rinfo) +{ + const jsmntok_t *moves, *t; + size_t i; + struct bkpr *bkpr = bkpr_of(cmd->plugin); + be64 be_index; + + moves = json_get_member(buf, result, "channelmoves"); + json_for_each_arr(i, t, moves) + parse_and_log_channel_move(cmd, buf, t, rinfo); + + be_index = cpu_to_be64(bkpr->channelmoves_index); + jsonrpc_set_datastore_binary(cmd, "bookkeeper/channelmoves_index", + &be_index, sizeof(be_index), + "create-or-replace", + datastore_done, NULL, use_rinfo(rinfo)); + return rinfo_one_done(cmd, rinfo); +} + +static struct command_result *listchainmoves_done(struct command *cmd, + const char *method, + const char *buf, + const jsmntok_t *result, + struct refresh_info *rinfo) +{ + struct out_req *req; + const jsmntok_t *moves, *t; + size_t i; + struct bkpr *bkpr = bkpr_of(cmd->plugin); + be64 be_index; + + moves = json_get_member(buf, result, "chainmoves"); + json_for_each_arr(i, t, moves) + parse_and_log_chain_move(cmd, buf, t, rinfo); + + be_index = cpu_to_be64(bkpr->chainmoves_index); + jsonrpc_set_datastore_binary(cmd, "bookkeeper/chainmoves_index", + &be_index, sizeof(be_index), + "create-or-replace", + datastore_done, NULL, use_rinfo(rinfo)); + + req = jsonrpc_request_start(cmd, "listchannelmoves", + listchannelmoves_done, + plugin_broken_cb, + use_rinfo(rinfo)); + json_add_string(req->js, "index", "created"); + json_add_u64(req->js, "start", bkpr->channelmoves_index + 1); + send_outreq(req); + return rinfo_one_done(cmd, rinfo); +} + +static struct command_result *refresh_moves_(struct command *cmd, + struct command_result *(*cb)( + struct command *, + void *), + void *arg) +{ + struct refresh_info *rinfo = tal(cmd, struct refresh_info); + struct out_req *req; + struct bkpr *bkpr = bkpr_of(cmd->plugin); + + rinfo->cb = cb; + rinfo->arg = arg; + rinfo->calls_remaining = 0; + req = jsonrpc_request_start(cmd, "listchainmoves", + listchainmoves_done, + plugin_broken_cb, + use_rinfo(rinfo)); + json_add_string(req->js, "index", "created"); + json_add_u64(req->js, "start", bkpr->chainmoves_index + 1); + return send_outreq(req); +} + +#define refresh_moves(cmd, cb, arg) \ + refresh_moves_((cmd), \ + typesafe_cb_preargs(struct command_result *, void *, \ + (cb), (arg), \ + struct command *), \ + arg) + struct apy_req { u64 *start_time; u64 *end_time; @@ -63,6 +203,7 @@ getblockheight_done(struct command *cmd, u32 blockheight; struct json_stream *res; struct channel_apy **apys, *net_apys; + struct bkpr *bkpr = bkpr_of(cmd->plugin); blockheight_tok = json_get_member(buf, result, "blockheight"); if (!blockheight_tok) @@ -76,12 +217,10 @@ getblockheight_done(struct command *cmd, result->end - result->start, buf); /* Get the income events */ - db_begin_transaction(db); - apys = compute_channel_apys(cmd, db, + apys = compute_channel_apys(cmd, bkpr, cmd, *req->start_time, *req->end_time, blockheight); - db_commit_transaction(db); /* Setup the net_apys entry */ net_apys = new_channel_apy(cmd); @@ -111,11 +250,23 @@ getblockheight_done(struct command *cmd, return command_finished(cmd, res); } +static struct command_result * +do_channel_apy(struct command *cmd, struct apy_req *apyreq) +{ + struct out_req *req; + + /* First get the current blockheight */ + req = jsonrpc_request_start(cmd, "getinfo", + &getblockheight_done, + forward_error, + apyreq); + return send_outreq(req); +} + static struct command_result *json_channel_apy(struct command *cmd, const char *buf, const jsmntok_t *params) { - struct out_req *req; struct apy_req *apyreq = tal(cmd, struct apy_req); if (!param(cmd, buf, params, @@ -125,12 +276,7 @@ static struct command_result *json_channel_apy(struct command *cmd, NULL)) return command_param_failed(); - /* First get the current blockheight */ - req = jsonrpc_request_start(cmd, "getinfo", - &getblockheight_done, - forward_error, - apyreq); - return send_outreq(req); + return refresh_moves(cmd, do_channel_apy, apyreq); } static struct command_result *param_csv_format(struct command *cmd, const char *name, @@ -148,71 +294,74 @@ static struct command_result *param_csv_format(struct command *cmd, const char * csv_list_fmts(cmd))); } -static struct command_result *json_dump_income(struct command *cmd, - const char *buf, - const jsmntok_t *params) -{ - struct json_stream *res; - struct income_event **evs; +struct dump_income_info { struct csv_fmt *csv_fmt; const char *filename; bool *consolidate_fees; - char *err; u64 *start_time, *end_time; +}; - if (!param(cmd, buf, params, - p_req("csv_format", param_csv_format, &csv_fmt), - p_opt("csv_file", param_string, &filename), - p_opt_def("consolidate_fees", param_bool, - &consolidate_fees, true), - p_opt_def("start_time", param_u64, &start_time, 0), - p_opt_def("end_time", param_u64, &end_time, SQLITE_MAX_UINT), - NULL)) - return command_param_failed(); +static struct command_result *do_dump_income(struct command *cmd, + struct dump_income_info *info) +{ + struct bkpr *bkpr = bkpr_of(cmd->plugin); + struct json_stream *res; + struct income_event **evs; + char *err; /* Ok, go find me some income events! */ - db_begin_transaction(db); - evs = list_income_events(cmd, db, *start_time, *end_time, - *consolidate_fees); - db_commit_transaction(db); + evs = list_income_events(cmd, bkpr, cmd, *info->start_time, *info->end_time, + *info->consolidate_fees); - if (!filename) - filename = csv_filename(cmd, csv_fmt); + if (!info->filename) + info->filename = csv_filename(info, info->csv_fmt); - err = csv_print_income_events(cmd, csv_fmt, filename, evs); + err = csv_print_income_events(cmd, info->csv_fmt, info->filename, evs); if (err) return command_fail(cmd, PLUGIN_ERROR, "Unable to create csv file: %s", err); res = jsonrpc_stream_success(cmd); - json_add_string(res, "csv_file", filename); - json_add_string(res, "csv_format", csv_fmt->fmt_name); + json_add_string(res, "csv_file", info->filename); + json_add_string(res, "csv_format", info->csv_fmt->fmt_name); return command_finished(cmd, res); } -static struct command_result *json_list_income(struct command *cmd, +static struct command_result *json_dump_income(struct command *cmd, const char *buf, const jsmntok_t *params) { - struct json_stream *res; - struct income_event **evs; - bool *consolidate_fees; - u64 *start_time, *end_time; + struct dump_income_info *info = tal(cmd, struct dump_income_info); if (!param(cmd, buf, params, + p_req("csv_format", param_csv_format, &info->csv_fmt), + p_opt("csv_file", param_string, &info->filename), p_opt_def("consolidate_fees", param_bool, - &consolidate_fees, true), - p_opt_def("start_time", param_u64, &start_time, 0), - p_opt_def("end_time", param_u64, &end_time, SQLITE_MAX_UINT), + &info->consolidate_fees, true), + p_opt_def("start_time", param_u64, &info->start_time, 0), + p_opt_def("end_time", param_u64, &info->end_time, SQLITE_MAX_UINT), NULL)) return command_param_failed(); + return refresh_moves(cmd, do_dump_income, info); +} + +struct list_income_info { + bool *consolidate_fees; + u64 *start_time, *end_time; +}; + +static struct command_result *do_list_income(struct command *cmd, + struct list_income_info *info) +{ + struct json_stream *res; + struct income_event **evs; + struct bkpr *bkpr = bkpr_of(cmd->plugin); + /* Ok, go find me some income events! */ - db_begin_transaction(db); - evs = list_income_events(cmd, db, *start_time, *end_time, - *consolidate_fees); - db_commit_transaction(db); + evs = list_income_events(cmd, bkpr, cmd, *info->start_time, *info->end_time, + *info->consolidate_fees); res = jsonrpc_stream_success(cmd); @@ -224,40 +373,45 @@ static struct command_result *json_list_income(struct command *cmd, return command_finished(cmd, res); } -static struct command_result *json_inspect(struct command *cmd, - const char *buf, - const jsmntok_t *params) +static struct command_result *json_list_income(struct command *cmd, + const char *buf, + const jsmntok_t *params) { - struct json_stream *res; - struct account *acct; - const char *acct_name; - struct fee_sum **fee_sums; - struct txo_set **txos; + struct list_income_info *info = tal(cmd, struct list_income_info); - /* Only available for channel accounts? */ if (!param(cmd, buf, params, - p_req("account", param_string, &acct_name), + p_opt_def("consolidate_fees", param_bool, + &info->consolidate_fees, true), + p_opt_def("start_time", param_u64, &info->start_time, 0), + p_opt_def("end_time", param_u64, &info->end_time, SQLITE_MAX_UINT), NULL)) return command_param_failed(); + return refresh_moves(cmd, do_list_income, info); +} + +static struct command_result *do_inspect(struct command *cmd, + char *acct_name) +{ + struct json_stream *res; + struct account *acct; + struct fee_sum **fee_sums; + struct txo_set **txos; + struct bkpr *bkpr = bkpr_of(cmd->plugin); + if (!is_channel_account(acct_name)) return command_fail(cmd, PLUGIN_ERROR, "`inspect` not supported for" " non-channel accounts"); - db_begin_transaction(db); - acct = find_account(cmd, db, acct_name); - db_commit_transaction(db); - + acct = find_account(bkpr, acct_name); if (!acct) return command_fail(cmd, PLUGIN_ERROR, "Account %s not found", acct_name); - db_begin_transaction(db); - find_txo_chain(cmd, db, acct, &txos); - fee_sums = find_account_onchain_fees(cmd, db, acct); - db_commit_transaction(db); + find_txo_chain(cmd, bkpr, cmd, acct, &txos); + fee_sums = find_account_onchain_fees(cmd, bkpr, acct); res = jsonrpc_stream_success(cmd); json_array_start(res, "txs"); @@ -296,12 +450,12 @@ static struct command_result *json_inspect(struct command *cmd, if (pr->txo->origin_acct) { if (!streq(pr->txo->origin_acct, acct->name)) continue; - } else if (pr->txo->acct_db_id != acct->db_id + } else if (!streq(pr->txo->acct_name, acct->name) /* We make an exception for wallet events */ && !is_wallet_account(pr->txo->acct_name)) continue; } else if (pr->spend - && pr->spend->acct_db_id != acct->db_id) + && !streq(pr->spend->acct_name, acct->name)) continue; json_object_start(res, NULL); @@ -355,7 +509,23 @@ static struct command_result *json_inspect(struct command *cmd, return command_finished(cmd, res); } +static struct command_result *json_inspect(struct command *cmd, + const char *buf, + const jsmntok_t *params) +{ + char *acct_name; + + /* Only available for channel accounts? */ + if (!param(cmd, buf, params, + p_req("account", param_string, cast_const2(const char **, &acct_name)), + NULL)) + return command_param_failed(); + + return refresh_moves(cmd, do_inspect, acct_name); +} + static void json_add_events(struct json_stream *res, + const struct bkpr *bkpr, struct channel_event **channel_events, struct chain_event **chain_events, struct onchain_fee **onchain_fees) @@ -396,13 +566,13 @@ static void json_add_events(struct json_stream *res, /* chain events first, then channel events, then fees. */ if (chain && chain->timestamp == lowest) { - json_add_chain_event(res, chain); + json_add_chain_event(res, bkpr, chain); i++; continue; } if (chan && chan->timestamp == lowest) { - json_add_channel_event(res, chan); + json_add_channel_event(res, bkpr, chan); j++; continue; } @@ -413,109 +583,139 @@ static void json_add_events(struct json_stream *res, } } -/* Find all the events for this account, ordered by timestamp */ -static struct command_result *json_list_account_events(struct command *cmd, - const char *buf, - const jsmntok_t *params) +struct account_events_info { + const char *acct_name; + struct sha256 *payment_id; +}; + +static struct command_result *do_account_events(struct command *cmd, + struct account_events_info *info) { struct json_stream *res; struct account *acct; - struct sha256 *payment_id; struct bitcoin_txid *tx_id; - const char *acct_name; struct channel_event **channel_events; struct chain_event **chain_events; struct onchain_fee **onchain_fees; + struct bkpr *bkpr = bkpr_of(cmd->plugin); - if (!param(cmd, buf, params, - p_opt("account", param_string, &acct_name), - p_opt("payment_id", param_sha256, &payment_id), - NULL)) - return command_param_failed(); - - if (acct_name && payment_id != NULL) { + if (info->acct_name && info->payment_id != NULL) { return command_fail(cmd, JSONRPC2_INVALID_PARAMS, "Can only specify one of " "{account} or {payment_id}"); } - if (acct_name) { - db_begin_transaction(db); - acct = find_account(cmd, db, acct_name); - db_commit_transaction(db); - + if (info->acct_name) { + acct = find_account(bkpr, info->acct_name); if (!acct) return command_fail(cmd, JSONRPC2_INVALID_PARAMS, "Account '%s' not found", - acct_name); + info->acct_name); } else acct = NULL; - db_begin_transaction(db); if (acct) { - channel_events = account_get_channel_events(cmd, db, acct); - chain_events = account_get_chain_events(cmd, db, acct); - onchain_fees = account_get_chain_fees(cmd, db, acct); - } else if (payment_id != NULL) { - channel_events = get_channel_events_by_id(cmd, db, payment_id); + channel_events = account_get_channel_events(cmd, bkpr, cmd, acct); + chain_events = account_get_chain_events(cmd, bkpr, cmd, acct); + onchain_fees = account_get_chain_fees(tmpctx, bkpr, acct->name); + } else if (info->payment_id != NULL) { + channel_events = get_channel_events_by_id(cmd, bkpr, cmd, info->payment_id); tx_id = tal(cmd, struct bitcoin_txid); - tx_id->shad.sha = *payment_id; + tx_id->shad.sha = *info->payment_id; /* Transaction ids are stored as big-endian in the database */ reverse_bytes(tx_id->shad.sha.u.u8, sizeof(tx_id->shad.sha.u.u8)); - chain_events = find_chain_events_bytxid(cmd, db, tx_id); - onchain_fees = get_chain_fees_by_txid(cmd, db, tx_id); + chain_events = find_chain_events_bytxid(cmd, bkpr, cmd, tx_id); + onchain_fees = get_chain_fees_by_txid(cmd, bkpr, tx_id); } else { - channel_events = list_channel_events(cmd, db); - chain_events = list_chain_events(cmd, db); - onchain_fees = list_chain_fees(cmd, db); + channel_events = list_channel_events(cmd, bkpr, cmd); + chain_events = list_chain_events(cmd, bkpr, cmd); + onchain_fees = list_chain_fees(cmd, bkpr); } - db_commit_transaction(db); res = jsonrpc_stream_success(cmd); json_array_start(res, "events"); - json_add_events(res, channel_events, chain_events, onchain_fees); + json_add_events(res, bkpr, channel_events, chain_events, onchain_fees); json_array_end(res); return command_finished(cmd, res); } -static struct command_result *param_outpoint(struct command *cmd, - const char *name, - const char *buffer, - const jsmntok_t *tok, - struct bitcoin_outpoint **outp) +/* Find all the events for this account, ordered by timestamp */ +static struct command_result *json_list_account_events(struct command *cmd, + const char *buf, + const jsmntok_t *params) { - *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"); + struct account_events_info *info = tal(cmd, struct account_events_info); + + if (!param(cmd, buf, params, + p_opt("account", param_string, &info->acct_name), + p_opt("payment_id", param_sha256, &info->payment_id), + NULL)) + return command_param_failed(); + + return refresh_moves(cmd, do_account_events, info); +} + +struct edit_desc_info { + struct bitcoin_outpoint *outpoint; + const char *new_desc; +}; + +static struct command_result *do_edit_desc(struct command *cmd, + struct edit_desc_info *info) +{ + struct json_stream *res; + struct chain_event **chain_events; + struct bkpr *bkpr = bkpr_of(cmd->plugin); + + add_utxo_description(cmd, bkpr, info->outpoint, info->new_desc); + chain_events = get_chain_events_by_outpoint(cmd, bkpr, cmd, info->outpoint); + + res = jsonrpc_stream_success(cmd); + json_array_start(res, "updated"); + json_add_events(res, bkpr, NULL, chain_events, NULL); + json_array_end(res); + + return command_finished(cmd, res); } 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; + struct edit_desc_info *info = tal(cmd, struct edit_desc_info); if (!param(cmd, buf, params, - p_req("outpoint", param_outpoint, &outpoint), - p_req("description", param_string, &new_desc), + p_req("outpoint", param_outpoint, &info->outpoint), + p_req("description", param_string, &info->new_desc), NULL)) return command_param_failed(); - 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); + return refresh_moves(cmd, do_edit_desc, info); +} + +struct edit_desc_payment_info { + struct sha256 *identifier; + const char *new_desc; +}; + +static struct command_result *do_edit_desc_payment(struct command *cmd, + struct edit_desc_payment_info *info) +{ + struct json_stream *res; + struct channel_event **channel_events; + struct chain_event **chain_events; + struct bkpr *bkpr = bkpr_of(cmd->plugin); + + add_payment_hash_description(cmd, bkpr, info->identifier, info->new_desc); + + chain_events = get_chain_events_by_id(cmd, bkpr, cmd, info->identifier); + channel_events = get_channel_events_by_id(cmd, bkpr, cmd, info->identifier); res = jsonrpc_stream_success(cmd); json_array_start(res, "updated"); - json_add_events(res, NULL, chain_events, NULL); + json_add_events(res, bkpr, channel_events, chain_events, NULL); json_array_end(res); return command_finished(cmd, res); @@ -525,54 +725,34 @@ 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; + struct edit_desc_payment_info *info = tal(cmd, struct edit_desc_payment_info); if (!param(cmd, buf, params, - p_req("payment_id", param_sha256, &identifier), - p_req("description", param_string, &new_desc), + p_req("payment_id", param_sha256, &info->identifier), + p_req("description", param_string, &info->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); + return refresh_moves(cmd, do_edit_desc_payment, info); } -static struct command_result *json_list_balances(struct command *cmd, - const char *buf, - const jsmntok_t *params) +static struct command_result *do_list_balances(struct command *cmd, + void *unused) { struct json_stream *res; struct account **accts; - - if (!param(cmd, buf, params, NULL)) - return command_param_failed(); + struct bkpr *bkpr = bkpr_of(cmd->plugin); res = jsonrpc_stream_success(cmd); /* List of accts */ - db_begin_transaction(db); - accts = list_accounts(cmd, db); + accts = list_accounts(cmd, bkpr); json_array_start(res, "accounts"); for (size_t i = 0; i < tal_count(accts); i++) { struct amount_msat credit, debit, balance; bool has_events; - has_events = account_get_credit_debit(cmd->plugin, db, + has_events = account_get_credit_debit(bkpr, cmd, accts[i]->name, &credit, &debit); if (!amount_msat_sub(&balance, credit, debit)) { @@ -620,558 +800,84 @@ static struct command_result *json_list_balances(struct command *cmd, json_object_end(res); } json_array_end(res); - db_commit_transaction(db); return command_finished(cmd, res); } +static struct command_result *json_list_balances(struct command *cmd, + const char *buf, + const jsmntok_t *params) +{ + if (!param(cmd, buf, params, NULL)) + return command_param_failed(); + + return refresh_moves(cmd, do_list_balances, NULL); +} + struct new_account_info { struct account *acct; struct amount_msat curr_bal; u32 timestamp; }; -static void try_update_open_fees(struct command *cmd, - struct account *acct) +static struct command_result *log_error(struct command *cmd, + const char *method, + const char *buf, + const jsmntok_t *error, + void *arg UNNEEDED) { - struct chain_event *ev; - char *err; - - assert(acct->closed_event_db_id); - ev = find_chain_event_by_id(cmd, db, *acct->closed_event_db_id); - assert(ev); - - err = maybe_update_onchain_fees(cmd, db, ev->spending_txid); - if (err) - plugin_err(cmd->plugin, - "failure updating chain fees:" - " %s", err); + plugin_log(cmd->plugin, LOG_BROKEN, + "error calling %s: %.*s", + method, json_tok_full_len(error), + json_tok_full(buf, error)); + return notification_handled(cmd); } -static void find_push_amts(const char *buf, - const jsmntok_t *curr_chan, - bool is_opener, - struct amount_msat *push_credit, - struct amount_msat *push_debit, - bool *is_leased) +static char *do_account_close_checks(struct command *cmd, + struct bkpr *bkpr, + struct chain_event *e, + struct account *acct) { - const char *err; - struct amount_msat push_amt; - - /* Try to pull out fee_rcvd_msat */ - err = json_scan(tmpctx, buf, curr_chan, - "{funding:{fee_rcvd_msat:%}}", - JSON_SCAN(json_to_msat, - push_credit)); + struct account *closed_acct; - if (!err) { - *is_leased = true; - *push_debit = AMOUNT_MSAT(0); - return; - } + /* If is an external acct event, might be close channel related */ + if (!is_channel_account(acct->name) && e->origin_acct) { + closed_acct = find_account(bkpr, e->origin_acct); + } else if (!is_channel_account(acct->name) && !e->spending_txid) { + const char *acctname; - /* Try to pull out fee_paid_msat */ - err = json_scan(tmpctx, buf, curr_chan, - "{funding:{fee_paid_msat:%}}", - JSON_SCAN(json_to_msat, - push_debit)); - if (!err) { - *is_leased = true; - *push_credit = AMOUNT_MSAT(0); - return; - } + acctname = find_close_account_name(tmpctx, bkpr, cmd, &e->outpoint.txid); + if (acctname) { + closed_acct = find_account(bkpr, acctname); + } else { + closed_acct = NULL; + } + } else + /* Get most up to date account entry */ + closed_acct = find_account(bkpr, acct->name); - /* Try to pull out pushed amt? */ - err = json_scan(tmpctx, buf, curr_chan, - "{funding:{pushed_msat:%}}", - JSON_SCAN(json_to_msat, &push_amt)); - if (!err) { - *is_leased = false; - if (is_opener) { - *push_credit = AMOUNT_MSAT(0); - *push_debit = push_amt; - } else { - *push_credit = push_amt; - *push_debit = AMOUNT_MSAT(0); + if (closed_acct && closed_acct->closed_event_db_id) { + u64 closeheight = account_onchain_closeheight(bkpr, cmd, closed_acct); + if (closeheight != 0) { + char *err; + account_update_closeheight(cmd, closed_acct, closeheight); + err = update_channel_onchain_fees(cmd, cmd, bkpr, closed_acct); + if (err) { + return err; + } } - return; } - /* Nothing pushed nor fees paid */ - *is_leased = false; - *push_credit = AMOUNT_MSAT(0); - *push_debit = AMOUNT_MSAT(0); + return NULL; } -static bool new_missed_channel_account(struct command *cmd, - const char *buf, - const jsmntok_t *result, - struct account *acct, - u64 timestamp) +/* Returns true if "fatal" error, otherwise just a normal error */ +static char *fetch_out_desc_invstr(const tal_t *ctx, const char *buf, + const jsmntok_t *tok, char **err) { - struct chain_event *chain_ev; - const char *err; - size_t i; - const jsmntok_t *curr_chan, *chan_arr_tok; - - chan_arr_tok = json_get_member(buf, result, "channels"); - assert(chan_arr_tok && chan_arr_tok->type == JSMN_ARRAY); - - json_for_each_arr(i, curr_chan, chan_arr_tok) { - struct bitcoin_outpoint opt; - struct amount_msat amt, remote_amt, - push_credit, push_debit; - struct node_id peer_id; - char *opener, *chan_id; - enum mvt_tag *tags; - bool ok, is_opener, is_leased; - - err = json_scan(tmpctx, buf, curr_chan, - "{peer_id:%," - "channel_id:%," - "funding_txid:%," - "funding_outnum:%," - "funding:{local_funds_msat:%," - "remote_funds_msat:%}," - "opener:%}", - JSON_SCAN(json_to_node_id, &peer_id), - JSON_SCAN_TAL(tmpctx, json_strdup, &chan_id), - JSON_SCAN(json_to_txid, &opt.txid), - JSON_SCAN(json_to_number, &opt.n), - JSON_SCAN(json_to_msat, &amt), - JSON_SCAN(json_to_msat, &remote_amt), - JSON_SCAN_TAL(tmpctx, json_strdup, &opener)); - if (err) - plugin_err(cmd->plugin, - "failure scanning listpeerchannels" - " result: %s", err); - - if (!streq(chan_id, acct->name)) - continue; - - plugin_log(cmd->plugin, LOG_DBG, - "Logging channel account from list %s", - acct->name); - - chain_ev = tal(cmd, struct chain_event); - chain_ev->tag = mvt_tag_str(MVT_CHANNEL_OPEN); - chain_ev->debit = AMOUNT_MSAT(0); - ok = amount_msat_add(&chain_ev->output_value, - amt, remote_amt); - assert(ok); - chain_ev->origin_acct = NULL; - /* 2s before the channel opened, minimum */ - chain_ev->timestamp = timestamp - 2; - chain_ev->blockheight = 0; - chain_ev->outpoint = opt; - chain_ev->spending_txid = NULL; - chain_ev->payment_id = NULL; - chain_ev->stealable = false; - chain_ev->splice_close = false; - chain_ev->desc = NULL; - - /* Update the account info too */ - tags = tal_arr(chain_ev, enum mvt_tag, 1); - tags[0] = MVT_CHANNEL_OPEN; - - is_opener = streq(opener, "local"); - - /* Leased/pushed channels have some extra work */ - find_push_amts(buf, curr_chan, is_opener, - &push_credit, &push_debit, - &is_leased); - - if (is_leased) - tal_arr_expand(&tags, MVT_LEASED); - if (is_opener) - tal_arr_expand(&tags, MVT_OPENER); - - chain_ev->credit = amt; - db_begin_transaction(db); - if (!log_chain_event(db, acct, chain_ev)) - goto done; - - maybe_update_account(db, acct, chain_ev, - tags, 0, &peer_id); - maybe_update_onchain_fees(cmd, db, &opt.txid); - - /* We won't count the close's fees if we're - * *not* the opener, which we didn't know - * until now, so now try to update the - * fees for the close tx's spending_txid..*/ - if (acct->closed_event_db_id) - try_update_open_fees(cmd, acct); - - /* We log a channel event for the push amt */ - if (!amount_msat_is_zero(push_credit) - || !amount_msat_is_zero(push_debit)) { - struct channel_event *chan_ev; - char *chan_tag; - - chan_tag = tal_fmt(tmpctx, "%s", - mvt_tag_str( - is_leased ? - MVT_LEASE_FEE : MVT_PUSHED)); - chan_ev = new_channel_event(tmpctx, - chan_tag, - push_credit, - push_debit, - AMOUNT_MSAT(0), - NULL, 0, - timestamp - 1); - log_channel_event(db, acct, chan_ev); - } - -done: - db_commit_transaction(db); - return true; - } - - return false; -} - -/* Net out credit/debit --> basically find the diff */ -static char *msat_net(const tal_t *ctx, - struct amount_msat credit, - struct amount_msat debit, - struct amount_msat *credit_net, - struct amount_msat *debit_net) -{ - if (amount_msat_eq(credit, debit)) { - *credit_net = AMOUNT_MSAT(0); - *debit_net = AMOUNT_MSAT(0); - } else if (amount_msat_greater(credit, debit)) { - if (!amount_msat_sub(credit_net, credit, debit)) - return tal_fmt(ctx, "unexpected fail, can't sub." - " %s - %s", - fmt_amount_msat(ctx, credit), - fmt_amount_msat(ctx, debit)); - *debit_net = AMOUNT_MSAT(0); - } else { - if (!amount_msat_sub(debit_net, debit, credit)) { - return tal_fmt(ctx, "unexpected fail, can't sub." - " %s - %s", - fmt_amount_msat(ctx, debit), - fmt_amount_msat(ctx, credit)); - } - *credit_net = AMOUNT_MSAT(0); - } - - return NULL; -} - -static char *msat_find_diff(struct amount_msat balance, - struct amount_msat credits, - struct amount_msat debits, - struct amount_msat *credit_diff, - struct amount_msat *debit_diff) -{ - struct amount_msat net_credit, net_debit; - char *err; - - err = msat_net(tmpctx, credits, debits, - &net_credit, &net_debit); - if (err) - return err; - - /* If we're not missing events, debits == 0 */ - if (!amount_msat_is_zero(net_debit)) { - assert(amount_msat_is_zero(net_credit)); - if (!amount_msat_add(credit_diff, net_debit, balance)) - return "Overflow finding credit_diff"; - *debit_diff = AMOUNT_MSAT(0); - } else { - assert(amount_msat_is_zero(net_debit)); - if (amount_msat_greater(net_credit, balance)) { - if (!amount_msat_sub(debit_diff, net_credit, - balance)) - return "Err net_credit - amt"; - *credit_diff = AMOUNT_MSAT(0); - } else { - if (!amount_msat_sub(credit_diff, balance, - net_credit)) - return "Err amt - net_credit"; - - *debit_diff = AMOUNT_MSAT(0); - } - } - - return NULL; -} - -static void log_journal_entry(struct account *acct, - u64 timestamp, - struct amount_msat credit_diff, - struct amount_msat debit_diff) -{ - struct channel_event *chan_ev; - - /* No diffs to register, no journal needed */ - if (amount_msat_is_zero(credit_diff) - && amount_msat_is_zero(debit_diff)) - return; - - chan_ev = new_channel_event(tmpctx, - tal_fmt(tmpctx, "%s", - account_entry_tag_str(JOURNAL_ENTRY)), - credit_diff, - debit_diff, - AMOUNT_MSAT(0), - NULL, 0, - timestamp); - db_begin_transaction(db); - log_channel_event(db, acct, chan_ev); - db_commit_transaction(db); -} - -static struct command_result *log_error(struct command *cmd, - const char *method, - const char *buf, - const jsmntok_t *error, - void *arg UNNEEDED) -{ - plugin_log(cmd->plugin, LOG_BROKEN, - "error calling %s: %.*s", - method, json_tok_full_len(error), - json_tok_full(buf, error)); - - return notification_handled(cmd); -} - -static struct command_result *listpeerchannels_multi_done(struct command *cmd, - const char *method, - const char *buf, - const jsmntok_t *result, - struct new_account_info **new_accts) -{ - /* Let's register all these accounts! */ - for (size_t i = 0; i < tal_count(new_accts); i++) { - struct new_account_info *info = new_accts[i]; - struct amount_msat credit, debit, credit_diff, debit_diff; - char *err; - - if (!new_missed_channel_account(cmd, buf, result, - info->acct, - info->timestamp)) { - plugin_log(cmd->plugin, LOG_BROKEN, - "Unable to find account %s in listpeerchannels", - info->acct->name); - continue; - } - - db_begin_transaction(db); - account_get_credit_debit(cmd->plugin, db, - info->acct->name, - &credit, &debit); - db_commit_transaction(db); - - err = msat_find_diff(info->curr_bal, - credit, - debit, - &credit_diff, &debit_diff); - if (err) - plugin_err(cmd->plugin, "%s", err); - - log_journal_entry(info->acct, - info->timestamp - 1, - credit_diff, debit_diff); - } - plugin_log(cmd->plugin, LOG_DBG, "Snapshot balances updated"); - return notification_handled(cmd); -} - -static char *do_account_close_checks(const tal_t *ctx, - struct chain_event *e, - struct account *acct) -{ - struct account *closed_acct; - - db_begin_transaction(db); - - /* If is an external acct event, might be close channel related */ - if (!is_channel_account(acct->name) && e->origin_acct) { - closed_acct = find_account(ctx, db, e->origin_acct); - } else if (!is_channel_account(acct->name) && !e->spending_txid) - closed_acct = find_close_account(ctx, db, &e->outpoint.txid); - else - /* Get most up to date account entry */ - closed_acct = find_account(ctx, db, acct->name); - - - if (closed_acct && closed_acct->closed_event_db_id) { - maybe_mark_account_onchain(db, closed_acct); - if (closed_acct->onchain_resolved_block > 0) { - char *err; - err = update_channel_onchain_fees(ctx, db, closed_acct); - if (err) { - db_commit_transaction(db); - return err; - } - } - } - - db_commit_transaction(db); - - return NULL; -} - -static struct command_result *json_balance_snapshot(struct command *cmd, - const char *buf, - const jsmntok_t *params) -{ - const char *err; - size_t i; - u32 blockheight; - u64 timestamp; - struct new_account_info **new_accts; - const jsmntok_t *accounts_tok, *acct_tok, - *snap_tok = json_get_member(buf, params, "balance_snapshot"); - - if (snap_tok == NULL || snap_tok->type != JSMN_OBJECT) - plugin_err(cmd->plugin, - "`balance_snapshot` payload did not scan %s: %.*s", - "no 'balance_snapshot'", json_tok_full_len(params), - json_tok_full(buf, params)); - - err = json_scan(cmd, buf, snap_tok, - "{blockheight:%" - ",timestamp:%}", - JSON_SCAN(json_to_number, &blockheight), - JSON_SCAN(json_to_u64, ×tamp)); - - if (err) - plugin_err(cmd->plugin, - "`balance_snapshot` payload did not scan %s: %.*s", - err, json_tok_full_len(params), - json_tok_full(buf, params)); - - accounts_tok = json_get_member(buf, snap_tok, "accounts"); - if (accounts_tok == NULL || accounts_tok->type != JSMN_ARRAY) - plugin_err(cmd->plugin, - "`balance_snapshot` payload did not scan %s: %.*s", - "no 'balance_snapshot.accounts'", - json_tok_full_len(params), - json_tok_full(buf, params)); - - new_accts = tal_arr(cmd, struct new_account_info *, 0); - - db_begin_transaction(db); - json_for_each_arr(i, acct_tok, accounts_tok) { - struct account *acct; - struct amount_msat snap_balance, credit, debit, credit_diff, debit_diff; - char *acct_name; - bool existed; - - err = json_scan(cmd, buf, acct_tok, - "{account_id:%" - ",balance_msat:%}", - JSON_SCAN_TAL(tmpctx, json_strdup, &acct_name), - JSON_SCAN(json_to_msat, &snap_balance)); - if (err) - plugin_err(cmd->plugin, - "`balance_snapshot` payload did not" - " scan %s: %.*s", - err, json_tok_full_len(params), - json_tok_full(buf, params)); - - plugin_log(cmd->plugin, LOG_DBG, "account %s has balance %s", - acct_name, - fmt_amount_msat(tmpctx, snap_balance)); - - /* Find the account balances */ - account_get_credit_debit(cmd->plugin, db, acct_name, - &credit, &debit); - - /* Figure out what the net diff is btw reported & actual */ - err = msat_find_diff(snap_balance, - credit, - debit, - &credit_diff, &debit_diff); - if (err) - plugin_err(cmd->plugin, - "Unable to find_diff for amounts: %s", - err); - - acct = find_account(cmd, db, acct_name); - if (!acct) { - plugin_log(cmd->plugin, LOG_INFORM, - "account %s not found, adding", - acct_name); - - /* FIXME: lookup peer id for channel? */ - acct = new_account(cmd, acct_name, NULL); - account_add(db, acct); - existed = false; - } else - existed = true; - - /* If we're entering a channel account, - * from a balance entry, we need to - * go find the channel open info*/ - if (!existed && is_channel_account(acct->name)) { - struct new_account_info *info; - u64 timestamp_now; - - timestamp_now = time_now().ts.tv_sec; - info = tal(new_accts, struct new_account_info); - info->acct = tal_steal(info, acct); - info->curr_bal = snap_balance; - info->timestamp = timestamp_now; - - tal_arr_expand(&new_accts, info); - continue; - } - - if (!amount_msat_is_zero(credit_diff) || !amount_msat_is_zero(debit_diff)) { - struct channel_event *ev; - - plugin_log(cmd->plugin, LOG_UNUSUAL, - "Snapshot balance does not equal ondisk" - " reported %s, off by (+%s/-%s) (account %s)" - " Logging journal entry.", - fmt_amount_msat(tmpctx, snap_balance), - fmt_amount_msat(tmpctx, debit_diff), - fmt_amount_msat(tmpctx, credit_diff), - acct_name); - - - ev = new_channel_event(cmd, - tal_fmt(tmpctx, "%s", - account_entry_tag_str(JOURNAL_ENTRY)), - credit_diff, - debit_diff, - AMOUNT_MSAT(0), - NULL, 0, - timestamp); - - log_channel_event(db, acct, ev); - } - } - db_commit_transaction(db); - - if (tal_count(new_accts) > 0) { - struct out_req *req; - - req = jsonrpc_request_start(cmd, - "listpeerchannels", - listpeerchannels_multi_done, - log_error, - new_accts); - /* FIXME(vicenzopalazzo) require the channel by channel_id to avoid parsing not useful json */ - return send_outreq(req); - } - - plugin_log(cmd->plugin, LOG_DBG, "Snapshot balances updated"); - return notification_handled(cmd); -} - -/* Returns true if "fatal" error, otherwise just a normal error */ -static char *fetch_out_desc_invstr(const tal_t *ctx, const char *buf, - const jsmntok_t *tok, char **err) -{ - char *bolt, *desc, *fail; + char *bolt, *desc, *fail; /* It's a bolt11! Parse it out to a desc */ if (!json_scan(ctx, buf, tok, "{bolt11:%}", @@ -1229,16 +935,23 @@ static char *fetch_out_desc_invstr(const tal_t *ctx, const char *buf, return desc; } +struct payment_hash_info { + struct refresh_info *rinfo; + struct sha256 payment_hash; +}; + static struct command_result * listinvoices_done(struct command *cmd, const char *method, const char *buf, const jsmntok_t *result, - struct sha256 *payment_hash) + struct payment_hash_info *phinfo) { size_t i; const jsmntok_t *inv_arr_tok, *inv_tok; const char *desc; + struct bkpr *bkpr = bkpr_of(cmd->plugin); + inv_arr_tok = json_get_member(buf, result, "invoices"); assert(inv_arr_tok->type == JSMN_ARRAY); @@ -1262,11 +975,10 @@ listinvoices_done(struct command *cmd, } if (desc) { - db_begin_transaction(db); - add_payment_hash_desc(db, payment_hash, + add_payment_hash_description(cmd, bkpr, &phinfo->payment_hash, json_escape_unescape(cmd, (struct json_escape *)desc)); - db_commit_transaction(db); + } else plugin_log(cmd->plugin, LOG_DBG, "listinvoices:" @@ -1274,7 +986,7 @@ listinvoices_done(struct command *cmd, " not found (%.*s)", result->end - result->start, buf); - return notification_handled(cmd); + return rinfo_one_done(cmd, phinfo->rinfo); } static struct command_result * @@ -1282,11 +994,13 @@ listsendpays_done(struct command *cmd, const char *method, const char *buf, const jsmntok_t *result, - struct sha256 *payment_hash) + struct payment_hash_info *phinfo) { size_t i; const jsmntok_t *pays_arr_tok, *pays_tok; const char *desc; + struct bkpr *bkpr = bkpr_of(cmd->plugin); + pays_arr_tok = json_get_member(buf, result, "payments"); assert(pays_arr_tok->type == JSMN_ARRAY); @@ -1305,139 +1019,113 @@ listsendpays_done(struct command *cmd, } if (desc) { - db_begin_transaction(db); - add_payment_hash_desc(db, payment_hash, desc); - db_commit_transaction(db); + add_payment_hash_description(cmd, bkpr, &phinfo->payment_hash, desc); } else plugin_log(cmd->plugin, LOG_DBG, "listpays: bolt11/bolt12 not found:" "(%.*s)", result->end - result->start, buf); - return notification_handled(cmd); + return rinfo_one_done(cmd, phinfo->rinfo); } static struct command_result *lookup_invoice_desc(struct command *cmd, struct amount_msat credit, - struct sha256 *payment_hash STEALS) + const struct sha256 *payment_hash, + struct refresh_info *rinfo) { struct out_req *req; + struct payment_hash_info *phinfo; + + phinfo = tal(cmd, struct payment_hash_info); + phinfo->payment_hash = *payment_hash; + phinfo->rinfo = use_rinfo(rinfo); - /* Otherwise will go away when event is cleaned up */ - tal_steal(cmd, payment_hash); if (!amount_msat_is_zero(credit)) req = jsonrpc_request_start(cmd, "listinvoices", listinvoices_done, log_error, - payment_hash); + phinfo); else req = jsonrpc_request_start(cmd, "listsendpays", listsendpays_done, log_error, - payment_hash); + phinfo); json_add_sha256(req->js, "payment_hash", payment_hash); return send_outreq(req); } -struct event_info { - struct chain_event *ev; - struct account *acct; -}; - -static struct command_result * -listpeerchannels_done(struct command *cmd, - const char *method, - const char *buf, - const jsmntok_t *result, - struct event_info *info) +static enum mvt_tag *json_to_tags(const tal_t *ctx, const char *buffer, const jsmntok_t *tok) { - struct amount_msat credit, debit, credit_diff, debit_diff; - const char *err; - - if (new_missed_channel_account(cmd, buf, result, - info->acct, - info->ev->timestamp)) { - db_begin_transaction(db); - account_get_credit_debit(cmd->plugin, db, info->acct->name, - &credit, &debit); - db_commit_transaction(db); - - /* The expected current balance is zero, since - * we just got the channel close event */ - err = msat_find_diff(AMOUNT_MSAT(0), - credit, - debit, - &credit_diff, &debit_diff); - if (err) - plugin_err(cmd->plugin, "%s", err); - - log_journal_entry(info->acct, - info->ev->timestamp - 1, - credit_diff, debit_diff); - } else - plugin_log(cmd->plugin, LOG_BROKEN, - "Unable to find account %s in listpeers", - info->acct->name); - - /* Maybe mark acct as onchain resolved */ - err = do_account_close_checks(cmd, info->ev, info->acct); - if (err) - plugin_err(cmd->plugin, "%s", err); + size_t i; + const jsmntok_t *t; + enum mvt_tag *tags = tal_arr(ctx, enum mvt_tag, tok->size); - if (info->ev->payment_id && - streq(info->ev->tag, mvt_tag_str(MVT_INVOICE))) { - return lookup_invoice_desc(cmd, info->ev->credit, - info->ev->payment_id); + json_for_each_arr(i, t, tok) { + if (!json_to_coin_mvt_tag(buffer, t, &tags[i])) + return tal_free(tags); } - return notification_handled(cmd); + return tags; } -static struct command_result * + +static void parse_and_log_chain_move(struct command *cmd, const char *buf, - const jsmntok_t *params, - const char *acct_name STEALS, - const struct amount_msat credit, - const struct amount_msat debit, - const char *coin_type STEALS, - const u64 timestamp, - const enum mvt_tag *tags, - const char *desc) + const jsmntok_t *chainmove, + struct refresh_info *rinfo) { struct chain_event *e = tal(cmd, struct chain_event); struct sha256 *payment_hash = tal(cmd, struct sha256); struct bitcoin_txid *spending_txid = tal(cmd, struct bitcoin_txid); struct node_id *peer_id; - struct account *acct, *orig_acct; + struct account *acct; u32 closed_count; + char *acct_name; const char *err; + struct bkpr *bkpr = bkpr_of(cmd->plugin); + enum mvt_tag tag, *tags; /* Fields we expect on *every* chain movement */ - err = json_scan(tmpctx, buf, params, - "{coin_movement:" - "{utxo:%" + closed_count = 0; + err = json_scan(tmpctx, buf, chainmove, + "{account_id:%" + ",created_index:%" + ",credit_msat:%" + ",debit_msat:%" + ",timestamp:%" + ",utxo:%" ",output_msat:%" ",blockheight:%" - "}}", + ",primary_tag:%" + ",extra_tags:%" + ",output_count?:%" + "}", + JSON_SCAN_TAL(tmpctx, json_strdup, &acct_name), + JSON_SCAN(json_to_u64, &e->db_id), + JSON_SCAN(json_to_msat, &e->credit), + JSON_SCAN(json_to_msat, &e->debit), + JSON_SCAN(json_to_u64, &e->timestamp), JSON_SCAN(json_to_outpoint, &e->outpoint), JSON_SCAN(json_to_msat, &e->output_value), - JSON_SCAN(json_to_number, &e->blockheight)); - + JSON_SCAN(json_to_number, &e->blockheight), + JSON_SCAN(json_to_coin_mvt_tag, &tag), + JSON_SCAN_TAL(tmpctx, json_to_tags, &tags), + JSON_SCAN(json_to_number, &closed_count)); if (err) plugin_err(cmd->plugin, - "`coin_movement` parameters did" + "chainmove did" " not scan %s: %.*s", - err, json_tok_full_len(params), - json_tok_full(buf, params)); + err, json_tok_full_len(chainmove), + json_tok_full(buf, chainmove)); /* Now try to get out the optional parts */ - err = json_scan(tmpctx, buf, params, - "{coin_movement:" + err = json_scan(tmpctx, buf, chainmove, "{spending_txid:%" - "}}", + "}", JSON_SCAN(json_to_txid, spending_txid)); if (err) { @@ -1448,7 +1136,7 @@ parse_and_log_chain_move(struct command *cmd, e->spending_txid = tal_steal(e, spending_txid); /* Now try to get out the optional parts */ - err = json_scan(tmpctx, buf, params, + err = json_scan(tmpctx, buf, chainmove, "{coin_movement:" "{payment_hash:%" "}}", @@ -1459,9 +1147,8 @@ parse_and_log_chain_move(struct command *cmd, err = tal_free(err); } - err = json_scan(tmpctx, buf, params, - "{coin_movement:" - "{originating_account:%}}", + err = json_scan(tmpctx, buf, chainmove, + "{originating_account:%}", JSON_SCAN_TAL(e, json_strdup, &e->origin_acct)); if (err) { @@ -1470,81 +1157,66 @@ parse_and_log_chain_move(struct command *cmd, } peer_id = tal(cmd, struct node_id); - err = json_scan(tmpctx, buf, params, - "{coin_movement:" - "{peer_id:%}}", + err = json_scan(tmpctx, buf, chainmove, + "{peer_id:%}", JSON_SCAN(json_to_node_id, peer_id)); if (err) { peer_id = tal_free(peer_id); err = tal_free(err); } - - err = json_scan(tmpctx, buf, params, - "{coin_movement:" - "{output_count:%}}", - JSON_SCAN(json_to_number, &closed_count)); - - if (err) { - closed_count = 0; - err = tal_free(err); - } - e->payment_id = tal_steal(e, payment_hash); - e->credit = credit; - e->debit = debit; - e->timestamp = timestamp; - e->tag = mvt_tag_str(tags[0]); - e->desc = tal_steal(e, desc); - + e->tag = mvt_tag_str(tag); e->stealable = false; e->splice_close = false; + e->foreign = false; for (size_t i = 0; i < tal_count(tags); i++) { e->stealable |= tags[i] == MVT_STEALABLE; e->splice_close |= tags[i] == MVT_SPLICE; + e->foreign |= tags[i] == MVT_FOREIGN; } + /* FIXME: tags below is expected to contain primary tag too */ + tal_arr_insert(&tags, 0, tag); - db_begin_transaction(db); - acct = find_account(tmpctx, db, acct_name); - - if (!acct) { - /* FIXME: lookup the peer id for this channel! */ - acct = new_account(tmpctx, acct_name, NULL); - account_add(db, acct); - } - - if (e->origin_acct) { - orig_acct = find_account(tmpctx, db, e->origin_acct); - /* Go fetch the originating account - * (we might not have it) */ - if (!orig_acct) { - orig_acct = new_account(tmpctx, e->origin_acct, NULL); - account_add(db, orig_acct); - } - } else - orig_acct = NULL; - - - if (!log_chain_event(db, acct, e)) { - db_commit_transaction(db); - /* This is not a new event, do nothing */ - return notification_handled(cmd); - } + /* For tests, we log these harder! */ + if (e->foreign) + plugin_log(cmd->plugin, LOG_DBG, + "Foreign chain event: %s (%s) %s -%s %"PRIu64" %d %s %s", + e->tag, acct_name, + fmt_amount_msat(tmpctx, e->credit), + fmt_amount_msat(tmpctx, e->debit), + e->timestamp, e->blockheight, + fmt_bitcoin_outpoint(tmpctx, &e->outpoint), + e->spending_txid ? fmt_bitcoin_txid(tmpctx, e->spending_txid) : ""); + + plugin_log(cmd->plugin, LOG_DBG, "coin_move 2 (%s) %s -%s %s %"PRIu64, + e->tag, + fmt_amount_msat(tmpctx, e->credit), + fmt_amount_msat(tmpctx, e->debit), + CHAIN_MOVE, e->timestamp); + + /* FIXME: lookup the peer id for this channel! */ + acct = find_or_create_account(cmd, bkpr, acct_name); + + if (e->origin_acct) + find_or_create_account(cmd, bkpr, e->origin_acct); + + /* Make this visible for queries (we expect increasing!) */ + assert(e->db_id > bkpr->chainmoves_index); + bkpr->chainmoves_index = e->db_id; /* This event *might* have implications for account; * update as necessary */ - maybe_update_account(db, acct, e, tags, closed_count, + maybe_update_account(cmd, acct, e, tags, closed_count, peer_id); /* Can we calculate any onchain fees now? */ - err = maybe_update_onchain_fees(cmd, db, + err = maybe_update_onchain_fees(cmd, cmd, bkpr, e->spending_txid ? e->spending_txid : &e->outpoint.txid); - db_commit_transaction(db); - if (err) plugin_err(cmd->plugin, "Unable to update onchain fees %s", @@ -1554,47 +1226,15 @@ parse_and_log_chain_move(struct command *cmd, * that it we've got an external deposit that's now * confirmed */ if (e->spending_txid) { - db_begin_transaction(db); /* Go see if there's any deposits to an external * that are now confirmed */ /* FIXME: might need updating when we can splice? */ - maybe_closeout_external_deposits(db, e->spending_txid, + maybe_closeout_external_deposits(cmd, bkpr, e->spending_txid, e->blockheight); - db_commit_transaction(db); - } - - /* If this is a channel account event, it's possible - * that we *never* got the open event. (This happens - * if you add the plugin *after* you've closed the channel) */ - if ((!acct->open_event_db_id && is_channel_account(acct->name)) - || (orig_acct && is_channel_account(orig_acct->name) - && !orig_acct->open_event_db_id)) { - /* Find the channel open info for this peer */ - struct out_req *req; - struct event_info *info; - - plugin_log(cmd->plugin, LOG_DBG, - "channel event received but no open for channel %s." - " Calling `listpeerchannls` to fetch missing info", - acct->name); - - info = tal(cmd, struct event_info); - info->ev = tal_steal(info, e); - info->acct = tal_steal(info, - is_channel_account(acct->name) ? - acct : orig_acct); - - req = jsonrpc_request_start(cmd, - "listpeerchannels", - listpeerchannels_done, - log_error, - info); - /* FIXME: use the peer_id to reduce work here */ - return send_outreq(req); } /* Maybe mark acct as onchain resolved */ - err = do_account_close_checks(cmd, e, acct); + err = do_account_close_checks(cmd, bkpr, e, acct); if (err) plugin_err(cmd->plugin, "%s", err); @@ -1604,201 +1244,181 @@ parse_and_log_chain_move(struct command *cmd, if (tags[i] != MVT_INVOICE) continue; - return lookup_invoice_desc(cmd, e->credit, - e->payment_id); + lookup_invoice_desc(cmd, e->credit, + e->payment_id, rinfo); + break; } } - - return notification_handled(cmd);; } -static struct command_result * +static void parse_and_log_channel_move(struct command *cmd, const char *buf, - const jsmntok_t *params, - const char *acct_name STEALS, - const struct amount_msat credit, - const struct amount_msat debit, - const char *coin_type STEALS, - const u64 timestamp, - const enum mvt_tag *tags, - const char *desc) + const jsmntok_t *channelmove, + struct refresh_info *rinfo) { struct channel_event *e = tal(cmd, struct channel_event); struct account *acct; const char *err; + char *acct_name; + enum mvt_tag tag; + struct bkpr *bkpr = bkpr_of(cmd->plugin); + + /* Fields we expect on *every* channel movement */ + e->part_id = 0; + e->fees = AMOUNT_MSAT(0); + err = json_scan(tmpctx, buf, channelmove, + "{account_id:%" + ",created_index:%" + ",credit_msat:%" + ",debit_msat:%" + ",timestamp:%" + ",primary_tag:%" + ",part_id?:%" + ",fees_msat?:%}", + JSON_SCAN_TAL(tmpctx, json_strdup, &acct_name), + JSON_SCAN(json_to_u64, &e->db_id), + JSON_SCAN(json_to_msat, &e->credit), + JSON_SCAN(json_to_msat, &e->debit), + JSON_SCAN(json_to_u64, &e->timestamp), + JSON_SCAN(json_to_coin_mvt_tag, &tag), + JSON_SCAN(json_to_number, &e->part_id), + JSON_SCAN(json_to_msat, &e->fees)); + if (err) + plugin_err(cmd->plugin, + "channelmove did" + " not scan %s: %.*s", + err, json_tok_full_len(channelmove), + json_tok_full(buf, channelmove)); + + e->tag = mvt_tag_str(tag); e->payment_id = tal(e, struct sha256); - err = json_scan(tmpctx, buf, params, - "{coin_movement:{payment_hash:%}}", + err = json_scan(tmpctx, buf, channelmove, + "{payment_hash:%}", JSON_SCAN(json_to_sha256, e->payment_id)); if (err) { e->payment_id = tal_free(e->payment_id); err = tal_free(err); } - err = json_scan(tmpctx, buf, params, - "{coin_movement:{part_id:%}}", - JSON_SCAN(json_to_number, &e->part_id)); - if (err) { - e->part_id = 0; - err = tal_free(err); - } - - err = json_scan(tmpctx, buf, params, - "{coin_movement:{fees_msat:%}}", - JSON_SCAN(json_to_msat, &e->fees)); - if (err) { - e->fees = AMOUNT_MSAT(0); - err = tal_free(err); - } - - e->credit = credit; - e->debit = debit; - e->timestamp = timestamp; - e->tag = mvt_tag_str(tags[0]); - e->desc = tal_steal(e, desc); - e->rebalance_id = NULL; + plugin_log(cmd->plugin, LOG_DBG, "coin_move 2 (%s) %s -%s %s %"PRIu64, + e->tag, + fmt_amount_msat(tmpctx, e->credit), + fmt_amount_msat(tmpctx, e->debit), + CHANNEL_MOVE, e->timestamp); /* Go find the account for this event */ - db_begin_transaction(db); - acct = find_account(tmpctx, db, acct_name); + acct = find_account(bkpr, acct_name); if (!acct) plugin_err(cmd->plugin, "Received channel event," " but no account exists %s", acct_name); - log_channel_event(db, acct, e); + /* Make this visible for queries (we expect increasing!) */ + assert(e->db_id > bkpr->channelmoves_index); + bkpr->channelmoves_index = e->db_id; /* Check for invoice desc data, necessary */ - if (e->payment_id) { - for (size_t i = 0; i < tal_count(tags); i++) { - if (tags[i] != MVT_INVOICE) - continue; - - /* We only do rebalance checks for debits, - * the credit event always arrives first */ - if (!amount_msat_is_zero(e->debit)) - maybe_record_rebalance(db, e); + if (e->payment_id && tag == MVT_INVOICE) { + /* We only do rebalance checks for debits, + * the credit event always arrives first */ + if (!amount_msat_is_zero(e->debit)) + maybe_record_rebalance(cmd, bkpr, e); - db_commit_transaction(db); - return lookup_invoice_desc(cmd, e->credit, - e->payment_id); - } + lookup_invoice_desc(cmd, e->credit, e->payment_id, rinfo); + return; } +} - db_commit_transaction(db); - return notification_handled(cmd); +static bool json_to_tok(const char *buffer, const jsmntok_t *tok, const jsmntok_t **ret) +{ + *ret = tok; + return true; } -static char *parse_tags(const tal_t *ctx, - const char *buf, - const jsmntok_t *tok, - enum mvt_tag **tags) +static struct command_result *inject_refresh_done(struct command *notif_cmd, + void *unused) { - size_t i; - const jsmntok_t *extras_tok, - *tag_tok = json_get_member(buf, tok, "primary_tag"); - - if (tag_tok == NULL) - return "missing 'primary_tag' field"; - *tags = tal_arr(ctx, enum mvt_tag, 1); - if (!json_to_coin_mvt_tag(buf, tag_tok, &(*tags)[0])) - return "Unable to parse 'primary_tag'"; - - extras_tok = json_get_member(buf, tok, "extra_tags"); - tal_resize(tags, 1 + extras_tok->size); - json_for_each_arr(i, tag_tok, extras_tok) { - if (!json_to_coin_mvt_tag(buf, tag_tok, &(*tags)[i + 1])) - return "Unable to parse 'extra_tags'"; - } + return notification_handled(notif_cmd); +} - return NULL; +static struct command_result *inject_done(struct command *notif_cmd, + const char *methodname, + const char *buf, + const jsmntok_t *result, + void *unused) +{ + /* We could do this lazily, but tests assume it happens now */ + return refresh_moves(notif_cmd, inject_refresh_done, NULL); } +/* FIXME: Deprecate */ static struct command_result *json_utxo_deposit(struct command *cmd, const char *buf, const jsmntok_t *params) { - const char *move_tag ="utxo_deposit"; - struct chain_event *ev = tal(cmd, struct chain_event); - struct account *acct; + const char *acct_name, *origin_acct; + struct amount_msat amount; + struct bitcoin_outpoint outpoint; + u64 timestamp; + u32 blockheight; + const jsmntok_t *transfer_from; const char *err; + struct out_req *req; + transfer_from = NULL; err = json_scan(tmpctx, buf, params, "{utxo_deposit:{" "account:%" - ",transfer_from:%" + ",transfer_from?:%" ",outpoint:%" ",amount_msat:%" ",timestamp:%" ",blockheight:%" "}}", - JSON_SCAN_TAL(tmpctx, json_strdup, &ev->acct_name), - JSON_SCAN_TAL(tmpctx, json_strdup, &ev->origin_acct), - JSON_SCAN(json_to_outpoint, &ev->outpoint), - JSON_SCAN(json_to_msat, &ev->credit), - JSON_SCAN(json_to_u64, &ev->timestamp), - JSON_SCAN(json_to_u32, &ev->blockheight)); + JSON_SCAN_TAL(tmpctx, json_strdup, &acct_name), + JSON_SCAN(json_to_tok, &transfer_from), + JSON_SCAN(json_to_outpoint, &outpoint), + JSON_SCAN(json_to_msat, &amount), + JSON_SCAN(json_to_u64, ×tamp), + JSON_SCAN(json_to_u32, &blockheight)); if (err) plugin_err(cmd->plugin, - "`%s` parameters did not scan %s: %.*s", - move_tag, err, json_tok_full_len(params), + "`utxo_deposit` parameters did not scan %s: %.*s", + err, json_tok_full_len(params), json_tok_full(buf, params)); - /* Log the thing */ - db_begin_transaction(db); - acct = find_account(tmpctx, db, ev->acct_name); - - if (!acct) { - acct = new_account(tmpctx, ev->acct_name, NULL); - account_add(db, acct); - } - - ev->tag = "deposit"; - ev->stealable = false; - ev->rebalance = false; - ev->splice_close = false; - ev->debit = AMOUNT_MSAT(0); - ev->output_value = ev->credit; - ev->spending_txid = NULL; - ev->payment_id = NULL; - ev->desc = NULL; - ev->splice_close = false; - - plugin_log(cmd->plugin, LOG_DBG, "%s (%s|%s) %s -%s %"PRIu64" %d %s", - move_tag, ev->tag, ev->acct_name, - fmt_amount_msat(tmpctx, ev->credit), - fmt_amount_msat(tmpctx, ev->debit), - ev->timestamp, ev->blockheight, - fmt_bitcoin_outpoint(tmpctx, &ev->outpoint)); - - if (!log_chain_event(db, acct, ev)) { - db_commit_transaction(db); - /* This is not a new event, do nothing */ - return notification_handled(cmd); - } - - /* Can we calculate any onchain fees now? */ - err = maybe_update_onchain_fees(cmd, db, &ev->outpoint.txid); - db_commit_transaction(db); - if (err) - plugin_err(cmd->plugin, - "Unable to update onchain fees %s", - err); - - /* FIXME: do account close checks, when allow onchain close to externals? */ - return notification_handled(cmd);; + if (!transfer_from || json_tok_is_null(buf, transfer_from)) + origin_acct = NULL; + else + origin_acct = json_strdup(tmpctx, buf, transfer_from); + + req = jsonrpc_request_start(cmd, "injectutxodeposit", + inject_done, + plugin_broken_cb, + NULL); + json_add_string(req->js, "account", acct_name); + if (origin_acct) + json_add_string(req->js, "transfer_from", origin_acct); + json_add_outpoint(req->js, "outpoint", &outpoint); + json_add_amount_msat(req->js, "amount_msat", amount); + json_add_u64(req->js, "timestamp", timestamp); + json_add_u32(req->js, "blockheight", blockheight); + return send_outreq(req); } static struct command_result *json_utxo_spend(struct command *cmd, const char *buf, const jsmntok_t *params) { - const char *move_tag ="utxo_spend"; - struct account *acct; - struct chain_event *ev = tal(cmd, struct chain_event); - const char *err, *acct_name; + const char *acct_name; + struct amount_msat amount; + struct bitcoin_txid spending_txid; + struct bitcoin_outpoint outpoint; + u64 timestamp; + u32 blockheight; + const char *err; + struct out_req *req; - ev->spending_txid = tal(ev, struct bitcoin_txid); err = json_scan(tmpctx, buf, params, "{utxo_spend:{" "account:%" @@ -1809,154 +1429,32 @@ static struct command_result *json_utxo_spend(struct command *cmd, const char *b ",blockheight:%" "}}", JSON_SCAN_TAL(tmpctx, json_strdup, &acct_name), - JSON_SCAN(json_to_outpoint, &ev->outpoint), - JSON_SCAN(json_to_txid, ev->spending_txid), - JSON_SCAN(json_to_msat, &ev->debit), - JSON_SCAN(json_to_u64, &ev->timestamp), - JSON_SCAN(json_to_u32, &ev->blockheight)); - - if (err) - plugin_err(cmd->plugin, - "`%s` parameters did not scan %s: %.*s", - move_tag, err, json_tok_full_len(params), - json_tok_full(buf, params)); - - /* Log the thing */ - db_begin_transaction(db); - acct = find_account(tmpctx, db, acct_name); - - if (!acct) { - acct = new_account(tmpctx, acct_name, NULL); - account_add(db, acct); - } - - ev->origin_acct = NULL; - ev->tag = "withdrawal"; - ev->stealable = false; - ev->rebalance = false; - ev->splice_close = false; - ev->credit = AMOUNT_MSAT(0); - ev->output_value = ev->debit; - ev->payment_id = NULL; - ev->desc = NULL; - - plugin_log(cmd->plugin, LOG_DBG, "%s (%s|%s) %s -%s %"PRIu64" %d %s %s", - move_tag, ev->tag, acct_name, - fmt_amount_msat(tmpctx, ev->credit), - fmt_amount_msat(tmpctx, ev->debit), - ev->timestamp, ev->blockheight, - fmt_bitcoin_outpoint(tmpctx, &ev->outpoint), - fmt_bitcoin_txid(tmpctx, ev->spending_txid)); - - if (!log_chain_event(db, acct, ev)) { - db_commit_transaction(db); - /* This is not a new event, do nothing */ - return notification_handled(cmd); - } - - err = maybe_update_onchain_fees(cmd, db, ev->spending_txid); - if (err) { - db_commit_transaction(db); - plugin_err(cmd->plugin, - "Unable to update onchain fees %s", - err); - } - - err = maybe_update_onchain_fees(cmd, db, &ev->outpoint.txid); - if (err) { - db_commit_transaction(db); - plugin_err(cmd->plugin, - "Unable to update onchain fees %s", - err); - } - - /* Go see if there's any deposits to an external - * that are now confirmed */ - /* FIXME: might need updating when we can splice? */ - maybe_closeout_external_deposits(db, ev->spending_txid, - ev->blockheight); - db_commit_transaction(db); - - /* FIXME: do account close checks, when allow onchain close to externals? */ - return notification_handled(cmd);; -} - -static struct command_result *json_coin_moved(struct command *cmd, - const char *buf, - const jsmntok_t *params) -{ - const char *err, *mvt_type, *acct_name, *coin_type; - u32 version; - u64 timestamp; - struct amount_msat credit, debit; - enum mvt_tag *tags; - - err = json_scan(tmpctx, buf, params, - "{coin_movement:" - "{version:%" - ",type:%" - ",account_id:%" - ",credit_msat:%" - ",debit_msat:%" - ",coin_type:%" - ",timestamp:%" - "}}", - JSON_SCAN(json_to_number, &version), - JSON_SCAN_TAL(tmpctx, json_strdup, &mvt_type), - JSON_SCAN_TAL(tmpctx, json_strdup, &acct_name), - JSON_SCAN(json_to_msat, &credit), - JSON_SCAN(json_to_msat, &debit), - JSON_SCAN_TAL(tmpctx, json_strdup, &coin_type), - JSON_SCAN(json_to_u64, ×tamp)); - - if (err) - plugin_err(cmd->plugin, - "`coin_movement` parameters did not scan %s: %.*s", - err, json_tok_full_len(params), - json_tok_full(buf, params)); + JSON_SCAN(json_to_outpoint, &outpoint), + JSON_SCAN(json_to_txid, &spending_txid), + JSON_SCAN(json_to_msat, &amount), + JSON_SCAN(json_to_u64, ×tamp), + JSON_SCAN(json_to_u32, &blockheight)); - err = parse_tags(tmpctx, buf, - json_get_member(buf, params, "coin_movement"), - &tags); if (err) plugin_err(cmd->plugin, - "`coin_movement` parameters did not scan %s: %.*s", + "`utxo_spend` parameters did not scan %s: %.*s", err, json_tok_full_len(params), json_tok_full(buf, params)); - /* We expect version 2 of coin movements */ - assert(version == 2); - - plugin_log(cmd->plugin, LOG_DBG, "coin_move %d (%s) %s -%s %s %"PRIu64, - version, - mvt_tag_str(tags[0]), - fmt_amount_msat(tmpctx, credit), - fmt_amount_msat(tmpctx, debit), - mvt_type, timestamp); - - if (streq(mvt_type, CHAIN_MOVE)) - return parse_and_log_chain_move(cmd, buf, params, - acct_name, credit, debit, - coin_type, timestamp, tags, - NULL); - - - assert(streq(mvt_type, CHANNEL_MOVE)); - return parse_and_log_channel_move(cmd, buf, params, - acct_name, credit, debit, - coin_type, timestamp, tags, - NULL); + req = jsonrpc_request_start(cmd, "injectutxospend", + inject_done, + plugin_broken_cb, + NULL); + json_add_string(req->js, "account", acct_name); + json_add_outpoint(req->js, "outpoint", &outpoint); + json_add_txid(req->js, "spending_txid", &spending_txid); + json_add_amount_msat(req->js, "amount_msat", amount); + json_add_u64(req->js, "timestamp", timestamp); + json_add_u32(req->js, "blockheight", blockheight); + return send_outreq(req); } const struct plugin_notification notifs[] = { - { - "coin_movement", - json_coin_moved, - }, - { - "balance_snapshot", - json_balance_snapshot, - }, { "utxo_deposit", json_utxo_deposit, @@ -2002,54 +1500,55 @@ static const struct plugin_command commands[] = { }, }; +static bool json_hex_to_be64(const char *buffer, const jsmntok_t *tok, + be64 *val) +{ + return hex_decode(buffer + tok->start, tok->end - tok->start, + val, sizeof(*val)); +} + static const char *init(struct command *init_cmd, const char *b, const jsmntok_t *t) { struct plugin *p = init_cmd->plugin; - - /* Switch to bookkeeper-dir, if specified */ - if (datadir && chdir(datadir) != 0) { - if (mkdir(datadir, 0700) != 0 && errno != EEXIST) - plugin_err(p, - "Unable to create 'bookkeeper-dir'=%s", - datadir); - if (chdir(datadir) != 0) - plugin_err(p, - "Unable to switch to 'bookkeeper-dir'=%s", - datadir); - } - - /* No user suppled db_dsn, set one up here */ - if (!db_dsn) - db_dsn = tal_fmt(NULL, "sqlite3://accounts.sqlite3"); - - plugin_log(p, LOG_DBG, "Setting up database at %s", db_dsn); - db = notleak(db_setup(p, p, db_dsn)); - db_dsn = tal_free(db_dsn); + struct bkpr *bkpr = bkpr_of(p); + be64 index; + + bkpr->accounts = init_accounts(bkpr, init_cmd); + bkpr->onchain_fees = init_onchain_fees(bkpr, init_cmd); + bkpr->descriptions = init_descriptions(bkpr, init_cmd); + bkpr->rebalances = init_rebalances(bkpr, init_cmd); + bkpr->blockheights = init_blockheights(bkpr, init_cmd); + + /* Callers always expect the wallet account to exist. */ + find_or_create_account(init_cmd, bkpr, ACCOUNT_NAME_WALLET); + + /* Not existing is OK! */ + if (rpc_scan_datastore_hex(tmpctx, init_cmd, "bookkeeper/channelmoves_index", + JSON_SCAN(json_hex_to_be64, &index)) == NULL) { + bkpr->channelmoves_index = be64_to_cpu(index); + } else + bkpr->channelmoves_index = 0; + if (rpc_scan_datastore_hex(tmpctx, init_cmd, "bookkeeper/chainmoves_index", + JSON_SCAN(json_hex_to_be64, &index)) == NULL) { + bkpr->chainmoves_index = be64_to_cpu(index); + } else + bkpr->chainmoves_index = 0; return NULL; } int main(int argc, char *argv[]) { + struct bkpr *bkpr; setup_locale(); /* No datadir is default */ - datadir = NULL; - db_dsn = NULL; - - plugin_main(argv, init, NULL, PLUGIN_STATIC, true, NULL, + bkpr = tal(NULL, struct bkpr); + plugin_main(argv, init, take(bkpr), PLUGIN_STATIC, true, NULL, commands, ARRAY_SIZE(commands), notifs, ARRAY_SIZE(notifs), NULL, 0, NULL, 0, - plugin_option("bookkeeper-dir", - "string", - "Location for bookkeeper records.", - charp_option, NULL, &datadir), - plugin_option("bookkeeper-db", - "string", - "Location of the bookkeeper database", - charp_option, NULL, &db_dsn), NULL); return 0; diff --git a/plugins/bkpr/bookkeeper.h b/plugins/bkpr/bookkeeper.h new file mode 100644 index 000000000000..5358f2cd1776 --- /dev/null +++ b/plugins/bkpr/bookkeeper.h @@ -0,0 +1,28 @@ +#ifndef LIGHTNING_PLUGINS_BKPR_BOOKKEEPER_H +#define LIGHTNING_PLUGINS_BKPR_BOOKKEEPER_H + +#include "config.h" +#include + +struct command; + +struct bkpr { + /* The datastore-backed lookup tables for our annotations */ + struct accounts *accounts; + struct onchain_fees *onchain_fees; + struct descriptions *descriptions; + struct rebalances *rebalances; + struct blockheights *blockheights; + + /* Where we're up to in listchainmoves, listchannelmoves */ + u64 chainmoves_index, channelmoves_index; +}; + +/* Helper to ignore returns from datastore */ +struct command_result *ignore_datastore_reply(struct command *cmd, + const char *method, + const char *buf, + const jsmntok_t *result, + void *arg); + +#endif /* LIGHTNING_PLUGINS_BKPR_BOOKKEEPER_H */ diff --git a/plugins/bkpr/chain_event.c b/plugins/bkpr/chain_event.c index 7f49a5e79c36..e77f15d418d3 100644 --- a/plugins/bkpr/chain_event.c +++ b/plugins/bkpr/chain_event.c @@ -2,9 +2,13 @@ #include #include +#include -void json_add_chain_event(struct json_stream *out, struct chain_event *ev) +void json_add_chain_event(struct json_stream *out, + const struct bkpr *bkpr, + struct chain_event *ev) { + const char *desc; json_object_start(out, NULL); json_add_string(out, "account", ev->acct_name); if (ev->origin_acct) @@ -15,14 +19,16 @@ void json_add_chain_event(struct json_stream *out, struct chain_event *ev) json_add_amount_msat(out, "debit_msat", ev->debit); json_add_string(out, "currency", chainparams->lightning_hrp); json_add_outpoint(out, "outpoint", &ev->outpoint); + desc = chain_event_description(bkpr, ev); if (ev->spending_txid) json_add_txid(out, "txid", ev->spending_txid); - if (ev->payment_id) + if (ev->payment_id) { json_add_sha256(out, "payment_id", ev->payment_id); + } json_add_u64(out, "timestamp", ev->timestamp); json_add_u32(out, "blockheight", ev->blockheight); - if (ev->desc) - json_add_string(out, "description", ev->desc); + if (desc) + json_add_string(out, "description", desc); json_object_end(out); } diff --git a/plugins/bkpr/chain_event.h b/plugins/bkpr/chain_event.h index e04ba6f4aa1e..0096ffca03e1 100644 --- a/plugins/bkpr/chain_event.h +++ b/plugins/bkpr/chain_event.h @@ -6,6 +6,7 @@ #include struct amount_msat; +struct bkpr; struct bitcoin_outpoint; struct bitcoin_txid; struct json_stream; @@ -15,14 +16,11 @@ struct chain_event { /* Id of this chain event in the database */ u64 db_id; - /* db_id of account this event belongs to */ - u64 acct_db_id; - /* Name of the account this belongs to */ - char *acct_name; + const char *acct_name; /* Name of account this originated from */ - char *origin_acct; + const char *origin_acct; /* Tag describing the event */ const char *tag; @@ -35,8 +33,8 @@ struct chain_event { * confirmation? */ bool splice_close; - /* Is this a rebalance event? */ - bool rebalance; + /* Injected? */ + bool foreign; /* Amount we received in this event */ struct amount_msat credit; @@ -61,12 +59,10 @@ struct chain_event { /* Sometimes chain events resolve payments */ struct sha256 *payment_id; - - /* Desc of event (maybe useful for printing notes) */ - const char *desc; }; void json_add_chain_event(struct json_stream *out, + const struct bkpr *bkpr, struct chain_event *ev); #endif /* LIGHTNING_PLUGINS_BKPR_CHAIN_EVENT_H */ diff --git a/plugins/bkpr/channel_event.c b/plugins/bkpr/channel_event.c index 8a7b3c099438..ded346cfed43 100644 --- a/plugins/bkpr/channel_event.c +++ b/plugins/bkpr/channel_event.c @@ -6,13 +6,15 @@ #include #include #include +#include +#include struct channel_event *new_channel_event(const tal_t *ctx, const char *tag, struct amount_msat credit, struct amount_msat debit, struct amount_msat fees, - struct sha256 *payment_id STEALS, + const struct sha256 *payment_id TAKES, u32 part_id, u64 timestamp) { @@ -22,16 +24,15 @@ struct channel_event *new_channel_event(const tal_t *ctx, ev->credit = credit; ev->debit = debit; ev->fees = fees; - ev->payment_id = tal_steal(ev, payment_id); + ev->payment_id = tal_dup_or_null(ev, struct sha256, payment_id); ev->part_id = part_id; ev->timestamp = timestamp; - ev->desc = NULL; - ev->rebalance_id = NULL; return ev; } void json_add_channel_event(struct json_stream *out, + const struct bkpr *bkpr, struct channel_event *ev) { json_object_start(out, NULL); @@ -44,12 +45,14 @@ void json_add_channel_event(struct json_stream *out, json_add_amount_msat(out, "fees_msat", ev->fees); json_add_string(out, "currency", chainparams->lightning_hrp); if (ev->payment_id) { + const char *desc = channel_event_description(bkpr, ev); json_add_sha256(out, "payment_id", ev->payment_id); json_add_u32(out, "part_id", ev->part_id); + if (desc) + json_add_string(out, "description", desc); } json_add_u64(out, "timestamp", ev->timestamp); - if (ev->desc) - json_add_string(out, "description", ev->desc); - json_add_bool(out, "is_rebalance", ev->rebalance_id != NULL); + json_add_bool(out, "is_rebalance", + find_rebalance(bkpr, ev->db_id) != NULL); json_object_end(out); } diff --git a/plugins/bkpr/channel_event.h b/plugins/bkpr/channel_event.h index c844f82d08c1..08cd357e2ca0 100644 --- a/plugins/bkpr/channel_event.h +++ b/plugins/bkpr/channel_event.h @@ -6,6 +6,7 @@ #include struct amount_msat; +struct bkpr; struct json_stream; struct sha256; @@ -14,11 +15,8 @@ struct channel_event { /* Id of this chain event in the database */ u64 db_id; - /* db_id of account this event belongs to */ - u64 acct_db_id; - /* Name of the account this belongs to */ - char *acct_name; + const char *acct_name; /* Tag describing the event */ const char *tag; @@ -40,12 +38,6 @@ struct channel_event { /* What time did the event happen */ u64 timestamp; - - /* Description, usually from invoice */ - const char *desc; - - /* ID of paired event, iff is a rebalance */ - u64 *rebalance_id; }; struct channel_event *new_channel_event(const tal_t *ctx, @@ -53,11 +45,12 @@ struct channel_event *new_channel_event(const tal_t *ctx, struct amount_msat credit, struct amount_msat debit, struct amount_msat fees, - struct sha256 *payment_id STEALS, + const struct sha256 *payment_id TAKES, u32 part_id, u64 timestamp); void json_add_channel_event(struct json_stream *out, + const struct bkpr *bkpr, struct channel_event *ev); #endif /* LIGHTNING_PLUGINS_BKPR_CHANNEL_EVENT_H */ diff --git a/plugins/bkpr/channelsapy.c b/plugins/bkpr/channelsapy.c index ec4e2de18c36..ed7f85b90e9a 100644 --- a/plugins/bkpr/channelsapy.c +++ b/plugins/bkpr/channelsapy.c @@ -12,6 +12,7 @@ #include #include #include +#include #include #include #include @@ -24,22 +25,14 @@ static int cmp_channel_event_acct(struct channel_event *const *ev1, struct channel_event *const *ev2, void *unused UNUSED) { - if ((*ev1)->acct_db_id < (*ev2)->acct_db_id) - return -1; - else if ((*ev1)->acct_db_id > (*ev2)->acct_db_id) - return 1; - return 0; + return strcmp((*ev1)->acct_name, (*ev2)->acct_name); } static int cmp_acct(struct account *const *a1, struct account *const *a2, void *unused UNUSED) { - if ((*a1)->db_id < (*a2)->db_id) - return -1; - else if ((*a1)->db_id > (*a2)->db_id) - return 1; - return 0; + return strcmp((*a1)->name, (*a2)->name); } struct channel_apy *new_channel_apy(const tal_t *ctx) @@ -93,17 +86,18 @@ bool channel_apy_sum(struct channel_apy *sum_apy, return ok; } -static struct account *search_account(struct account **accts, u64 acct_id) +static struct account *search_account(struct account **accts, const char *acctname) { for (size_t i = 0; i < tal_count(accts); i++) { - if (accts[i]->db_id == acct_id) + if (streq(accts[i]->name, acctname)) return accts[i]; } return NULL; } -static void fillin_apy_acct_details(struct db *db, +static void fillin_apy_acct_details(const struct bkpr *bkpr, + struct command *cmd, const struct account *acct, u32 current_blockheight, struct channel_apy *apy) @@ -114,7 +108,7 @@ static void fillin_apy_acct_details(struct db *db, apy->acct_name = tal_strdup(apy, acct->name); assert(acct->open_event_db_id); - ev = find_chain_event_by_id(acct, db, *acct->open_event_db_id); + ev = find_chain_event_by_id(tmpctx, bkpr, cmd, *acct->open_event_db_id); assert(ev); apy->start_blockheight = ev->blockheight; @@ -123,7 +117,7 @@ static void fillin_apy_acct_details(struct db *db, /* if this account is closed, add closing blockheight */ if (acct->closed_event_db_id) { - ev = find_chain_event_by_id(acct, db, + ev = find_chain_event_by_id(acct, bkpr, cmd, *acct->closed_event_db_id); assert(ev); apy->end_blockheight = ev->blockheight; @@ -146,7 +140,9 @@ static void fillin_apy_acct_details(struct db *db, assert(ok); } -struct channel_apy **compute_channel_apys(const tal_t *ctx, struct db *db, +struct channel_apy **compute_channel_apys(const tal_t *ctx, + const struct bkpr *bkpr, + struct command *cmd, u64 start_time, u64 end_time, u32 current_blockheight) @@ -155,8 +151,8 @@ struct channel_apy **compute_channel_apys(const tal_t *ctx, struct db *db, struct channel_apy *apy, **apys; struct account *acct, **accts; - evs = list_channel_events_timebox(ctx, db, start_time, end_time); - accts = list_accounts(ctx, db); + evs = list_channel_events_timebox(ctx, bkpr, cmd, start_time, end_time); + accts = list_accounts(ctx, bkpr); apys = tal_arr(ctx, struct channel_apy *, 0); @@ -171,16 +167,16 @@ struct channel_apy **compute_channel_apys(const tal_t *ctx, struct db *db, struct channel_event *ev = evs[i]; bool ok; - if (!acct || acct->db_id != ev->acct_db_id) { + if (!acct || !streq(acct->name, ev->acct_name)) { if (acct && is_channel_account(acct->name)) { - fillin_apy_acct_details(db, acct, + fillin_apy_acct_details(bkpr, cmd, acct, current_blockheight, apy); /* Save current apy, make new */ tal_arr_expand(&apys, apy); apy = new_channel_apy(apys); } - acct = search_account(accts, ev->acct_db_id); + acct = search_account(accts, ev->acct_name); assert(acct); } @@ -231,7 +227,7 @@ struct channel_apy **compute_channel_apys(const tal_t *ctx, struct db *db, } if (acct && is_channel_account(acct->name)) { - fillin_apy_acct_details(db, acct, + fillin_apy_acct_details(bkpr, cmd, acct, current_blockheight, apy); /* Save current apy, make new */ diff --git a/plugins/bkpr/channelsapy.h b/plugins/bkpr/channelsapy.h index 1e4ccef2d641..6a63d4e07a48 100644 --- a/plugins/bkpr/channelsapy.h +++ b/plugins/bkpr/channelsapy.h @@ -5,7 +5,7 @@ #include struct channel_apy { - char *acct_name; + const char *acct_name; struct amount_msat routed_in; struct amount_msat routed_out; @@ -33,7 +33,9 @@ struct channel_apy *new_channel_apy(const tal_t *ctx); WARN_UNUSED_RESULT bool channel_apy_sum(struct channel_apy *sum_apy, const struct channel_apy *entry); -struct channel_apy **compute_channel_apys(const tal_t *ctx, struct db *db, +struct channel_apy **compute_channel_apys(const tal_t *ctx, + const struct bkpr *bkpr, + struct command *cmd, u64 start_time, u64 end_time, u32 current_blockheight); diff --git a/plugins/bkpr/db.c b/plugins/bkpr/db.c deleted file mode 100644 index 161ce48979c5..000000000000 --- a/plugins/bkpr/db.c +++ /dev/null @@ -1,245 +0,0 @@ -#include "config.h" -#include -#include -#include -#include -#include -#include -#include -#include - -struct migration { - const char *sql; - void (*func)(struct plugin *p, struct db *db); -}; - -static void migration_remove_dupe_lease_fees(struct plugin *p, struct db *db); -static void migration_maybe_add_chainevents_spliced(struct plugin *p, struct db *db); - -/* Do not reorder or remove elements from this array. - * It is used to migrate existing databases from a prevoius state, based on - * string indices */ -static struct migration db_migrations[] = { - {SQL("CREATE TABLE version (version INTEGER);"), NULL}, - {SQL("INSERT INTO version VALUES (1);"), NULL}, - {SQL("CREATE TABLE vars (" - " name TEXT" - ", val TEXT" - ", intval INTEGER" - ", blobval BLOB" - ", PRIMARY KEY (name)" - ");"), - NULL}, - {SQL("INSERT INTO vars (" - " name" - ", intval" - ") VALUES (" - " 'data_version'" - ", 0" - ");"), - NULL}, - {SQL("CREATE TABLE accounts (" - " id BIGSERIAL" - ", name TEXT" - ", peer_id BLOB" - ", opened_event_id BIGINT" - ", closed_event_id BIGINT" - ", onchain_resolved_block INTEGER" - ", is_wallet INTEGER" - ", we_opened INTEGER" - ", leased INTEGER" - ", PRIMARY KEY (id)" - ");"), - NULL}, - {SQL("CREATE TABLE chain_events (" - " id BIGSERIAL" - ", account_id BIGINT REFERENCES accounts(id)" - ", tag TEXT" - ", credit BIGINT" - ", debit BIGINT" - ", output_value BIGINT" - ", currency TEXT" - ", timestamp BIGINT" - ", blockheight INTEGER" - ", utxo_txid BLOB" - ", outnum INTEGER" - ", payment_id BLOB" - ", spending_txid BLOB" - ", PRIMARY KEY (id)" - ");"), - NULL}, - {SQL("CREATE TABLE channel_events (" - " id BIGSERIAL" - ", account_id BIGINT REFERENCES accounts(id)" - ", tag TEXT" - ", credit BIGINT" - ", debit BIGINT" - ", fees BIGINT" - ", currency TEXT" - ", payment_id BLOB" - ", part_id INTEGER" - ", timestamp BIGINT" - ", PRIMARY KEY (id)" - ");"), - NULL}, - {SQL("CREATE TABLE onchain_fees (" - "account_id BIGINT REFERENCES accounts(id)" - ", txid BLOB" - ", credit BIGINT" - ", debit BIGINT" - ", currency TEXT" - ", timestamp BIGINT" - ", update_count INT" - ", PRIMARY KEY (account_id, txid, update_count)" - ");"), - NULL}, - {SQL("ALTER TABLE chain_events ADD origin TEXT;"), NULL}, - {SQL("ALTER TABLE accounts ADD closed_count INTEGER DEFAULT 0;"), NULL}, - {SQL("ALTER TABLE chain_events ADD ignored INTEGER;"), NULL}, - {SQL("ALTER TABLE chain_events ADD stealable INTEGER;"), NULL}, - {SQL("ALTER TABLE chain_events ADD ev_desc TEXT DEFAULT NULL;"), NULL}, - {SQL("ALTER TABLE channel_events ADD ev_desc TEXT DEFAULT NULL;"), NULL}, - {SQL("ALTER TABLE channel_events ADD rebalance_id BIGINT DEFAULT NULL;"), NULL}, - {SQL("ALTER TABLE chain_events ADD spliced INTEGER DEFAULT 0;"), NULL}, - {NULL, migration_remove_dupe_lease_fees}, - {NULL, migration_maybe_add_chainevents_spliced}, - /* We used to send anchors to the wallet, but set ignored tag. Now we send - * them to external. */ - {SQL("UPDATE chain_events" - " SET account_id = (SELECT id FROM accounts WHERE name = 'external')" - " WHERE account_id = (SELECT id FROM accounts WHERE name = 'wallet')" - " AND ignored = 1"), NULL}, -}; - -static bool db_migrate(struct plugin *p, struct db *db) -{ - /* Read current version from database */ - int current, orig, available; - struct db_stmt *stmt; - - orig = current = db_get_version(db); - available = ARRAY_SIZE(db_migrations) - 1; - - if (current == -1) { - plugin_log(p, LOG_INFORM, "Creating database"); - } else if (available < current) - plugin_err(p, - "Refusing to migrate down from version %u to %u", - current, available); - else if (current != available) - plugin_log(p, LOG_INFORM, - "Updating database from version %u to %u", - current, available); - - while (current < available) { - current++; - if (db_migrations[current].sql) { - stmt = db_prepare_v2(db, db_migrations[current].sql); - db_exec_prepared_v2(take(stmt)); - } - if (db_migrations[current].func) - db_migrations[current].func(p, db); - } - - /* Finally, update the version number in the version table */ - stmt = db_prepare_v2(db, SQL("UPDATE version SET version=?;")); - db_bind_int(stmt, available); - db_exec_prepared_v2(take(stmt)); - - return current != orig; -} - -static void migration_remove_dupe_lease_fees(struct plugin *p, struct db *db) -{ - struct db_stmt *stmt, *del_stmt; - u64 *last_acct_id; - - stmt = db_prepare_v2(db, SQL("SELECT" - " id" - ", account_id" - " FROM channel_events" - " WHERE tag = 'lease_fee'" - " ORDER BY account_id")); - db_query_prepared(stmt); - last_acct_id = NULL; - while (db_step(stmt)) { - u64 id, acct_id; - id = db_col_u64(stmt, "id"); - acct_id = db_col_u64(stmt, "account_id"); - - if (!last_acct_id) { - last_acct_id = tal(stmt, u64); - *last_acct_id = acct_id; - continue; - } - - if (*last_acct_id != acct_id) { - *last_acct_id = acct_id; - continue; - } - - plugin_log(p, LOG_INFORM, - "Duplicate 'lease_fee' found for" - " account %"PRIu64", deleting dupe", - id); - - /* same acct as last, we found a duplicate */ - del_stmt = db_prepare_v2(db, SQL("DELETE FROM channel_events" - " WHERE id=?")); - db_bind_u64(del_stmt, id); - db_exec_prepared_v2(take(del_stmt)); - } - tal_free(stmt); -} - -/* OK, funny story. We added the "ALTER TABLE chain_events ADD spliced INTEGER DEFAULT 0;" - * migration in the wrong place, NOT at the end. So if you are migrating from an old version, - * "migration_remove_dupe_lease_fees" ran (again), which is harmless, but this migration - * never got added. */ -static void migration_maybe_add_chainevents_spliced(struct plugin *p, struct db *db) -{ - struct db_stmt *stmt; - bool col_exists; - - stmt = db_prepare_v2(db, SQL("SELECT spliced FROM chain_events")); - col_exists = db_query_prepared_canfail(stmt); - tal_free(stmt); - if (col_exists) - return; - - plugin_log(p, LOG_INFORM, - "Database fixup: adding spliced column to chain_events table"); - stmt = db_prepare_v2(db, SQL("ALTER TABLE chain_events ADD spliced INTEGER DEFAULT 0;")); - db_exec_prepared_v2(take(stmt)); -} - -static void db_error(struct plugin *plugin, bool fatal, const char *fmt, va_list ap) -{ - if (fatal) - plugin_errv(plugin, fmt, ap); - else - plugin_logv(plugin, LOG_BROKEN, fmt, ap); -} - -struct db *db_setup(const tal_t *ctx, struct plugin *p, - const char *db_dsn) -{ - bool migrated; - struct db *db; - - db = db_open(ctx, db_dsn, plugin_developer_mode(p), db_error, p); - db->report_changes_fn = NULL; - - db_begin_transaction(db); - migrated = db_migrate(p, db); - db->data_version = db_data_version_get(db); - db_commit_transaction(db); - - /* This needs to be done outside a transaction, apparently. - * It's a good idea to do this every so often, and on db - * upgrade is a reasonable time. */ - if (migrated && !db->config->vacuum_fn(db)) - db_fatal(db, "Error vacuuming db: %s", db->error); - - return db; -} diff --git a/plugins/bkpr/db.h b/plugins/bkpr/db.h deleted file mode 100644 index 71af1ef6ba49..000000000000 --- a/plugins/bkpr/db.h +++ /dev/null @@ -1,11 +0,0 @@ -#ifndef LIGHTNING_PLUGINS_BKPR_DB_H -#define LIGHTNING_PLUGINS_BKPR_DB_H -#include "config.h" -#include - -struct plugin; -struct db; - -struct db *db_setup(const tal_t *ctx, struct plugin *p, const char *db_dsn); - -#endif /* LIGHTNING_PLUGINS_BKPR_DB_H */ diff --git a/plugins/bkpr/descriptions.c b/plugins/bkpr/descriptions.c new file mode 100644 index 000000000000..fd59dab57758 --- /dev/null +++ b/plugins/bkpr/descriptions.c @@ -0,0 +1,323 @@ +#include "config.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* We keep two hashes; one for UTXO descriptions, one for payment_hashes */ +struct utxo_desc { + struct bitcoin_outpoint outp; + const char *desc; +}; + +struct payment_hash_desc { + struct sha256 payment_hash; + const char *desc; +}; + +static size_t hash_utxo(const struct bitcoin_outpoint *outp) +{ + return siphash24(siphash_seed(), outp->txid.shad.sha.u.u8, + sizeof(outp->txid.shad.sha.u.u8)) + outp->n; +} + +static const struct bitcoin_outpoint *utxo_key(const struct utxo_desc *utxodesc) +{ + return &utxodesc->outp; +} + +static bool utxo_key_eq(const struct utxo_desc *utxodesc, + const struct bitcoin_outpoint *outp) +{ + return bitcoin_outpoint_eq(&utxodesc->outp, outp); +} + +HTABLE_DEFINE_NODUPS_TYPE(struct utxo_desc, + utxo_key, + hash_utxo, + utxo_key_eq, + utxo_desc_htable); + +static size_t hash_payment_hash(const struct sha256 *h) +{ + return siphash24(siphash_seed(), h->u.u8, sizeof(h->u.u8)); +} + +static const struct sha256 *payment_hash_key(const struct payment_hash_desc *phd) +{ + return &phd->payment_hash; +} + +static bool payment_hash_key_eq(const struct payment_hash_desc *phd, + const struct sha256 *h) +{ + return sha256_eq(&phd->payment_hash, h); +} + +HTABLE_DEFINE_NODUPS_TYPE(struct payment_hash_desc, + payment_hash_key, + hash_payment_hash, + payment_hash_key_eq, + payment_hash_desc_htable); + + +/* We keep descriptions in memory and datastore */ +struct descriptions { + struct utxo_desc_htable *by_utxo; + struct payment_hash_desc_htable *by_payment_hash; +}; + +static const char *ds_desc_utxo_path(const tal_t *ctx, + const struct bitcoin_outpoint *outp) +{ + return tal_fmt(ctx, "bookkeeper/description/utxo/%s", fmt_bitcoin_outpoint(tmpctx, outp)); +} + +static void utxo_desc_datastore_update(struct command *cmd, + const struct utxo_desc *utxo_desc) +{ + const char *path = ds_desc_utxo_path(tmpctx, &utxo_desc->outp); + + jsonrpc_set_datastore_binary(cmd, path, + utxo_desc->desc, strlen(utxo_desc->desc), + "create-or-replace", + ignore_datastore_reply, NULL, NULL); +} + +static const char *ds_desc_payment_hash_path(const tal_t *ctx, + const struct sha256 *payment_hash) +{ + return tal_fmt(ctx, "bookkeeper/description/payment/%s", + fmt_sha256(tmpctx, payment_hash)); +} + +static void payment_hash_desc_datastore_update(struct command *cmd, + const struct payment_hash_desc *phd) +{ + const char *path = ds_desc_payment_hash_path(tmpctx, &phd->payment_hash); + jsonrpc_set_datastore_binary(cmd, path, phd->desc, strlen(phd->desc), + "create-or-replace", + ignore_datastore_reply, NULL, NULL); +} + +static struct payment_hash_desc * +new_payment_hash_description(struct descriptions *descriptions, + const struct sha256 *payment_hash, + const char *desc TAKES) +{ + struct payment_hash_desc *phd; + + phd = payment_hash_desc_htable_get(descriptions->by_payment_hash, payment_hash); + if (phd) { + tal_free(phd->desc); + phd->desc = tal_strdup(phd, desc); + } else { + phd = tal(descriptions->by_payment_hash, struct payment_hash_desc); + phd->payment_hash = *payment_hash; + phd->desc = tal_strdup(phd, desc); + payment_hash_desc_htable_add(descriptions->by_payment_hash, phd); + } + return phd; +} + +void add_payment_hash_description(struct command *cmd, + struct bkpr *bkpr, + const struct sha256 *payment_hash, + const char *desc TAKES) +{ + struct payment_hash_desc *phd; + + phd = new_payment_hash_description(bkpr->descriptions, + payment_hash, desc); + payment_hash_desc_datastore_update(cmd, phd); +} + +static struct utxo_desc * +new_utxo_description(struct descriptions *descriptions, + const struct bitcoin_outpoint *outpoint, + const char *desc TAKES) +{ + struct utxo_desc *ud; + + ud = utxo_desc_htable_get(descriptions->by_utxo, outpoint); + if (ud) { + tal_free(ud->desc); + ud->desc = tal_strdup(ud, desc); + } else { + ud = tal(descriptions->by_utxo, struct utxo_desc); + ud->outp = *outpoint; + ud->desc = tal_strdup(ud, desc); + utxo_desc_htable_add(descriptions->by_utxo, ud); + } + return ud; +} + +void add_utxo_description(struct command *cmd, + struct bkpr *bkpr, + const struct bitcoin_outpoint *outpoint, + const char *desc TAKES) +{ + struct utxo_desc *ud; + + ud = new_utxo_description(bkpr->descriptions, outpoint, desc); + utxo_desc_datastore_update(cmd, ud); +} + +static void memleak_scan_utxo_desc_htable(struct htable *memtable, + struct utxo_desc_htable *ht) +{ + memleak_scan_htable(memtable, &ht->raw); +} + +static void memleak_scan_payment_hash_desc_htable(struct htable *memtable, + struct payment_hash_desc_htable *ht) +{ + memleak_scan_htable(memtable, &ht->raw); +} + +/* To avoid JSON encoding/decoding, we use hex encoding directly */ +static char *json_cstr_from_hex(const tal_t *ctx, const char *buffer, const jsmntok_t *tok) +{ + char *result; + size_t hexlen, rawlen; + hexlen = tok->end - tok->start; + rawlen = hex_data_size(hexlen); + + result = tal_arr(ctx, char, rawlen + 1); + if (!hex_decode(buffer + tok->start, hexlen, result, rawlen)) + return tal_free(result); + + result[rawlen] = '\0'; + return result; +} + +struct descriptions *init_descriptions(const tal_t *ctx, + struct command *init_cmd) +{ + struct descriptions *descriptions = tal(ctx, struct descriptions); + struct json_out *params; + const jsmntok_t *result, *datastore, *t; + size_t i; + const char *buf; + + descriptions->by_utxo = tal(descriptions, struct utxo_desc_htable); + utxo_desc_htable_init(descriptions->by_utxo); + memleak_add_helper(descriptions->by_utxo, memleak_scan_utxo_desc_htable); + + descriptions->by_payment_hash = tal(descriptions, struct payment_hash_desc_htable); + payment_hash_desc_htable_init(descriptions->by_payment_hash); + memleak_add_helper(descriptions->by_payment_hash, memleak_scan_payment_hash_desc_htable); + + /* Load everything under "bookkeeper/description/utxo" */ + params = json_out_new(tmpctx); + json_out_start(params, NULL, '{'); + json_out_start(params, "key", '['); + json_out_addstr(params, NULL, "bookkeeper"); + json_out_addstr(params, NULL, "description"); + json_out_addstr(params, NULL, "utxo"); + json_out_end(params, ']'); + json_out_end(params, '}'); + + result = jsonrpc_request_sync(tmpctx, init_cmd, + "listdatastore", + params, &buf); + + datastore = json_get_member(buf, result, "datastore"); + json_for_each_arr(i, t, datastore) { + const jsmntok_t *keytok, *hextok; + const char *desc_str; + struct bitcoin_outpoint outp; + + keytok = json_get_member(buf, t, "key"); + if (keytok->size != 4) + continue; + + /* ["bookkeeper", "description", "utxo", ] */ + hextok = json_get_member(buf, t, "hex"); + desc_str = json_cstr_from_hex(tmpctx, buf, hextok); + if (!json_to_outpoint(buf, keytok + 4, &outp)) { + plugin_log(init_cmd->plugin, LOG_BROKEN, + "Invalid outpoint desc for %.*s", + json_tok_full_len(keytok), + json_tok_full(buf, keytok)); + continue; + } + new_utxo_description(descriptions, &outp, take(desc_str)); + } + + /* Load everything under "bookkeeper/description/payment" */ + params = json_out_new(tmpctx); + json_out_start(params, NULL, '{'); + json_out_start(params, "key", '['); + json_out_addstr(params, NULL, "bookkeeper"); + json_out_addstr(params, NULL, "description"); + json_out_addstr(params, NULL, "payment"); + json_out_end(params, ']'); + json_out_end(params, '}'); + + result = jsonrpc_request_sync(tmpctx, init_cmd, + "listdatastore", + params, &buf); + + datastore = json_get_member(buf, result, "datastore"); + json_for_each_arr(i, t, datastore) { + const jsmntok_t *keytok, *hextok; + const char *desc_str; + struct sha256 ph; + + keytok = json_get_member(buf, t, "key"); + if (keytok->size != 4) + continue; + + /* ["bookkeeper", "description", "payment", ] */ + hextok = json_get_member(buf, t, "hex"); + desc_str = json_cstr_from_hex(tmpctx, buf, hextok); + if (!json_to_sha256(buf, keytok + 4, &ph)) { + plugin_log(init_cmd->plugin, LOG_BROKEN, + "Invalid payment hash desc for %.*s", + json_tok_full_len(keytok), + json_tok_full(buf, keytok)); + continue; + } + new_payment_hash_description(descriptions, &ph, take(desc_str)); + } + + return descriptions; +} + +const char *chain_event_description(const struct bkpr *bkpr, + const struct chain_event *ce) +{ + const struct utxo_desc *ud; + + /* We only put descriptions on the *credit* events */ + if (amount_msat_is_zero(ce->credit)) + return NULL; + + ud = utxo_desc_htable_get(bkpr->descriptions->by_utxo, &ce->outpoint); + return ud ? ud->desc : NULL; +} + +const char *channel_event_description(const struct bkpr *bkpr, + const struct channel_event *ce) +{ + const struct payment_hash_desc *phd; + + if (!ce->payment_id) + return NULL; + + phd = payment_hash_desc_htable_get(bkpr->descriptions->by_payment_hash, + ce->payment_id); + return phd ? phd->desc : NULL; +} diff --git a/plugins/bkpr/descriptions.h b/plugins/bkpr/descriptions.h new file mode 100644 index 000000000000..4e7f957e5d13 --- /dev/null +++ b/plugins/bkpr/descriptions.h @@ -0,0 +1,30 @@ +#ifndef LIGHTNING_PLUGINS_BKPR_DESCRIPTIONS_H +#define LIGHTNING_PLUGINS_BKPR_DESCRIPTIONS_H +#include "config.h" + +struct command; +struct bkpr; +struct sha256; +struct bitcoin_outpoint; +struct chain_event; +struct channel_event; + +void add_payment_hash_description(struct command *cmd, + struct bkpr *bkpr, + const struct sha256 *payment_hash, + const char *desc); + +void add_utxo_description(struct command *cmd, + struct bkpr *bkpr, + const struct bitcoin_outpoint *outpoint, + const char *desc); + +const char *chain_event_description(const struct bkpr *bkpr, + const struct chain_event *ce); + +const char *channel_event_description(const struct bkpr *bkpr, + const struct channel_event *ce); + +struct descriptions *init_descriptions(const tal_t *ctx, + struct command *init_cmd); +#endif /* LIGHTNING_PLUGINS_BKPR_DESCRIPTIONS_H */ diff --git a/plugins/bkpr/incomestmt.c b/plugins/bkpr/incomestmt.c index 999088689452..fd3a7e452c08 100644 --- a/plugins/bkpr/incomestmt.c +++ b/plugins/bkpr/incomestmt.c @@ -1,39 +1,32 @@ #include "config.h" +#include #include #include +#include #include #include #include #include -#include -#include -#include -#include #include #include #include +#include #include #include +#include #include #include +#include #include +#include #include #define ONCHAIN_FEE "onchain_fee" -static struct account *get_account(struct account **accts, - u64 acct_db_id) -{ - for (size_t i = 0; i < tal_count(accts); i++) { - if (accts[i]->db_id == acct_db_id) - return accts[i]; - } - return NULL; -} - static struct income_event *chain_to_income(const tal_t *ctx, + const struct bkpr *bkpr, struct chain_event *ev, - char *acct_to_attribute, + const char *acct_to_attribute, struct amount_msat credit, struct amount_msat debit) { @@ -46,7 +39,7 @@ static struct income_event *chain_to_income(const tal_t *ctx, inc->fees = AMOUNT_MSAT(0); inc->timestamp = ev->timestamp; inc->outpoint = tal_dup(inc, struct bitcoin_outpoint, &ev->outpoint); - inc->desc = tal_strdup_or_null(inc, ev->desc); + inc->desc = tal_strdup_or_null(inc, chain_event_description(bkpr, ev)); inc->txid = tal_dup_or_null(inc, struct bitcoin_txid, ev->spending_txid); inc->payment_id = tal_dup_or_null(inc, struct sha256, ev->payment_id); @@ -54,6 +47,7 @@ static struct income_event *chain_to_income(const tal_t *ctx, } static struct income_event *channel_to_income(const tal_t *ctx, + const struct bkpr *bkpr, struct channel_event *ev, struct amount_msat credit, struct amount_msat debit) @@ -68,7 +62,7 @@ static struct income_event *channel_to_income(const tal_t *ctx, inc->timestamp = ev->timestamp; inc->outpoint = NULL; inc->txid = NULL; - inc->desc = tal_strdup_or_null(inc, ev->desc); + inc->desc = tal_strdup_or_null(inc, channel_event_description(bkpr, ev)); inc->payment_id = tal_dup_or_null(inc, struct sha256, ev->payment_id); return inc; @@ -98,7 +92,7 @@ static struct income_event *onchainfee_to_income(const tal_t *ctx, * by wrapping the desc in double-quotes ("). But what if * there's already double-quotes? Well we swap these to * single-quotes (') and then use the json_escape function */ -static char *csv_safe_str(const tal_t *ctx, char *input TAKES) +static char *csv_safe_str(const tal_t *ctx, const char *input TAKES) { struct json_escape *esc; char *dupe; @@ -115,19 +109,20 @@ static char *csv_safe_str(const tal_t *ctx, char *input TAKES) } static struct income_event *maybe_chain_income(const tal_t *ctx, - struct db *db, + const struct bkpr *bkpr, + struct command *cmd, struct account *acct, struct chain_event *ev) { if (streq(ev->tag, "htlc_fulfill")) { if (is_external_account(ev->acct_name)) /* Swap the credit/debit as it went to external */ - return chain_to_income(ctx, ev, + return chain_to_income(ctx, bkpr, ev, ev->origin_acct, ev->debit, ev->credit); /* Normal credit/debit as it originated from external */ - return chain_to_income(ctx, ev, + return chain_to_income(ctx, bkpr, ev, ev->acct_name, ev->credit, ev->debit); } @@ -136,7 +131,7 @@ static struct income_event *maybe_chain_income(const tal_t *ctx, if (streq(ev->tag, "anchor")) { if (acct->we_opened) /* for now, we count all anchors as expenses */ - return chain_to_income(ctx, ev, + return chain_to_income(ctx, bkpr, ev, ev->acct_name, ev->debit, ev->credit); @@ -146,7 +141,8 @@ static struct income_event *maybe_chain_income(const tal_t *ctx, /* income */ if (streq(ev->tag, "deposit")) { - struct db_stmt *stmt; + const jsmntok_t *toks; + const char *buf; /* deposit to external is cost to us */ if (is_external_account(ev->acct_name)) { @@ -157,7 +153,7 @@ static struct income_event *maybe_chain_income(const tal_t *ctx, if (ev->blockheight == 0) return NULL; - iev = chain_to_income(ctx, ev, + iev = chain_to_income(ctx, bkpr, ev, ev->origin_acct, ev->debit, ev->credit); @@ -173,28 +169,23 @@ static struct income_event *maybe_chain_income(const tal_t *ctx, * into a tx that included funds from a 3rd party * coming to us... eg. a splice out from the peer * to our onchain wallet */ - stmt = db_prepare_v2(db, SQL("SELECT" - " 1" - " FROM chain_events e" - " LEFT OUTER JOIN accounts a" - " ON e.account_id = a.id" - " WHERE " - " e.spending_txid = ?")); - - db_bind_txid(stmt, &ev->outpoint.txid); - db_query_prepared(stmt); - if (!db_step(stmt)) { - tal_free(stmt); + toks = sql_req(tmpctx, cmd, &buf, + "SELECT" + " 1" + " FROM chainmoves" + " WHERE " + " spending_txid = X'%s'" + " AND created_index <= %"PRIu64, + fmt_bitcoin_txid(tmpctx, &ev->outpoint.txid), + bkpr->chainmoves_index); + if (json_get_member(buf, toks, "rows")->size == 0) { /* no matching withdrawal from internal, * so must be new deposit (external) */ - return chain_to_income(ctx, ev, + return chain_to_income(ctx, bkpr, ev, ev->acct_name, ev->credit, ev->debit); } - - db_col_ignore(stmt, "1"); - tal_free(stmt); return NULL; } @@ -202,26 +193,29 @@ static struct income_event *maybe_chain_income(const tal_t *ctx, } static struct income_event *paid_invoice_fee(const tal_t *ctx, + const struct bkpr *bkpr, struct channel_event *ev) { struct income_event *iev; - iev = channel_to_income(ctx, ev, AMOUNT_MSAT(0), ev->fees); + iev = channel_to_income(ctx, bkpr, ev, AMOUNT_MSAT(0), ev->fees); iev->tag = tal_free(ev->tag); iev->tag = (char *)account_entry_tag_str(INVOICEFEE); return iev; } static struct income_event *rebalance_fee(const tal_t *ctx, + const struct bkpr *bkpr, struct channel_event *ev) { struct income_event *iev; - iev = channel_to_income(ctx, ev, AMOUNT_MSAT(0), ev->fees); + iev = channel_to_income(ctx, bkpr, ev, AMOUNT_MSAT(0), ev->fees); iev->tag = tal_free(ev->tag); iev->tag = (char *)account_entry_tag_str(REBALANCEFEE); return iev; } static struct income_event *maybe_channel_income(const tal_t *ctx, + const struct bkpr *bkpr, struct channel_event *ev) { if (amount_msat_is_zero(ev->credit) @@ -231,7 +225,7 @@ static struct income_event *maybe_channel_income(const tal_t *ctx, /* We record a +/- penalty adj, but we only count the credit */ if (streq(ev->tag, "penalty_adj")) { if (!amount_msat_is_zero(ev->credit)) - return channel_to_income(ctx, ev, + return channel_to_income(ctx, bkpr, ev, ev->credit, ev->debit); return NULL; @@ -239,7 +233,7 @@ static struct income_event *maybe_channel_income(const tal_t *ctx, if (streq(ev->tag, "invoice")) { /* Skip events for rebalances */ - if (ev->rebalance_id) + if (find_rebalance(bkpr, ev->db_id)) return NULL; /* If it's a payment, we note fees separately */ @@ -248,12 +242,12 @@ static struct income_event *maybe_channel_income(const tal_t *ctx, bool ok; ok = amount_msat_sub(&paid, ev->debit, ev->fees); assert(ok); - return channel_to_income(ctx, ev, + return channel_to_income(ctx, bkpr, ev, ev->credit, paid); } - return channel_to_income(ctx, ev, + return channel_to_income(ctx, bkpr, ev, ev->credit, ev->debit); } @@ -262,7 +256,7 @@ static struct income_event *maybe_channel_income(const tal_t *ctx, * debiting side -- the side the $$ was made on! */ if (streq(ev->tag, "routed")) { if (!amount_msat_is_zero(ev->debit)) - return channel_to_income(ctx, ev, + return channel_to_income(ctx, bkpr, ev, ev->fees, AMOUNT_MSAT(0)); return NULL; @@ -270,11 +264,11 @@ static struct income_event *maybe_channel_income(const tal_t *ctx, /* For everything else, it's straight forward */ /* (lease_fee, pushed, journal_entry) */ - return channel_to_income(ctx, ev, ev->credit, ev->debit); + return channel_to_income(ctx, bkpr, ev, ev->credit, ev->debit); } static struct onchain_fee **find_consolidated_fees(const tal_t *ctx, - struct db *db, + const struct bkpr *bkpr, u64 start_time, u64 end_time) { @@ -282,7 +276,7 @@ static struct onchain_fee **find_consolidated_fees(const tal_t *ctx, struct onchain_fee **fee_sums = tal_arr(ctx, struct onchain_fee *, 0); - sums = calculate_onchain_fee_sums(ctx, db); + sums = calculate_onchain_fee_sums(ctx, bkpr); for (size_t i = 0; i < tal_count(sums); i++) { /* Find the last matching feerate's data */ @@ -298,7 +292,7 @@ static struct onchain_fee **find_consolidated_fees(const tal_t *ctx, fee->txid = *sums[i]->txid; fee->timestamp = - onchain_fee_last_timestamp(db, sums[i]->acct_db_id, + onchain_fee_last_timestamp(bkpr, sums[i]->acct_name, sums[i]->txid); tal_arr_expand(&fee_sums, fee); @@ -309,7 +303,8 @@ static struct onchain_fee **find_consolidated_fees(const tal_t *ctx, } struct income_event **list_income_events(const tal_t *ctx, - struct db *db, + const struct bkpr *bkpr, + struct command *cmd, u64 start_time, u64 end_time, bool consolidate_fees) @@ -317,21 +312,18 @@ struct income_event **list_income_events(const tal_t *ctx, struct channel_event **channel_events; struct chain_event **chain_events; struct onchain_fee **onchain_fees; - struct account **accts; - struct income_event **evs; - channel_events = list_channel_events_timebox(ctx, db, + channel_events = list_channel_events_timebox(ctx, bkpr, cmd, start_time, end_time); - chain_events = list_chain_events_timebox(ctx, db, start_time, end_time); - accts = list_accounts(ctx, db); + chain_events = list_chain_events_timebox(ctx, bkpr, cmd, start_time, end_time); if (consolidate_fees) { - onchain_fees = find_consolidated_fees(ctx, db, + onchain_fees = find_consolidated_fees(ctx, bkpr, start_time, end_time); } else - onchain_fees = list_chain_fees_timebox(ctx, db, + onchain_fees = list_chain_fees_timebox(ctx, bkpr, start_time, end_time); evs = tal_arr(ctx, struct income_event *, 0); @@ -374,9 +366,9 @@ struct income_event **list_income_events(const tal_t *ctx, if (chain && chain->timestamp == lowest) { struct income_event *ev; struct account *acct = - get_account(accts, chain->acct_db_id); + find_account(bkpr, chain->acct_name); - ev = maybe_chain_income(evs, db, acct, chain); + ev = maybe_chain_income(evs, bkpr, cmd, acct, chain); if (ev) tal_arr_expand(&evs, ev); i++; @@ -385,7 +377,7 @@ struct income_event **list_income_events(const tal_t *ctx, if (chan && chan->timestamp == lowest) { struct income_event *ev; - ev = maybe_channel_income(evs, chan); + ev = maybe_channel_income(evs, bkpr, chan); if (ev) tal_arr_expand(&evs, ev); @@ -393,10 +385,10 @@ struct income_event **list_income_events(const tal_t *ctx, if (streq(chan->tag, "invoice") && !amount_msat_is_zero(chan->debit) && !amount_msat_is_zero(chan->fees)) { - if (!chan->rebalance_id) - ev = paid_invoice_fee(evs, chan); + if (!find_rebalance(bkpr, chan->db_id)) + ev = paid_invoice_fee(evs, bkpr, chan); else - ev = rebalance_fee(evs, chan); + ev = rebalance_fee(evs, bkpr, chan); tal_arr_expand(&evs, ev); } @@ -412,10 +404,12 @@ struct income_event **list_income_events(const tal_t *ctx, return evs; } -struct income_event **list_income_events_all(const tal_t *ctx, struct db *db, +struct income_event **list_income_events_all(const tal_t *ctx, + const struct bkpr *bkpr, + struct command *cmd, bool consolidate_fees) { - return list_income_events(ctx, db, 0, SQLITE_MAX_UINT, + return list_income_events(ctx, bkpr, cmd, 0, SQLITE_MAX_UINT, consolidate_fees); } diff --git a/plugins/bkpr/incomestmt.h b/plugins/bkpr/incomestmt.h index b381598f961b..0de3c00dc6e1 100644 --- a/plugins/bkpr/incomestmt.h +++ b/plugins/bkpr/incomestmt.h @@ -6,9 +6,9 @@ #include struct income_event { - char *acct_name; - char *tag; - char *desc; + const char *acct_name; + const char *tag; + const char *desc; struct amount_msat credit; struct amount_msat debit; /* Some CSVs require us to put fees on the @@ -23,19 +23,22 @@ struct income_event { /* Each csv format has a header and a 'row print' function */ struct csv_fmt { - char *fmt_name; + const char *fmt_name; void (*emit_header)(FILE *); void (*emit_entry)(const tal_t *, FILE *, struct income_event *); }; /* List all the events that are income related (gain/loss) */ -struct income_event **list_income_events_all(const tal_t *ctx, struct db *db, +struct income_event **list_income_events_all(const tal_t *ctx, + const struct bkpr *bkpr, + struct command *cmd, bool consolidate_fees); /* List all the events that are income related (gain/loss), * by a start and end date */ struct income_event **list_income_events(const tal_t *ctx, - struct db *db, + const struct bkpr *bkpr, + struct command *cmd, u64 start_time, u64 end_time, bool consolidate_fees); diff --git a/plugins/bkpr/onchain_fee.c b/plugins/bkpr/onchain_fee.c index 08afe69a40cc..4af85e259e0c 100644 --- a/plugins/bkpr/onchain_fee.c +++ b/plugins/bkpr/onchain_fee.c @@ -1,11 +1,197 @@ #include "config.h" #include +#include +#include +#include +#include +#include +#include #include +#include +#include +#include +#include +#include +#include +#include #include +#include +#include +#include +/* We keep a hash of onchain_fee arrays. Each array is sorted. */ + +/* Sort these as ORDER BY timestamp, account_name, txid, update_count */ +static int compare_onchain_fee(struct onchain_fee *const *a, + struct onchain_fee *const *b, + void *arg) +{ + const struct onchain_fee *ofa = *a, *ofb = *b; + int cmp; + + if (ofa->timestamp < ofb->timestamp) + return -1; + if (ofa->timestamp > ofb->timestamp) + return 1; + cmp = strcmp(ofa->acct_name, ofb->acct_name); + if (cmp) + return cmp; + cmp = memcmp(&ofa->txid, &ofb->txid, sizeof(ofb->txid)); + if (cmp) + return cmp; + if (ofa->update_count < ofb->update_count) + return -1; + if (ofa->update_count > ofb->update_count) + return 1; + return 0; +} + +static void order_fees(struct onchain_fee **ofs) +{ + asort(ofs, tal_count(ofs), compare_onchain_fee, NULL); +} + +/* Convenience struct: array is always in compare_onchain_fee order! */ +struct ordered_ofees { + struct onchain_fee **ofs; +}; + +static size_t hash_acctname(const char *str) +{ + return siphash24(siphash_seed(), str, strlen(str)); +} + +static const char *onchain_fees_keyof(const struct ordered_ofees *ofees) +{ + return ofees->ofs[0]->acct_name; +} + +static bool onchain_account_eq(const struct ordered_ofees *ofees, + const char *acct_name) +{ + return streq(ofees->ofs[0]->acct_name, acct_name); +} + +HTABLE_DEFINE_NODUPS_TYPE(struct ordered_ofees, + onchain_fees_keyof, + hash_acctname, + onchain_account_eq, + ofees_hash); + +struct onchain_fees { + /* Hash table by account. */ + struct ofees_hash *by_account; +}; + +static void destroy_onchain_fee(struct onchain_fee *of, + struct ofees_hash *ofees_hash) +{ + struct ordered_ofees *ofees = ofees_hash_get(ofees_hash, + of->acct_name); + /* Only one in array? Must be this. */ + if (tal_count(ofees->ofs) == 1) { + ofees_hash_del(ofees_hash, ofees); + assert(ofees->ofs[0] == of); + tal_free(ofees); + return; + } + for (size_t i = 0; i < tal_count(ofees->ofs); i++) { + if (ofees->ofs[i] == of) { + tal_arr_remove(&ofees->ofs, i); + return; + } + } + abort(); +} + +static struct onchain_fee *new_onchain_fee(struct onchain_fees *onchain_fees, + const char *acct_name TAKES, + const struct bitcoin_txid *txid, + struct amount_msat credit, + struct amount_msat debit, + u64 timestamp, + u32 update_count) +{ + struct onchain_fee *of = tal(NULL, struct onchain_fee); + struct ordered_ofees *ofees; + + of->acct_name = tal_strdup(of, acct_name); + of->txid = *txid; + of->credit = credit; + of->debit = debit; + of->timestamp = timestamp; + of->update_count = update_count; + + /* Add to sorted array in hash table */ + ofees = ofees_hash_get(onchain_fees->by_account, of->acct_name); + if (ofees) { + tal_arr_expand(&ofees->ofs, of); + order_fees(ofees->ofs); + } else { + ofees = tal(onchain_fees->by_account, struct ordered_ofees); + ofees->ofs = tal_arr(ofees, struct onchain_fee *, 1); + ofees->ofs[0] = of; + ofees_hash_add(onchain_fees->by_account, ofees); + } + tal_steal(ofees->ofs, of); + + tal_add_destructor2(of, destroy_onchain_fee, onchain_fees->by_account); + return of; +} + +static void towire_onchain_fee(u8 **pptr, const struct onchain_fee *of) +{ + towire_wirestring(pptr, of->acct_name); + towire_bitcoin_txid(pptr, &of->txid); + towire_amount_msat(pptr, of->credit); + towire_amount_msat(pptr, of->debit); + towire_u64(pptr, of->timestamp); + towire_u32(pptr, of->update_count); +} + +static struct onchain_fee *fromwire_onchain_fee(struct onchain_fees *onchain_fees, + const u8 **pptr, size_t *max) +{ + const char *acctname; + struct bitcoin_txid txid; + struct amount_msat credit; + struct amount_msat debit; + u64 timestamp; + u32 update_count; + + acctname = fromwire_wirestring(tmpctx, pptr, max); + fromwire_bitcoin_txid(pptr, max, &txid); + credit = fromwire_amount_msat(pptr, max); + debit = fromwire_amount_msat(pptr, max); + timestamp = fromwire_u64(pptr, max); + update_count = fromwire_u32(pptr, max); + if (pptr == NULL) + return NULL; + + return new_onchain_fee(onchain_fees, + take(acctname), &txid, credit, debit, timestamp, + update_count); +} + +static const char *ds_ofee_path(const tal_t *ctx, const char *acctname) +{ + return tal_fmt(ctx, "bookkeeper/onchain_fee/%s", acctname); +} + +static void onchain_fee_datastore_add(struct command *cmd, + const struct onchain_fee *of) +{ + const char *path = ds_ofee_path(tmpctx, of->acct_name); + u8 *data = tal_arr(tmpctx, u8, 0); + + towire_onchain_fee(&data, of); + jsonrpc_set_datastore_binary(cmd, path, data, tal_bytelen(data), + "create-or-append", + ignore_datastore_reply, NULL, NULL); +} void json_add_onchain_fee(struct json_stream *out, - struct onchain_fee *fee) + const struct onchain_fee *fee) { json_object_start(out, NULL); json_add_string(out, "account", fee->acct_name); @@ -19,3 +205,569 @@ void json_add_onchain_fee(struct json_stream *out, json_object_end(out); } +/* Get all onchain_fee for this account */ +struct onchain_fee **account_get_chain_fees(const tal_t *ctx, + const struct bkpr *bkpr, + const char *acct_name) +{ + struct ordered_ofees *ofees; + + ofees = ofees_hash_get(bkpr->onchain_fees->by_account, acct_name); + if (ofees) + return tal_dup_talarr(ctx, struct onchain_fee *, ofees->ofs); + return NULL; +} + +/* FIXME: Slow */ +struct onchain_fee **get_chain_fees_by_txid(const tal_t *ctx, + const struct bkpr *bkpr, + const struct bitcoin_txid *txid) +{ + struct onchain_fee **ret = tal_arr(ctx, struct onchain_fee *, 0); + struct ofees_hash_iter it; + struct ordered_ofees *ofees; + + for (ofees = ofees_hash_first(bkpr->onchain_fees->by_account, &it); + ofees; + ofees = ofees_hash_next(bkpr->onchain_fees->by_account, &it)) { + for (size_t i = 0; i < tal_count(ofees->ofs); i++) { + if (bitcoin_txid_eq(&ofees->ofs[i]->txid, txid)) + tal_arr_expand(&ret, ofees->ofs[i]); + } + } + order_fees(ret); + return ret; +} + +/* FIXME: slow */ +struct onchain_fee **list_chain_fees_timebox(const tal_t *ctx, + const struct bkpr *bkpr, + u64 start_time, u64 end_time) +{ + struct onchain_fee **ret = tal_arr(ctx, struct onchain_fee *, 0); + struct ofees_hash_iter it; + struct ordered_ofees *ofees; + + for (ofees = ofees_hash_first(bkpr->onchain_fees->by_account, &it); + ofees; + ofees = ofees_hash_next(bkpr->onchain_fees->by_account, &it)) { + for (size_t i = 0; i < tal_count(ofees->ofs); i++) { + if (ofees->ofs[i]->timestamp <= start_time) + continue; + if (ofees->ofs[i]->timestamp > end_time) + continue; + tal_arr_expand(&ret, ofees->ofs[i]); + } + } + order_fees(ret); + return ret; +} + +struct onchain_fee **list_chain_fees(const tal_t *ctx, const struct bkpr *bkpr) +{ + return list_chain_fees_timebox(ctx, bkpr, 0, SQLITE_MAX_UINT); +} + +static void insert_chain_fees_diff(struct command *cmd, + struct bkpr *bkpr, + const char *acct_name, + struct bitcoin_txid *txid, + struct amount_msat amount, + u64 timestamp) +{ + struct onchain_fee *of, **ofs; + u32 max_update_count = 0; + struct amount_msat current_amt, credit, debit; + + current_amt = AMOUNT_MSAT(0); + ofs = account_get_chain_fees(tmpctx, bkpr, acct_name); + + for (size_t i = 0; i < tal_count(ofs); i++) { + if (!bitcoin_txid_eq(&ofs[i]->txid, txid)) + continue; + if (!amount_msat_accumulate(¤t_amt, ofs[i]->credit)) + plugin_err(cmd->plugin, "Overflow when adding onchain fees"); + + if (!amount_msat_sub(¤t_amt, current_amt, ofs[i]->debit)) + plugin_err(cmd->plugin, "Underflow when subtracting onchain fees"); + if (ofs[i]->update_count > max_update_count) + max_update_count = ofs[i]->update_count; + } + + /* If they're already equal, no need to update */ + if (amount_msat_eq(current_amt, amount)) + return; + + if (!amount_msat_sub(&credit, amount, current_amt)) { + credit = AMOUNT_MSAT(0); + if (!amount_msat_sub(&debit, current_amt, amount)) + plugin_err(cmd->plugin, "shouldn't happen, unable to subtract"); + } else + debit = AMOUNT_MSAT(0); + + of = new_onchain_fee(bkpr->onchain_fees, + acct_name, txid, credit, debit, timestamp, + max_update_count+1); + onchain_fee_datastore_add(cmd, of); +} + +/* Sort these as ORDER BY txid, account_name */ +static int compare_onchain_fee_txid_account(struct onchain_fee *const *a, + struct onchain_fee *const *b, + void *arg) +{ + const struct onchain_fee *ofa = *a, *ofb = *b; + int cmp; + + cmp = memcmp(&ofa->txid, &ofb->txid, sizeof(ofb->txid)); + if (cmp) + return cmp; + return strcmp(ofa->acct_name, ofb->acct_name); +} + +static void finalize_sum(struct fee_sum ***sums, + struct fee_sum *sum, + struct amount_msat credit, + struct amount_msat debit) +{ + bool ok; + ok = amount_msat_sub(&sum->fees_paid, credit, debit); + assert(ok); + tal_arr_expand(sums, sum); +} + +/* Add up each account/txid group into a fee_sum */ +static struct fee_sum **fee_sums_by_txid_and_account(const tal_t *ctx, + struct onchain_fee **ofs) +{ + struct fee_sum **sums, *sum; + struct amount_msat credit, debit; + bool ok; + + /* We want this ordered by txid, accountname */ + if (ofs) /* Keep pendantic sanity checker happy! */ + asort(ofs, tal_count(ofs), compare_onchain_fee_txid_account, NULL); + + sums = tal_arr(ctx, struct fee_sum *, 0); + sum = NULL; + + /* Now for each txid, account_name pair, create a sum */ + for (size_t i = 0; i < tal_count(ofs); i++) { + /* If this is a new group, end the previous. */ + if (sum + && (!bitcoin_txid_eq(&ofs[i]->txid, sum->txid) + || !streq(ofs[i]->acct_name, sum->acct_name))) { + finalize_sum(&sums, sum, credit, debit); + sum = NULL; + } + if (!sum) { + sum = tal(sums, struct fee_sum); + sum->acct_name = tal_strdup(sum, ofs[i]->acct_name); + sum->txid = tal_dup(sum, struct bitcoin_txid, + &ofs[i]->txid); + credit = debit = AMOUNT_MSAT(0); + } + ok = amount_msat_accumulate(&credit, ofs[i]->credit); + assert(ok); + ok = amount_msat_accumulate(&debit, ofs[i]->debit); + } + + /* Final, if any */ + if (sum) + finalize_sum(&sums, sum, credit, debit); + + return sums; +} + +struct fee_sum **calculate_onchain_fee_sums(const tal_t *ctx, + const struct bkpr *bkpr) +{ + struct onchain_fee **ofs; + + ofs = list_chain_fees(tmpctx, bkpr); + return fee_sums_by_txid_and_account(ctx, ofs); +} + +char *update_channel_onchain_fees(const tal_t *ctx, + struct command *cmd, + struct bkpr *bkpr, + struct account *acct) +{ + struct chain_event *close_ev, **events; + struct amount_msat onchain_amt; + + assert(acct->onchain_resolved_block); + close_ev = find_chain_event_by_id(ctx, bkpr, cmd, + *acct->closed_event_db_id); + events = find_chain_events_bytxid(ctx, bkpr, cmd, + close_ev->spending_txid); + + /* Starting balance is close-ev's debit amount */ + onchain_amt = AMOUNT_MSAT(0); + for (size_t i = 0; i < tal_count(events); i++) { + struct chain_event *ev = events[i]; + + /* Ignore: + - htlc_fufill (to me) + - anchors (already exlc from output) + - to_external (if !htlc_fulfill) + */ + if (is_channel_account(ev->acct_name) + && streq("htlc_fulfill", ev->tag)) + continue; + + if (streq("anchor", ev->tag)) + continue; + + /* Ignore stuff which is paid to + * the peer's account (external), + * except for fulfilled htlcs (which originated + * in our balance) */ + if (is_external_account(ev->acct_name) + && !streq("htlc_fulfill", ev->tag)) + continue; + + /* anything else we count? */ + if (!amount_msat_accumulate(&onchain_amt, ev->credit)) + return tal_fmt(ctx, "Unable to add" + "onchain + %s's credit", + ev->tag); + } + + if (amount_msat_less_eq(onchain_amt, close_ev->debit)) { + struct amount_msat fees; + if (!amount_msat_sub(&fees, close_ev->debit, + onchain_amt)) + return tal_fmt(ctx, "Unable to sub" + "onchain sum from %s", + close_ev->tag); + + insert_chain_fees_diff(cmd, bkpr, acct->name, + close_ev->spending_txid, + fees, + close_ev->timestamp); + } + + return NULL; +} + +static char *is_closed_channel_txid(const tal_t *ctx, + struct bkpr *bkpr, + struct command *cmd, + struct chain_event *ev, + struct bitcoin_txid *txid, + bool *is_channel_close_tx) +{ + struct account *acct; + struct chain_event *closed; + u8 *inner_ctx = tal(NULL, u8); + + /* Figure out if this is a channel close tx */ + acct = find_account(bkpr, ev->acct_name); + assert(acct); + + /* There's a separate process for figuring out + * our onchain fees for channel closures */ + if (!acct->closed_event_db_id) { + *is_channel_close_tx = false; + tal_free(inner_ctx); + return NULL; + } + + /* is the closed utxo the same as the one + * we're trying to find fees for now */ + closed = find_chain_event_by_id(inner_ctx, bkpr, cmd, + *acct->closed_event_db_id); + if (!closed) { + *is_channel_close_tx = false; + tal_free(inner_ctx); + return tal_fmt(ctx, "Unable to find" + " db record (chain_evt)" + " with id %"PRIu64, + *acct->closed_event_db_id); + } + + if (!closed->spending_txid) { + *is_channel_close_tx = false; + tal_free(inner_ctx); + return tal_fmt(ctx, "Marked a closing" + " event that's not" + " actually a spend"); + } + + *is_channel_close_tx = + bitcoin_txid_eq(txid, closed->spending_txid); + tal_free(inner_ctx); + return NULL; +} + +char *maybe_update_onchain_fees(const tal_t *ctx, + struct command *cmd, + struct bkpr *bkpr, + struct bitcoin_txid *txid) +{ + size_t no_accts = 0, plus_ones; + const char *last_acctname = NULL; + bool contains_wallet = false, skip_wallet = false; + struct chain_event **events; + struct amount_msat deposit_msat = AMOUNT_MSAT(0), + withdraw_msat = AMOUNT_MSAT(0), + fees_msat, fee_part_msat; + char *err = NULL; + u8 *inner_ctx = tal(NULL, u8); + + /* Find all the deposits/withdrawals for this txid */ + events = find_chain_events_bytxid(inner_ctx, bkpr, cmd, txid); + + /* If we don't even have two events, skip */ + if (tal_count(events) < 2) + goto finished; + + for (size_t i = 0; i < tal_count(events); i++) { + bool is_channel_close_tx; + err = is_closed_channel_txid(ctx, bkpr, cmd, + events[i], txid, + &is_channel_close_tx); + + if (err) + goto finished; + + /* We skip channel close txs here! */ + if (is_channel_close_tx) + goto finished; + + if (events[i]->spending_txid) { + if (!amount_msat_accumulate(&withdraw_msat, + events[i]->debit)) { + err = tal_fmt(ctx, "Overflow adding withdrawal debits for" + " txid: %s", + fmt_bitcoin_txid(ctx, + txid)); + goto finished; + } + } else { + if (!amount_msat_accumulate(&deposit_msat, + events[i]->credit)) { + err = tal_fmt(ctx, "Overflow adding deposit credits for" + " txid: %s", + fmt_bitcoin_txid(ctx, + txid)); + goto finished; + } + } + + /* While we're here, also count number of accts + * that were involved! Two little tricks here. + * + * One) we sorted the output + * by acct id, so we can cheat how we count: if + * it's a different acct_id than the last seen, we inc + * the counter. + * + * Two) who "gets" fee attribution is complicated + * and requires knowing if the wallet/external accts + * were involved (everything else is channel accts) + * */ + if (!last_acctname || !streq(last_acctname, events[i]->acct_name)) { + last_acctname = events[i]->acct_name; + /* Don't count external accts */ + if (!is_external_account(last_acctname)) + no_accts++; + + contains_wallet |= is_wallet_account(last_acctname); + } + } + + /* Only affects external accounts, we can ignore */ + if (no_accts == 0) + goto finished; + + /* If either is zero, keep waiting */ + if (amount_msat_is_zero(withdraw_msat) + || amount_msat_is_zero(deposit_msat)) + goto finished; + + /* If our withdraws < deposits, wait for more data */ + if (amount_msat_less(withdraw_msat, deposit_msat)) + goto finished; + + if (!amount_msat_sub(&fees_msat, withdraw_msat, deposit_msat)) { + err = tal_fmt(ctx, "Err subtracting withdraw %s from deposit %s" + " for txid %s", + fmt_amount_msat(ctx, withdraw_msat), + fmt_amount_msat(ctx, deposit_msat), + fmt_bitcoin_txid(ctx, txid)); + goto finished; + } + + /* Now we need to figure out how to allocate fees to each account + * that was involved in the tx. This is a lil complex, buckle up*/ + + /* If the wallet's involved + there were any other accounts, decr by one */ + if (no_accts > 1 && contains_wallet) { + skip_wallet = true; + no_accts--; + } + + /* Now we divide by the number of accts involved, to figure out the + * value to log for each account! */ + fee_part_msat = amount_msat_div(fees_msat, no_accts); + + /* So we don't lose any msats b/c of rounding, find the number of + * accts to add an extra msat onto */ + plus_ones = fees_msat.millisatoshis % no_accts; /* Raw: mod calc */ + + /* Now we log (or update the existing record) for each acct */ + last_acctname = NULL; + for (size_t i = 0; i < tal_count(events); i++) { + struct amount_msat fees; + + if (last_acctname && streq(last_acctname, events[i]->acct_name)) + continue; + + last_acctname = events[i]->acct_name; + + /* We *never* assign fees to external accounts; + * if external funds were contributed to a tx + * we wouldn't record it -- fees are solely ours */ + if (is_external_account(last_acctname)) + continue; + + /* We only attribute fees to the wallet + * if the wallet is the only game in town */ + if (skip_wallet && is_wallet_account(last_acctname)) { + /* But we might need to clean up any fees assigned + * to the wallet from a previous round, where it + * *was* the only game in town */ + insert_chain_fees_diff(cmd, bkpr, last_acctname, txid, + AMOUNT_MSAT(0), + events[i]->timestamp); + continue; + } + + /* Add an extra msat onto plus_ones accts + * so we don't lose any precision in + * our accounting */ + if (plus_ones > 0) { + plus_ones--; + if (!amount_msat_add(&fees, fee_part_msat, + AMOUNT_MSAT(1))) { + err = "Overflow adding 1 ... yeah right"; + /* We're gonna keep going, yolo */ + fees = fee_part_msat; + } + } else + fees = fee_part_msat; + + insert_chain_fees_diff(cmd, bkpr, last_acctname, txid, fees, + events[i]->timestamp); + + } + +finished: + tal_free(inner_ctx); + return err; +} + +struct fee_sum **find_account_onchain_fees(const tal_t *ctx, + const struct bkpr *bkpr, + const struct account *acct) +{ + struct onchain_fee **ofs; + + ofs = account_get_chain_fees(tmpctx, bkpr, acct->name); + return fee_sums_by_txid_and_account(ctx, ofs); +} + +/* FIXME: Put this value into fee_sums! */ +u64 onchain_fee_last_timestamp(const struct bkpr *bkpr, + const char *acct_name, + const struct bitcoin_txid *txid) +{ + struct onchain_fee **ofs; + u64 timestamp = 0; + + ofs = account_get_chain_fees(tmpctx, bkpr, acct_name); + for (size_t i = 0; i < tal_count(ofs); i++) { + if (!bitcoin_txid_eq(&ofs[i]->txid, txid)) + continue; + if (ofs[i]->timestamp > timestamp) + timestamp = ofs[i]->timestamp; + } + return timestamp; +} + +/* If we're freeing the entire hash table, remove destructors from + * individual entries! */ +static void ofees_hash_destroy(struct ofees_hash *ofees_hash) +{ + struct ofees_hash_iter it; + struct ordered_ofees *ofees; + + for (ofees = ofees_hash_first(ofees_hash, &it); + ofees; + ofees = ofees_hash_next(ofees_hash, &it)) { + for (size_t i = 0; i < tal_count(ofees->ofs); i++) { + tal_del_destructor2(ofees->ofs[i], + destroy_onchain_fee, ofees_hash); + } + } +} + +static void memleak_scan_ofees_hash(struct htable *memtable, + struct ofees_hash *ht) +{ + memleak_scan_htable(memtable, &ht->raw); +} + +struct onchain_fees *init_onchain_fees(const tal_t *ctx, + struct command *init_cmd) +{ + struct onchain_fees *onchain_fees = tal(ctx, struct onchain_fees); + struct json_out *params = json_out_new(tmpctx); + const jsmntok_t *result; + const char *buf; + const jsmntok_t *datastore, *t; + size_t i; + + onchain_fees->by_account = tal(onchain_fees, struct ofees_hash); + ofees_hash_init(onchain_fees->by_account); + tal_add_destructor(onchain_fees->by_account, ofees_hash_destroy); + memleak_add_helper(onchain_fees->by_account, memleak_scan_ofees_hash); + + json_out_start(params, NULL, '{'); + json_out_start(params, "key", '['); + json_out_addstr(params, NULL, "bookkeeper"); + json_out_addstr(params, NULL, "onchain_fee"); + json_out_end(params, ']'); + json_out_end(params, '}'); + result = jsonrpc_request_sync(tmpctx, init_cmd, + "listdatastore", + params, &buf); + + datastore = json_get_member(buf, result, "datastore"); + json_for_each_arr(i, t, datastore) { + size_t datalen; + const jsmntok_t *key, *datatok; + const u8 *data; + + /* Key is an array, first two elements are bookkeeper, onchain_fee */ + key = json_get_member(buf, t, "key") + 3; + datatok = json_get_member(buf, t, "hex"); + /* In case someone creates a subdir? */ + if (!datatok) + continue; + + data = json_tok_bin_from_hex(tmpctx, buf, datatok); + datalen = tal_bytelen(data); + + while (datalen != 0) { + if (!fromwire_onchain_fee(onchain_fees, &data, &datalen)) + plugin_err(init_cmd->plugin, + "Invalid onchain_fee for %.*s in datastore", + json_tok_full_len(key), + json_tok_full(buf, key)); + } + } + return onchain_fees; +} diff --git a/plugins/bkpr/onchain_fee.h b/plugins/bkpr/onchain_fee.h index 03773a014cf7..8b954b092194 100644 --- a/plugins/bkpr/onchain_fee.h +++ b/plugins/bkpr/onchain_fee.h @@ -4,17 +4,17 @@ #include "config.h" #include #include +#include +struct account; struct amount_msat; struct bitcoin_txid; +struct db; +struct onchain_fees; struct onchain_fee { - - /* db_id of account this event belongs to */ - u64 acct_db_id; - /* Name of the account this belongs to */ - char *acct_name; + const char *acct_name; /* Transaction that we're recording fees for */ struct bitcoin_txid txid; @@ -31,6 +31,61 @@ struct onchain_fee { }; void json_add_onchain_fee(struct json_stream *out, - struct onchain_fee *fee); + const struct onchain_fee *fee); + +/* List all chain fees, for all accounts */ +struct onchain_fee **list_chain_fees(const tal_t *ctx, const struct bkpr *bkpr); + +/* Get all chain fees, order by timestamp. + * + * @ctx - context to allocate from + * @db - database to query + * @start_time - UNIX timestamp to query after (exclusive) + * @end_time - UNIX timestamp to query until (inclusive) + */ +struct onchain_fee **list_chain_fees_timebox(const tal_t *ctx, + const struct bkpr *bkpr, + u64 start_time, u64 end_time); + +/* Get all chain fees for a transaction id, order by timestamp */ +struct onchain_fee **get_chain_fees_by_txid(const tal_t *ctx, + const struct bkpr *bkpr, + const struct bitcoin_txid *txid); + +/* Get chain fees for account */ +struct onchain_fee **account_get_chain_fees(const tal_t *ctx, + const struct bkpr *bkpr, + const char *acct_name); + +/* Returns a list of sums of the fees we've recorded for every txid + * for the given account */ +struct fee_sum **find_account_onchain_fees(const tal_t *ctx, + const struct bkpr *bkpr, + const struct account *acct); + +/* Final all the onchain fees */ +struct fee_sum **calculate_onchain_fee_sums(const tal_t *ctx, + const struct bkpr *bkpr); + +/* Find the last timestamp for the onchain fees for this txid + account */ +u64 onchain_fee_last_timestamp(const struct bkpr *bkpr, + const char *acct_name, + const struct bitcoin_txid *txid); + +/* Update our onchain fees now? */ +char *maybe_update_onchain_fees(const tal_t *ctx, + struct command *cmd, + struct bkpr *bkpr, + struct bitcoin_txid *txid); + +/* We calculate onchain fees for channel closes a bit different */ +char *update_channel_onchain_fees(const tal_t *ctx, + struct command *cmd, + struct bkpr *bkpr, + struct account *acct); + +/* Set up the onchain_fees struct */ +struct onchain_fees *init_onchain_fees(const tal_t *ctx, + struct command *init_cmd); #endif /* LIGHTNING_PLUGINS_BKPR_ONCHAIN_FEE_H */ diff --git a/plugins/bkpr/rebalances.c b/plugins/bkpr/rebalances.c new file mode 100644 index 000000000000..d61c330b0d6d --- /dev/null +++ b/plugins/bkpr/rebalances.c @@ -0,0 +1,155 @@ +#include "config.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* Hash table contains a pair of these: [a, b] and [b, a] */ +struct rebalance_pair { + u64 pair[2]; +}; + +static size_t rebalance_hash(u64 key) +{ + return siphash24(siphash_seed(), &key, sizeof(key)); +} + +static u64 rebalance_key(const struct rebalance_pair *p) +{ + return p->pair[0]; +} + +static bool rebalance_key_eq(const struct rebalance_pair *p, u64 key) +{ + return p->pair[0] == key; +} + +HTABLE_DEFINE_NODUPS_TYPE(struct rebalance_pair, + rebalance_key, + rebalance_hash, + rebalance_key_eq, + rebalance_htable); + +struct rebalances { + struct rebalance_htable *pairs; +}; + +static void new_rebalance_pair(struct rebalances *r, + u64 created_index1, u64 created_index2) +{ + struct rebalance_pair *p1, *p2; + + p1 = tal(r->pairs, struct rebalance_pair); + p1->pair[0] = created_index1; + p1->pair[1] = created_index2; + rebalance_htable_add(r->pairs, p1); + + p2 = tal(r->pairs, struct rebalance_pair); + p2->pair[0] = created_index2; + p2->pair[1] = created_index1; + rebalance_htable_add(r->pairs, p2); +} + +static const char *ds_rebalance_path(const tal_t *ctx, u64 id1, u64 id2) +{ + u64 lesser, greater; + if (id1 < id2) { + lesser = id1; + greater = id2; + } else { + lesser = id2; + greater = id1; + } + return tal_fmt(ctx, "bookkeeper/rebalances/%"PRIu64"-%"PRIu64, + lesser, greater); +} + +void add_rebalance_pair(struct command *cmd, + struct bkpr *bkpr, + u64 created_index1, u64 created_index2) +{ + const char *path; + new_rebalance_pair(bkpr->rebalances, created_index1, created_index2); + + path = ds_rebalance_path(tmpctx, created_index1, created_index2); + /* Contents are ignored: key is the data */ + jsonrpc_set_datastore_string(cmd, path, "", "must-create", + ignore_datastore_reply, NULL, NULL); +} + +const u64 *find_rebalance(const struct bkpr *bkpr, u64 created_index) +{ + const struct rebalance_pair *p; + + p = rebalance_htable_get(bkpr->rebalances->pairs, created_index); + return p ? &p->pair[1] : NULL; +} + +static void memleak_scan_rebalance_htable(struct htable *memtable, + struct rebalance_htable *ht) +{ + memleak_scan_htable(memtable, &ht->raw); +} + +struct rebalances *init_rebalances(const tal_t *ctx, + struct command *init_cmd) +{ + struct json_out *params = json_out_new(tmpctx); + const jsmntok_t *result; + const char *buf; + const jsmntok_t *datastore, *t; + size_t i; + + struct rebalances *r = tal(ctx, struct rebalances); + r->pairs = tal(r, struct rebalance_htable); + rebalance_htable_init(r->pairs); + memleak_add_helper(r->pairs, memleak_scan_rebalance_htable); + + /* Query all keys under bookkeeper/rebalances */ + json_out_start(params, NULL, '{'); + json_out_start(params, "key", '['); + json_out_addstr(params, NULL, "bookkeeper"); + json_out_addstr(params, NULL, "rebalances"); + json_out_end(params, ']'); + json_out_end(params, '}'); + + result = jsonrpc_request_sync(tmpctx, init_cmd, + "listdatastore", params, &buf); + + datastore = json_get_member(buf, result, "datastore"); + json_for_each_arr(i, t, datastore) { + const jsmntok_t *keytok = json_get_member(buf, t, "key"); + jsmntok_t lessertok, greatertok; + u64 lesser, greater; + + if (keytok->size != 3) + goto weird; + + /* key = ["bookkeeper", "rebalances", "-"] */ + if (!split_tok(buf, keytok + 2, '-', &lessertok, &greatertok)) + goto weird; + + if (!json_to_u64(buf, &lessertok, &lesser) + || !json_to_u64(buf, &greatertok, &greater)) + goto weird; + + new_rebalance_pair(r, lesser, greater); + continue; + + weird: + plugin_log(init_cmd->plugin, LOG_BROKEN, "Unparsable datastore %.*s", + json_tok_full_len(keytok), + json_tok_full(buf, keytok)); + } + + return r; +} diff --git a/plugins/bkpr/rebalances.h b/plugins/bkpr/rebalances.h new file mode 100644 index 000000000000..fa18e7d187e1 --- /dev/null +++ b/plugins/bkpr/rebalances.h @@ -0,0 +1,20 @@ +#ifndef LIGHTNING_PLUGINS_BKPR_REBALANCES_H +#define LIGHTNING_PLUGINS_BKPR_REBALANCES_H +#include "config.h" + +struct command; +struct bkpr; +struct sha256; +struct bitcoin_outpoint; +struct channel_event; + +void add_rebalance_pair(struct command *cmd, + struct bkpr *bkpr, + u64 created_index1, u64 created_index2); + +/* Return NULL, or pointer to the other part of this rebalance pair */ +const u64 *find_rebalance(const struct bkpr *bkpr, u64 created_index); + +struct rebalances *init_rebalances(const tal_t *ctx, + struct command *init_cmd); +#endif /* LIGHTNING_PLUGINS_BKPR_REBALANCES_H */ diff --git a/plugins/bkpr/recorder.c b/plugins/bkpr/recorder.c index 55a7b2d3814c..82c3498c4a70 100644 --- a/plugins/bkpr/recorder.c +++ b/plugins/bkpr/recorder.c @@ -11,376 +11,69 @@ #include #include #include +#include +#include #include #include #include +#include #include +#include #include - -static struct chain_event *stmt2chain_event(const tal_t *ctx, struct db_stmt *stmt) -{ - struct chain_event *e = tal(ctx, struct chain_event); - e->db_id = db_col_u64(stmt, "e.id"); - e->acct_db_id = db_col_u64(stmt, "e.account_id"); - e->acct_name = db_col_strdup(e, stmt, "a.name"); - - if (!db_col_is_null(stmt, "e.origin")) - e->origin_acct = db_col_strdup(e, stmt, "e.origin"); - else - e->origin_acct = NULL; - - e->tag = db_col_strdup(e, stmt, "e.tag"); - - e->credit = db_col_amount_msat(stmt, "e.credit"); - e->debit = db_col_amount_msat(stmt, "e.debit"); - e->output_value = db_col_amount_msat(stmt, "e.output_value"); - - e->timestamp = db_col_u64(stmt, "e.timestamp"); - e->blockheight = db_col_int(stmt, "e.blockheight"); - - db_col_txid(stmt, "e.utxo_txid", &e->outpoint.txid); - e->outpoint.n = db_col_int(stmt, "e.outnum"); - - if (!db_col_is_null(stmt, "e.payment_id")) { - e->payment_id = tal(e, struct sha256); - db_col_sha256(stmt, "e.payment_id", e->payment_id); - } else - e->payment_id = NULL; - - if (!db_col_is_null(stmt, "e.spending_txid")) { - e->spending_txid = tal(e, struct bitcoin_txid); - db_col_txid(stmt, "e.spending_txid", e->spending_txid); - } else - e->spending_txid = NULL; - - e->stealable = db_col_int(stmt, "e.stealable") == 1; - - if (!db_col_is_null(stmt, "e.ev_desc")) - e->desc = db_col_strdup(e, stmt, "e.ev_desc"); - else - e->desc = NULL; - - e->splice_close = db_col_int(stmt, "e.spliced") == 1; - - return e; -} - -static struct chain_event **find_chain_events(const tal_t *ctx, - struct db_stmt *stmt TAKES) -{ - struct chain_event **results; - - db_query_prepared(stmt); - if (stmt->error) - db_fatal(stmt->db, "find_chain_events err: %s", stmt->error); - results = tal_arr(ctx, struct chain_event *, 0); - while (db_step(stmt)) { - struct chain_event *e = stmt2chain_event(results, stmt); - tal_arr_expand(&results, e); - } - - if (taken(stmt)) - tal_free(stmt); - - return results; -} - -static struct channel_event *stmt2channel_event(const tal_t *ctx, struct db_stmt *stmt) -{ - struct channel_event *e = tal(ctx, struct channel_event); - - e->db_id = db_col_u64(stmt, "e.id"); - e->acct_db_id = db_col_u64(stmt, "e.account_id"); - e->acct_name = db_col_strdup(e, stmt, "a.name"); - - e->tag = db_col_strdup(e, stmt, "e.tag"); - - e->credit = db_col_amount_msat(stmt, "e.credit"); - e->debit = db_col_amount_msat(stmt, "e.debit"); - e->fees = db_col_amount_msat(stmt, "e.fees"); - - if (!db_col_is_null(stmt, "e.payment_id")) { - e->payment_id = tal(e, struct sha256); - db_col_sha256(stmt, "e.payment_id", e->payment_id); - } else - e->payment_id = NULL; - e->part_id = db_col_int(stmt, "e.part_id"); - e->timestamp = db_col_u64(stmt, "e.timestamp"); - - if (!db_col_is_null(stmt, "e.ev_desc")) - e->desc = db_col_strdup(e, stmt, "e.ev_desc"); - else - e->desc = NULL; - - if (!db_col_is_null(stmt, "e.rebalance_id")) { - e->rebalance_id = tal(e, u64); - *e->rebalance_id = db_col_u64(stmt, "e.rebalance_id"); - } else - e->rebalance_id = NULL; - - return e; -} - -static struct channel_event **find_channel_events(const tal_t *ctx, - struct db_stmt *stmt TAKES) -{ - struct channel_event **results; - - db_query_prepared(stmt); - if (stmt->error) - db_fatal(stmt->db, "find_channel_events err: %s", stmt->error); - results = tal_arr(ctx, struct channel_event *, 0); - while (db_step(stmt)) { - struct channel_event *e = stmt2channel_event(results, stmt); - tal_arr_expand(&results, e); - } - - if (taken(stmt)) - tal_free(stmt); - - return results; -} - -static struct rebalance *stmt2rebalance(const tal_t *ctx, struct db_stmt *stmt) -{ - struct rebalance *r = tal(ctx, struct rebalance); - - r->in_ev_id = db_col_u64(stmt, "in_e.id"); - r->out_ev_id = db_col_u64(stmt, "out_e.id"); - r->in_acct_name = db_col_strdup(r, stmt, "in_acct.name"); - r->out_acct_name = db_col_strdup(r, stmt, "out_acct.name"); - r->rebal_msat = db_col_amount_msat(stmt, "in_e.credit"); - r->fee_msat = db_col_amount_msat(stmt, "out_e.fees"); - - return r; -} - struct chain_event **list_chain_events_timebox(const tal_t *ctx, - struct db *db, + const struct bkpr *bkpr, + struct command *cmd, u64 start_time, u64 end_time) { - 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.timestamp" - ", e.blockheight" - ", e.utxo_txid" - ", e.outnum" - ", e.spending_txid" - ", e.payment_id" - ", e.stealable" - ", e.ev_desc" - ", e.spliced" - " FROM chain_events e" - " LEFT OUTER JOIN accounts a" - " ON e.account_id = a.id" - " WHERE e.timestamp > ?" - " AND e.timestamp <= ?" - " ORDER BY e.timestamp, e.id;")); - - db_bind_u64(stmt, start_time); - db_bind_u64(stmt, end_time); - return find_chain_events(ctx, take(stmt)); + return chain_events_from_sql(ctx, bkpr, cmd, + SELECT_CHAIN_EVENTS + " WHERE timestamp > %"PRIu64 + " AND timestamp <= %"PRIu64 + " AND created_index <= %"PRIu64 + " ORDER BY timestamp, created_index;", + start_time, end_time, + bkpr->chainmoves_index); } -struct chain_event **list_chain_events(const tal_t *ctx, struct db *db) +struct chain_event **list_chain_events(const tal_t *ctx, + const struct bkpr *bkpr, + struct command *cmd) { - return list_chain_events_timebox(ctx, db, 0, SQLITE_MAX_UINT); + return list_chain_events_timebox(ctx, bkpr, cmd, 0, SQLITE_MAX_UINT); } struct chain_event **account_get_chain_events(const tal_t *ctx, - struct db *db, + const struct bkpr *bkpr, + struct command *cmd, struct account *acct) { - 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.timestamp" - ", e.blockheight" - ", e.utxo_txid" - ", e.outnum" - ", e.spending_txid" - ", e.payment_id" - ", e.stealable" - ", e.ev_desc" - ", e.spliced" - " FROM chain_events e" - " LEFT OUTER JOIN accounts a" - " ON e.account_id = a.id" - " WHERE e.account_id = ?" - " ORDER BY e.timestamp, e.id")); - - db_bind_int(stmt, acct->db_id); - return find_chain_events(ctx, take(stmt)); + return chain_events_from_sql(ctx, bkpr, cmd, + SELECT_CHAIN_EVENTS + " WHERE account_id = '%s'" + " AND created_index <= %"PRIu64 + " ORDER BY timestamp, created_index;", + sql_string(tmpctx, acct->name), + bkpr->chainmoves_index); } static struct chain_event **find_txos_for_tx(const tal_t *ctx, - struct db *db, - struct bitcoin_txid *txid) -{ - 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.timestamp" - ", e.blockheight" - ", e.utxo_txid" - ", e.outnum" - ", e.spending_txid" - ", e.payment_id" - ", 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 = ?" + const struct bkpr *bkpr, + struct command *cmd, + const struct bitcoin_txid *txid) +{ + return chain_events_from_sql(ctx, bkpr, cmd, + SELECT_CHAIN_EVENTS + /* utxo is txid:outnum */ + " WHERE utxo LIKE '%s:%%'" + " AND created_index <= %"PRIu64 " ORDER BY " - " e.utxo_txid" - ", e.outnum" - ", e.spending_txid NULLS FIRST" - ", e.blockheight")); - - db_bind_txid(stmt, txid); - return find_chain_events(ctx, take(stmt)); -} - -struct fee_sum **calculate_onchain_fee_sums(const tal_t *ctx, struct db *db) -{ - struct db_stmt *stmt; - struct fee_sum **sums; - stmt = db_prepare_v2(db, SQL("SELECT" - " of.txid" - ", of.account_id" - ", a.name" - ", CAST(SUM(of.credit) AS BIGINT) as credit" - ", CAST(SUM(of.debit) AS BIGINT) as debit" - " FROM onchain_fees of" - " LEFT OUTER JOIN accounts a" - " ON of.account_id = a.id" - " GROUP BY of.txid" - ", of.account_id" - ", a.name" - " ORDER BY txid, account_id")); - - db_query_prepared(stmt); - - sums = tal_arr(ctx, struct fee_sum *, 0); - while (db_step(stmt)) { - struct fee_sum *sum; - struct amount_msat debit; - bool ok; - - sum = tal(sums, struct fee_sum); - sum->txid = tal(sum, struct bitcoin_txid); - - db_col_txid(stmt, "of.txid", sum->txid); - sum->acct_db_id = db_col_u64(stmt, "of.account_id"); - sum->acct_name = db_col_strdup(sum, stmt, "a.name"); - sum->fees_paid = db_col_amount_msat(stmt, "credit"); - debit = db_col_amount_msat(stmt, "debit"); - - ok = amount_msat_sub(&sum->fees_paid, sum->fees_paid, - debit); - assert(ok); - tal_arr_expand(&sums, sum); - } - - tal_free(stmt); - return sums; -} - -u64 onchain_fee_last_timestamp(struct db *db, - u64 acct_db_id, - struct bitcoin_txid *txid) -{ - struct db_stmt *stmt; - u64 timestamp; - - stmt = db_prepare_v2(db, SQL("SELECT" - " timestamp" - " FROM onchain_fees" - " WHERE account_id = ?" - " AND txid = ?" - " ORDER BY timestamp DESC")); - - - db_bind_u64(stmt, acct_db_id); - db_bind_txid(stmt, txid); - db_query_prepared(stmt); - - if (db_step(stmt)) - timestamp = db_col_u64(stmt, "timestamp"); - else - timestamp = 0; - - tal_free(stmt); - return timestamp; -} - -struct fee_sum **find_account_onchain_fees(const tal_t *ctx, - struct db *db, - struct account *acct) -{ - struct db_stmt *stmt; - struct fee_sum **sums; - stmt = db_prepare_v2(db, SQL("SELECT" - " txid" - ", CAST(SUM(credit) AS BIGINT) as credit" - ", CAST(SUM(debit) AS BIGINT) as debit" - " FROM onchain_fees" - " WHERE account_id = ?" - " GROUP BY txid" - " ORDER BY txid")); - - db_bind_u64(stmt, acct->db_id); - db_query_prepared(stmt); - - sums = tal_arr(ctx, struct fee_sum *, 0); - while (db_step(stmt)) { - struct fee_sum *sum; - struct amount_msat amt; - bool ok; - - sum = tal(sums, struct fee_sum); - sum->acct_db_id = acct->db_id; - sum->txid = tal(sum, struct bitcoin_txid); - db_col_txid(stmt, "txid", sum->txid); - - sum->fees_paid = db_col_amount_msat(stmt, "credit"); - amt = db_col_amount_msat(stmt, "debit"); - ok = amount_msat_sub(&sum->fees_paid, sum->fees_paid, amt); - assert(ok); - tal_arr_expand(&sums, sum); - } - - tal_free(stmt); - return sums; + " utxo" + ", spending_txid NULLS FIRST" + ", blockheight", + fmt_bitcoin_txid(tmpctx, txid), + bkpr->chainmoves_index); } static struct txo_pair *new_txo_pair(const tal_t *ctx) @@ -392,9 +85,10 @@ static struct txo_pair *new_txo_pair(const tal_t *ctx) } static struct txo_set *find_txo_set(const tal_t *ctx, - struct db *db, - struct bitcoin_txid *txid, - u64 *acct_db_id, + const struct bkpr *bkpr, + struct command *cmd, + const struct bitcoin_txid *txid, + const char *acct_name, bool *is_complete) { struct txo_pair *pr; @@ -404,7 +98,7 @@ static struct txo_set *find_txo_set(const tal_t *ctx, /* In some special cases (the opening tx), we only * want the outputs that pertain to a given account, * most other times we want all utxos, regardless of account */ - evs = find_txos_for_tx(ctx, db, txid); + evs = find_txos_for_tx(ctx, bkpr, cmd, txid); txos->pairs = tal_arr(txos, struct txo_pair *, 0); txos->txid = tal_dup(txos, struct bitcoin_txid, txid); @@ -417,7 +111,7 @@ static struct txo_set *find_txo_set(const tal_t *ctx, for (size_t i = 0; i < tal_count(evs); i++) { struct chain_event *ev = evs[i]; - if (acct_db_id && ev->acct_db_id != *acct_db_id) + if (acct_name && !streq(ev->acct_name, acct_name)) continue; if (ev->spending_txid) { @@ -471,17 +165,18 @@ static bool txid_in_list(struct bitcoin_txid **list, } bool find_txo_chain(const tal_t *ctx, - struct db *db, - struct account *acct, + const struct bkpr *bkpr, + struct command *cmd, + const struct account *acct, struct txo_set ***sets) { struct bitcoin_txid **txids; struct chain_event *open_ev; bool is_complete = true; - u64 *start_acct_id = tal(NULL, u64); + const char *start_acct_name; assert(acct->open_event_db_id); - open_ev = find_chain_event_by_id(ctx, db, + open_ev = find_chain_event_by_id(ctx, bkpr, cmd, *acct->open_event_db_id); if (sets) @@ -492,21 +187,20 @@ bool find_txo_chain(const tal_t *ctx, /* We only want to filter by the account for the very * first utxo that we get the tree for, so we * start w/ this acct id... */ - *start_acct_id = open_ev->acct_db_id; + start_acct_name = open_ev->acct_name; for (size_t i = 0; i < tal_count(txids); i++) { struct txo_set *set; bool set_complete; - set = find_txo_set(ctx, db, txids[i], - start_acct_id, + set = find_txo_set(ctx, bkpr, cmd, txids[i], + start_acct_name, &set_complete); /* After first use, we free the acct dbid ptr, * which will pass in NULL and not filter by * account for any subsequent txo_set hunt */ - if (start_acct_id) - start_acct_id = tal_free(start_acct_id); + start_acct_name = NULL; is_complete &= set_complete; for (size_t j = 0; j < tal_count(set->pairs); j++) { @@ -526,7 +220,7 @@ bool find_txo_chain(const tal_t *ctx, && !streq(pr->spend->tag, "to_miner") && !txid_in_list(txids, pr->spend->spending_txid) /* We dont trace utxos for non related accts */ - && (pr->spend->acct_db_id == acct->db_id + && (streq(pr->spend->acct_name, acct->name) /* Unless it's stealable, in which case * we track the resolution of the htlc tx */ || pr->spend->stealable)) @@ -541,59 +235,70 @@ bool find_txo_chain(const tal_t *ctx, return is_complete; } -struct account *find_close_account(const tal_t *ctx, - struct db *db, - struct bitcoin_txid *txid) -{ - struct db_stmt *stmt; - struct account *close_acct; - char *acct_name; - - stmt = db_prepare_v2(db, SQL("SELECT" - " a.name" - " FROM chain_events e" - " LEFT OUTER JOIN accounts a" - " ON e.account_id = a.id" - " WHERE " - " e.tag = ?" - " AND e.spending_txid = ?" - /* ignore splicing 'close' events */ - " AND e.spliced = 0 ")); - - db_bind_text(stmt, mvt_tag_str(MVT_CHANNEL_CLOSE)); - db_bind_txid(stmt, txid); - db_query_prepared(stmt); - - if (db_step(stmt)) { - acct_name = db_col_strdup(stmt, stmt, "a.name"); - close_acct = find_account(ctx, db, acct_name); - } else - close_acct = NULL; - - tal_free(stmt); - return close_acct; +const char *find_close_account_name(const tal_t *ctx, + const struct bkpr *bkpr, + struct command *cmd, + const struct bitcoin_txid *txid) +{ + const char *buf; + const jsmntok_t *result, *rows, *row; + size_t i; + const char *acct_name = NULL; + + /* We look for a CHANNEL_CLOSE spend, but ignore “spliced” close events. */ + result = sql_req(ctx, cmd, &buf, + "SELECT account_id" + " FROM chainmoves cm" + " WHERE cm.primary_tag = '%s'" + " AND cm.spending_txid = X'%s'" + " AND NOT EXISTS (" + " SELECT 1 FROM chainmoves_extra_tags et" + " WHERE et.row = cm.created_index" + " AND et.extra_tags = 'spliced'" + " )" + " AND" + " cm.created_index <= %"PRIu64 + " LIMIT 1", + sql_string(tmpctx, mvt_tag_str(MVT_CHANNEL_CLOSE)), + fmt_bitcoin_txid(tmpctx, txid), + bkpr->chainmoves_index); + + rows = json_get_member(buf, result, "rows"); + json_for_each_arr(i, row, rows) { + /* Single column => row->size == 1; first value token is row+1 */ + const jsmntok_t *val = row + 1; + acct_name = json_strdup(ctx, buf, val); + break; /* only need the first row */ + } + + return acct_name; /* NULL if none found */ } -void maybe_mark_account_onchain(struct db *db, struct account *acct) +u64 account_onchain_closeheight(const struct bkpr *bkpr, + struct command *cmd, + const struct account *acct) { const u8 *ctx = tal(NULL, u8); struct txo_set **sets; struct chain_event *close_ev; - struct db_stmt *stmt; + u64 height; assert(acct->closed_count > 0); - close_ev = find_chain_event_by_id(ctx, db, + close_ev = find_chain_event_by_id(ctx, bkpr, cmd, *acct->closed_event_db_id); - if (find_txo_chain(ctx, db, acct, &sets)) { + if (find_txo_chain(ctx, bkpr, cmd, acct, &sets)) { /* Ok now we find the max block height of the * spending chain_events for this channel */ bool ok; + const char *buf; + const jsmntok_t *result, *rows, *row; + size_t i; /* Have we accounted for all the outputs */ ok = false; - for (size_t i = 0; i < tal_count(sets); i++) { + for (i = 0; i < tal_count(sets); i++) { if (bitcoin_txid_eq(sets[i]->txid, close_ev->spending_txid)) { @@ -605,1163 +310,234 @@ void maybe_mark_account_onchain(struct db *db, struct account *acct) if (!ok) { tal_free(ctx); - return; + return 0; } - stmt = db_prepare_v2(db, SQL("SELECT" - " blockheight" - " FROM chain_events" - " WHERE account_id = ?" - " AND spending_txid IS NOT NULL" - " ORDER BY blockheight DESC" - " LIMIT 1")); - - db_bind_u64(stmt, acct->db_id); - db_query_prepared(stmt); - ok = db_step(stmt); - assert(ok); - - acct->onchain_resolved_block = db_col_int(stmt, "blockheight"); - tal_free(stmt); - - /* Ok, now we update the account with this blockheight */ - stmt = db_prepare_v2(db, SQL("UPDATE accounts SET" - " onchain_resolved_block = ?" - " WHERE" - " id = ?")); - db_bind_int(stmt, acct->onchain_resolved_block); - db_bind_u64(stmt, acct->db_id); - db_exec_prepared_v2(take(stmt)); + result = sql_req(tmpctx, cmd, &buf, + "SELECT blockheight" + " FROM chainmoves" + " WHERE account_id = '%s'" + " AND spending_txid IS NOT NULL" + " AND created_index <= %"PRIu64 + " ORDER BY blockheight DESC" + " LIMIT 1", + sql_string(tmpctx, acct->name), + bkpr->chainmoves_index); + + rows = json_get_member(buf, result, "rows"); + assert(rows && rows->type == JSMN_ARRAY); + + height = 0; + json_for_each_arr(i, row, rows) { + const jsmntok_t *val = row + 1; + assert(row->size == 1); + ok = json_to_u64(buf, val, &height); + assert(ok); + break; + } + } else { + height = 0; } 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) -{ - struct db_stmt *stmt; - - /* Ok, now we update the account with this blockheight */ - stmt = db_prepare_v2(db, SQL("UPDATE channel_events SET" - " ev_desc = ?" - " WHERE" - " payment_id = ?")); - db_bind_text(stmt, desc); - db_bind_sha256(stmt, payment_hash); - db_exec_prepared_v2(take(stmt)); - - /* Ok, now we update the account with this blockheight */ - stmt = db_prepare_v2(db, SQL("UPDATE chain_events SET" - " ev_desc = ?" - " WHERE" - " payment_id = ?")); - db_bind_text(stmt, desc); - db_bind_sha256(stmt, payment_hash); - db_exec_prepared_v2(take(stmt)); + return height; } struct chain_event *find_chain_event_by_id(const tal_t *ctx, - struct db *db, - u64 event_db_id) -{ - struct db_stmt *stmt; - struct chain_event *e; - - 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.timestamp" - ", e.blockheight" - ", e.utxo_txid" - ", e.outnum" - ", e.spending_txid" - ", e.payment_id" - ", e.stealable" - ", e.ev_desc" - ", e.spliced" - " FROM chain_events e" - " LEFT OUTER JOIN accounts a" - " ON e.account_id = a.id" - " WHERE " - " e.id = ?")); - - db_bind_u64(stmt, event_db_id); - db_query_prepared(stmt); - if (db_step(stmt)) - e = stmt2chain_event(ctx, stmt); - else - e = NULL; - - tal_free(stmt); - return e; + const struct bkpr *bkpr, + struct command *cmd, + u64 created_index) +{ + struct chain_event **evs = + chain_events_from_sql(tmpctx, bkpr, cmd, + SELECT_CHAIN_EVENTS + " WHERE created_index = %"PRIu64 + " LIMIT 1;", + created_index); + + if (tal_count(evs) == 0) + return NULL; + + return tal_steal(ctx, evs[0]); } struct chain_event **get_chain_events_by_outpoint(const tal_t *ctx, - struct db *db, - const struct bitcoin_outpoint *outpoint, - bool credits_only) + const struct bkpr *bkpr, + struct command *cmd, + const struct bitcoin_outpoint *outpoint) { - 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.timestamp" - ", e.blockheight" - ", e.utxo_txid" - ", e.outnum" - ", e.spending_txid" - ", e.payment_id" - ", 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.timestamp" - ", e.blockheight" - ", e.utxo_txid" - ", e.outnum" - ", e.spending_txid" - ", e.payment_id" - ", 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)); + return chain_events_from_sql(ctx, bkpr, cmd, + SELECT_CHAIN_EVENTS + " WHERE utxo = '%s'" + " AND credit_msat > 0" + " AND created_index <= %"PRIu64 + " ORDER BY timestamp, created_index", + fmt_bitcoin_outpoint(tmpctx, outpoint), + bkpr->chainmoves_index); } struct chain_event **get_chain_events_by_id(const tal_t *ctx, - struct db *db, + const struct bkpr *bkpr, + struct command *cmd, 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.timestamp" - ", e.blockheight" - ", e.utxo_txid" - ", e.outnum" - ", e.spending_txid" - ", e.payment_id" - ", 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, - const struct bitcoin_outpoint *outpoint, - const struct bitcoin_txid *spending_txid, - const char *tag) - -{ - struct db_stmt *stmt; - struct chain_event *e; - - if (spending_txid) { - 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.timestamp" - ", e.blockheight" - ", e.utxo_txid" - ", e.outnum" - ", e.spending_txid" - ", e.payment_id" - ", e.stealable" - ", e.ev_desc" - ", e.spliced" - " FROM chain_events e" - " LEFT OUTER JOIN accounts a" - " ON e.account_id = a.id" - " WHERE " - " e.spending_txid = ?" - " AND e.account_id = ?" - " AND e.utxo_txid = ?" - " AND e.outnum = ?")); - db_bind_txid(stmt, spending_txid); - } 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.timestamp" - ", e.blockheight" - ", e.utxo_txid" - ", e.outnum" - ", e.spending_txid" - ", e.payment_id" - ", e.stealable" - ", e.ev_desc" - ", e.spliced" - " FROM chain_events e" - " LEFT OUTER JOIN accounts a" - " ON e.account_id = a.id" - " WHERE " - " e.tag = ?" - " AND e.account_id = ?" - " AND e.utxo_txid = ?" - " AND e.outnum = ?" - " AND e.spending_txid IS NULL")); - db_bind_text(stmt, tag); - } - - db_bind_u64(stmt, acct->db_id); - db_bind_txid(stmt, &outpoint->txid); - db_bind_int(stmt, outpoint->n); - - db_query_prepared(stmt); - if (db_step(stmt)) - e = stmt2chain_event(ctx, stmt); - else - e = NULL; - - tal_free(stmt); - return e; + return chain_events_from_sql(ctx, bkpr, cmd, + SELECT_CHAIN_EVENTS + " WHERE payment_hash = X'%s'" + " AND created_index <= %"PRIu64 + " ORDER BY timestamp, created_index", + fmt_sha256(tmpctx, id), + bkpr->chainmoves_index); } -bool account_get_credit_debit(struct plugin *plugin, - struct db *db, +bool account_get_credit_debit(const struct bkpr *bkpr, + struct command *cmd, const char *acct_name, struct amount_msat *credit, struct amount_msat *debit) { - struct db_stmt *stmt; + const jsmntok_t *result, *rows, *row; + const char *buf; bool exists; /* Get sum from chain_events */ - stmt = db_prepare_v2(db, SQL("SELECT" - " CAST(SUM(ce.credit) AS BIGINT) as credit" - ", CAST(SUM(ce.debit) AS BIGINT) as debit" - " FROM chain_events ce" - " LEFT OUTER JOIN accounts a" - " ON a.id = ce.account_id" - " WHERE a.name = ?")); - db_bind_text(stmt, acct_name); - db_query_prepared(stmt); - - db_step(stmt); - if (db_col_is_null(stmt, "credit")) { - db_col_ignore(stmt, "debit"); + result = sql_req(tmpctx, cmd, &buf, + "SELECT" + " CAST(SUM(credit_msat) AS BIGINT)" + ", CAST(SUM(debit_msat) AS BIGINT)" + " FROM chainmoves" + " WHERE account_id = '%s'" + " AND created_index <= %"PRIu64, + sql_string(tmpctx, acct_name), + bkpr->chainmoves_index); + rows = json_get_member(buf, result, "rows"); + assert(rows && rows->type == JSMN_ARRAY && rows->size == 1); + row = rows + 1; + assert(row->size == 2); + if (json_tok_is_null(buf, row + 1)) { *credit = *debit = AMOUNT_MSAT(0); exists = false; } else { - *credit = db_col_amount_msat(stmt, "credit"); - *debit = db_col_amount_msat(stmt, "debit"); + json_to_msat(buf, row + 1, credit); + json_to_msat(buf, row + 2, debit); exists = true; } - tal_free(stmt); /* Get sum from channel_events */ - stmt = db_prepare_v2(db, SQL("SELECT" - " CAST(SUM(ce.credit) AS BIGINT) as credit" - ", CAST(SUM(ce.debit) AS BIGINT) as debit" - " FROM channel_events ce" - " LEFT OUTER JOIN accounts a" - " ON a.id = ce.account_id" - " WHERE a.name = ?")); - db_bind_text(stmt, acct_name); - db_query_prepared(stmt); - db_step(stmt); - - if (db_col_is_null(stmt, "credit")) { - db_col_ignore(stmt, "debit"); - } else { - if (!amount_msat_accumulate(credit, - db_col_amount_msat(stmt, "credit"))) { - plugin_err(plugin, "db overflow: chain credit %s, adding channel credit %s", + result = sql_req(tmpctx, cmd, &buf, + "SELECT" + " CAST(SUM(credit_msat) AS BIGINT)" + ", CAST(SUM(debit_msat) AS BIGINT)" + " FROM channelmoves" + " WHERE account_id = '%s'" + " AND created_index <= %"PRIu64, + sql_string(tmpctx, acct_name), + bkpr->channelmoves_index); + rows = json_get_member(buf, result, "rows"); + assert(rows && rows->type == JSMN_ARRAY && rows->size == 1); + row = rows + 1; + assert(row->size == 2); + if (!json_tok_is_null(buf, row + 1)) { + struct amount_msat channel_credit, channel_debit; + json_to_msat(buf, row + 1, &channel_credit); + json_to_msat(buf, row + 2, &channel_debit); + + if (!amount_msat_accumulate(credit, channel_credit)) { + plugin_err(cmd->plugin, "db overflow: chain credit %s, adding channel credit %s", fmt_amount_msat(tmpctx, *credit), - fmt_amount_msat(tmpctx, - db_col_amount_msat(stmt, "credit"))); + fmt_amount_msat(tmpctx, channel_credit)); } - if (!amount_msat_accumulate(debit, - db_col_amount_msat(stmt, "debit"))) { - plugin_err(plugin, "db overflow: chain debit %s, adding channel debit %s", + if (!amount_msat_accumulate(debit, channel_debit)) { + plugin_err(cmd->plugin, "db overflow: chain debit %s, adding channel debit %s", fmt_amount_msat(tmpctx, *debit), - fmt_amount_msat(tmpctx, - db_col_amount_msat(stmt, "debit"))); + fmt_amount_msat(tmpctx, channel_debit)); } exists = true; } - tal_free(stmt); return exists; } struct channel_event **list_channel_events_timebox(const tal_t *ctx, - struct db *db, + const struct bkpr *bkpr, + struct command *cmd, u64 start_time, u64 end_time) - { - struct db_stmt *stmt; - struct channel_event **results; - - stmt = db_prepare_v2(db, SQL("SELECT" - " e.id" - ", e.account_id" - ", a.name" - ", e.tag" - ", e.credit" - ", e.debit" - ", e.fees" - ", e.payment_id" - ", e.part_id" - ", e.timestamp" - ", e.ev_desc" - ", e.rebalance_id" - " FROM channel_events e" - " LEFT OUTER JOIN accounts a" - " ON a.id = e.account_id" - " WHERE e.timestamp > ?" - " AND e.timestamp <= ?" - " ORDER BY e.timestamp, e.id;")); - - db_bind_u64(stmt, start_time); - db_bind_u64(stmt, end_time); - db_query_prepared(stmt); - - results = tal_arr(ctx, struct channel_event *, 0); - while (db_step(stmt)) { - struct channel_event *e = stmt2channel_event(results, stmt); - tal_arr_expand(&results, e); - } - tal_free(stmt); - - return results; + return channel_events_from_sql(ctx, cmd, + SELECT_CHANNEL_EVENTS + " WHERE timestamp > %"PRIu64 + " AND timestamp <= %"PRIu64 + " AND created_index <= %"PRIu64 + " ORDER BY timestamp, created_index;", + start_time, end_time, + bkpr->channelmoves_index); } -struct channel_event **list_channel_events(const tal_t *ctx, struct db *db) +struct channel_event **list_channel_events(const tal_t *ctx, + const struct bkpr *bkpr, + struct command *cmd) { - return list_channel_events_timebox(ctx, db, 0, SQLITE_MAX_UINT); + return list_channel_events_timebox(ctx, bkpr, cmd, 0, SQLITE_MAX_UINT); } struct channel_event **account_get_channel_events(const tal_t *ctx, - struct db *db, + const struct bkpr *bkpr, + struct command *cmd, struct account *acct) { - struct db_stmt *stmt; - - stmt = db_prepare_v2(db, SQL("SELECT" - " e.id" - ", a.name" - ", e.account_id" - ", e.tag" - ", e.credit" - ", e.debit" - ", e.fees" - ", e.payment_id" - ", e.part_id" - ", e.timestamp" - ", e.ev_desc" - ", e.rebalance_id" - " FROM channel_events e" - " LEFT OUTER JOIN accounts a" - " ON a.id = e.account_id" - " WHERE e.account_id = ?" - " ORDER BY e.timestamp, e.id")); - - db_bind_u64(stmt, acct->db_id); - return find_channel_events(ctx, take(stmt)); + return channel_events_from_sql(ctx, cmd, + SELECT_CHANNEL_EVENTS + " WHERE account_id = '%s'" + " AND created_index <= %"PRIu64 + " ORDER BY timestamp, created_index", + sql_string(tmpctx, acct->name), + bkpr->channelmoves_index); } struct channel_event **get_channel_events_by_id(const tal_t *ctx, - struct db *db, - struct sha256 *id) -{ - struct db_stmt *stmt; - - stmt = db_prepare_v2(db, SQL("SELECT" - " e.id" - ", a.name" - ", e.account_id" - ", e.tag" - ", e.credit" - ", e.debit" - ", e.fees" - ", e.payment_id" - ", e.part_id" - ", e.timestamp" - ", e.ev_desc" - ", e.rebalance_id" - " FROM channel_events e" - " LEFT OUTER JOIN accounts a" - " ON a.id = e.account_id" - " WHERE e.payment_id = ?" - " ORDER BY e.timestamp, e.id")); - - db_bind_sha256(stmt, id); - return find_channel_events(ctx, take(stmt)); -} - -static struct onchain_fee *stmt2onchain_fee(const tal_t *ctx, - struct db_stmt *stmt) -{ - struct onchain_fee *of = tal(ctx, struct onchain_fee); - - of->acct_db_id = db_col_u64(stmt, "of.account_id"); - of->acct_name = db_col_strdup(of, stmt, "a.name"); - db_col_txid(stmt, "of.txid", &of->txid); - of->credit = db_col_amount_msat(stmt, "of.credit"); - of->debit = db_col_amount_msat(stmt, "of.debit"); - of->timestamp = db_col_u64(stmt, "of.timestamp"); - of->update_count = db_col_int(stmt, "of.update_count"); - - return of; -} - -static struct onchain_fee **find_onchain_fees(const tal_t *ctx, - struct db_stmt *stmt TAKES) -{ - struct onchain_fee **results; - - db_query_prepared(stmt); - if (stmt->error) - db_fatal(stmt->db, "find_onchain_fees err: %s", stmt->error); - results = tal_arr(ctx, struct onchain_fee *, 0); - while (db_step(stmt)) { - struct onchain_fee *of = stmt2onchain_fee(results, stmt); - tal_arr_expand(&results, of); - } - - if (taken(stmt)) - tal_free(stmt); - - return results; -} - -struct onchain_fee **account_get_chain_fees(const tal_t *ctx, struct db *db, - struct account *acct) -{ - struct db_stmt *stmt; - - stmt = db_prepare_v2(db, SQL("SELECT" - " of.account_id" - ", a.name" - ", of.txid" - ", of.credit" - ", of.debit" - ", of.timestamp" - ", of.update_count" - " FROM onchain_fees of" - " LEFT OUTER JOIN accounts a" - " ON a.id = of.account_id" - " WHERE of.account_id = ?" - " ORDER BY " - " of.timestamp" - ", of.txid" - ", of.update_count")); - - db_bind_u64(stmt, acct->db_id); - return find_onchain_fees(ctx, take(stmt)); -} - -struct onchain_fee **get_chain_fees_by_txid(const tal_t *ctx, struct db *db, - struct bitcoin_txid *txid) -{ - struct db_stmt *stmt; - - stmt = db_prepare_v2(db, SQL("SELECT" - " of.account_id" - ", a.name" - ", of.txid" - ", of.credit" - ", of.debit" - ", of.timestamp" - ", of.update_count" - " FROM onchain_fees of" - " LEFT OUTER JOIN accounts a" - " ON a.id = of.account_id" - " WHERE of.txid = ?" - " ORDER BY " - " of.timestamp" - ", of.txid" - ", of.update_count")); - - db_bind_txid(stmt, txid); - return find_onchain_fees(ctx, take(stmt)); -} - -struct onchain_fee **list_chain_fees_timebox(const tal_t *ctx, struct db *db, - u64 start_time, u64 end_time) -{ - struct db_stmt *stmt; - - stmt = db_prepare_v2(db, SQL("SELECT" - " of.account_id" - ", a.name" - ", of.txid" - ", of.credit" - ", of.debit" - ", of.timestamp" - ", of.update_count" - " FROM onchain_fees of" - " LEFT OUTER JOIN accounts a" - " ON a.id = of.account_id" - " WHERE timestamp > ?" - " AND timestamp <= ?" - " ORDER BY " - " of.timestamp" - ", of.account_id" - ", of.txid" - ", of.update_count")); - - db_bind_u64(stmt, start_time); - db_bind_u64(stmt, end_time); - return find_onchain_fees(ctx, take(stmt)); -} - -struct onchain_fee **list_chain_fees(const tal_t *ctx, struct db *db) -{ - return list_chain_fees_timebox(ctx, db, 0, SQLITE_MAX_UINT); -} - -static struct account *stmt2account(const tal_t *ctx, struct db_stmt *stmt) -{ - struct account *a = tal(ctx, struct account); - - a->db_id = db_col_u64(stmt, "id"); - a->name = db_col_strdup(a, stmt, "name"); - - if (!db_col_is_null(stmt, "peer_id")) { - a->peer_id = tal(a, struct node_id); - db_col_node_id(stmt, "peer_id", a->peer_id); - } else - a->peer_id = NULL; - a->is_wallet = db_col_int(stmt, "is_wallet") != 0; - a->we_opened = db_col_int(stmt, "we_opened") != 0; - a->leased = db_col_int(stmt, "leased") != 0; - - if (!db_col_is_null(stmt, "onchain_resolved_block")) { - a->onchain_resolved_block = db_col_int(stmt, "onchain_resolved_block"); - } else - a->onchain_resolved_block = 0; - - if (!db_col_is_null(stmt, "opened_event_id")) { - a->open_event_db_id = tal(a, u64); - *a->open_event_db_id = db_col_u64(stmt, "opened_event_id"); - } else - a->open_event_db_id = NULL; - - if (!db_col_is_null(stmt, "closed_event_id")) { - a->closed_event_db_id = tal(a, u64); - *a->closed_event_db_id = db_col_u64(stmt, "closed_event_id"); - } else - a->closed_event_db_id = NULL; - - a->closed_count = db_col_int(stmt, "closed_count"); - - return a; -} - -struct account *find_account(const tal_t *ctx, - struct db *db, - const char *name) -{ - struct db_stmt *stmt; - struct account *a; - - stmt = db_prepare_v2(db, SQL("SELECT" - " id" - ", name" - ", peer_id" - ", opened_event_id" - ", closed_event_id" - ", onchain_resolved_block" - ", is_wallet" - ", we_opened" - ", leased" - ", closed_count" - " FROM accounts" - " WHERE name = ?")); - - db_bind_text(stmt, name); - db_query_prepared(stmt); - - if (db_step(stmt)) - a = stmt2account(ctx, stmt); - else - a = NULL; - - tal_free(stmt); - - return a; -} - -struct onchain_fee **account_onchain_fees(const tal_t *ctx, - struct db *db, - struct account *acct) -{ - struct db_stmt *stmt; - - stmt = db_prepare_v2(db, SQL("SELECT" - " of.account_id" - ", a.name" - ", of.txid" - ", of.credit" - ", of.debit" - ", of.timestamp" - ", of.update_count" - " FROM onchain_fees of" - " LEFT OUTER JOIN accounts a" - " ON a.id = of.account_id" - " WHERE of.account_id = ?;")); - - db_bind_u64(stmt, acct->db_id); - return find_onchain_fees(ctx, take(stmt)); -} - -struct account **list_accounts(const tal_t *ctx, struct db *db) -{ - struct db_stmt *stmt; - struct account **results; - - stmt = db_prepare_v2(db, SQL("SELECT" - " id" - ", name" - ", peer_id" - ", opened_event_id" - ", closed_event_id" - ", onchain_resolved_block" - ", is_wallet" - ", we_opened" - ", leased" - ", closed_count" - " FROM accounts;")); - db_query_prepared(stmt); - - results = tal_arr(ctx, struct account *, 0); - while (db_step(stmt)) { - struct account *a = stmt2account(results, stmt); - tal_arr_expand(&results, a); - } - tal_free(stmt); - - return results; -} - -void account_add(struct db *db, struct account *acct) -{ - struct db_stmt *stmt; - - stmt = db_prepare_v2(db, SQL("INSERT INTO accounts" - " (" - " name" - ", peer_id" - ", is_wallet" - ", we_opened" - ", leased" - ")" - " VALUES" - " (?, ?, ?, ?, ?);")); - - db_bind_text(stmt, acct->name); - if (acct->peer_id) - db_bind_node_id(stmt, acct->peer_id); - else - db_bind_null(stmt); - db_bind_int(stmt, acct->is_wallet ? 1 : 0); - db_bind_int(stmt, acct->we_opened ? 1 : 0); - db_bind_int(stmt, acct->leased ? 1 : 0); - - db_exec_prepared_v2(stmt); - acct->db_id = db_last_insert_id_v2(stmt); - tal_free(stmt); -} - -void maybe_update_account(struct db *db, - struct account *acct, - struct chain_event *e, - const enum mvt_tag *tags, - u32 closed_count, - struct node_id *peer_id) -{ - struct db_stmt *stmt; - bool updated = false; - - for (size_t i = 0; i < tal_count(tags); i++) { - switch (tags[i]) { - case MVT_CHANNEL_PROPOSED: - case MVT_CHANNEL_OPEN: - updated = true; - acct->open_event_db_id = tal(acct, u64); - *acct->open_event_db_id = e->db_id; - break; - case MVT_CHANNEL_CLOSE: - /* Splices dont count as closes */ - if (e->splice_close) - break; - updated = true; - acct->closed_event_db_id = tal(acct, u64); - *acct->closed_event_db_id = e->db_id; - break; - case MVT_LEASED: - updated = true; - acct->leased = true; - break; - case MVT_OPENER: - updated = true; - acct->we_opened = true; - break; - case MVT_DEPOSIT: - case MVT_WITHDRAWAL: - case MVT_PENALTY: - case MVT_INVOICE: - case MVT_ROUTED: - case MVT_PUSHED: - case MVT_CHANNEL_TO_US: - case MVT_HTLC_TIMEOUT: - case MVT_HTLC_FULFILL: - case MVT_HTLC_TX: - case MVT_TO_WALLET: - case MVT_ANCHOR: - case MVT_TO_THEM: - case MVT_PENALIZED: - case MVT_STOLEN: - case MVT_TO_MINER: - case MVT_LEASE_FEE: - case MVT_STEALABLE: - case MVT_SPLICE: - case MVT_PENALTY_ADJ: - case MVT_JOURNAL: - /* Ignored */ - break; - } - } - - if (peer_id) { - updated = true; - acct->peer_id = tal_dup(acct, struct node_id, peer_id); - } - - if (!e->splice_close && closed_count > 0) { - updated = true; - acct->closed_count = closed_count; - } - - /* Nothing new here */ - if (!updated) - return; - - /* Otherwise, we update the account ! */ - stmt = db_prepare_v2(db, SQL("UPDATE accounts SET" - " opened_event_id = ?" - ", closed_event_id = ?" - ", we_opened = ?" - ", leased = ?" - ", closed_count = ?" - ", peer_id = ?" - " WHERE" - " name = ?")); - - if (acct->open_event_db_id) - db_bind_u64(stmt, *acct->open_event_db_id); - else - db_bind_null(stmt); - - if (acct->closed_event_db_id) - db_bind_u64(stmt, *acct->closed_event_db_id); - else - db_bind_null(stmt); - - db_bind_int(stmt, acct->we_opened ? 1 : 0); - db_bind_int(stmt, acct->leased ? 1 : 0); - db_bind_int(stmt, acct->closed_count); - if (acct->peer_id) - db_bind_node_id(stmt, acct->peer_id); - else - db_bind_null(stmt); - - db_bind_text(stmt, acct->name); - - db_exec_prepared_v2(take(stmt)); -} - -void log_channel_event(struct db *db, - const struct account *acct, - struct channel_event *e) -{ - struct db_stmt *stmt; - - stmt = db_prepare_v2(db, SQL("INSERT INTO channel_events" - " (" - " account_id" - ", tag" - ", credit" - ", debit" - ", fees" - ", payment_id" - ", part_id" - ", timestamp" - ", ev_desc" - ", rebalance_id" - ")" - " VALUES" - " (?, ?, ?, ?, ?, ?, ?, ?, ?, ?);")); - - db_bind_u64(stmt, acct->db_id); - db_bind_text(stmt, e->tag); - db_bind_amount_msat(stmt, e->credit); - db_bind_amount_msat(stmt, e->debit); - db_bind_amount_msat(stmt, e->fees); - if (e->payment_id) - db_bind_sha256(stmt, e->payment_id); - else - db_bind_null(stmt); - db_bind_int(stmt, e->part_id); - db_bind_u64(stmt, e->timestamp); - if (e->desc) - db_bind_text(stmt, e->desc); - else - db_bind_null(stmt); - - if (e->rebalance_id) - db_bind_u64(stmt, *e->rebalance_id); - else - db_bind_null(stmt); - - db_exec_prepared_v2(stmt); - e->db_id = db_last_insert_id_v2(stmt); - e->acct_db_id = acct->db_id; - e->acct_name = tal_strdup(e, acct->name); - tal_free(stmt); -} - -struct chain_event **find_chain_events_bytxid(const tal_t *ctx, struct db *db, - struct bitcoin_txid *txid) -{ - 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.timestamp" - ", e.blockheight" - ", e.utxo_txid" - ", e.outnum" - ", e.spending_txid" - ", e.payment_id" - ", e.stealable" - ", e.ev_desc" - ", e.spliced" - " FROM chain_events e" - " LEFT OUTER JOIN accounts a" - " ON a.id = e.account_id" - " WHERE e.spending_txid = ?" - " OR (e.utxo_txid = ? AND e.spending_txid IS NULL)" - " ORDER BY e.account_id")); - - db_bind_txid(stmt, txid); - db_bind_txid(stmt, txid); - return find_chain_events(ctx, take(stmt)); -} - -static u64 find_acct_id(struct db *db, const char *name) -{ - u64 acct_id; - struct db_stmt *stmt; - - stmt = db_prepare_v2(db, SQL("SELECT" - " id" - " FROM accounts" - " WHERE name = ?")); - - db_bind_text(stmt, name); - db_query_prepared(stmt); - if (db_step(stmt)) - acct_id = db_col_u64(stmt, "id"); - else - acct_id = 0; - - tal_free(stmt); - return acct_id; -} - -static void insert_chain_fees_diff(struct db *db, - u64 acct_id, - struct bitcoin_txid *txid, - struct amount_msat amount, - u64 timestamp) -{ - struct db_stmt *stmt; - u32 update_count; - struct amount_msat current_amt, credit, debit; - - /* First, look to see if there's an already existing - * record to update */ - stmt = db_prepare_v2(db, SQL("SELECT" - " update_count" - ", credit" - ", debit" - " FROM onchain_fees" - " WHERE txid = ?" - " AND account_id = ?" - " ORDER BY update_count")); - - db_bind_txid(stmt, txid); - db_bind_u64(stmt, acct_id); - db_query_prepared(stmt); - - /* If there's no current record, add it */ - current_amt = AMOUNT_MSAT(0); - update_count = 0; - while (db_step(stmt)) { - update_count = db_col_int(stmt, "update_count"); - credit = db_col_amount_msat(stmt, "credit"); - debit = db_col_amount_msat(stmt, "debit"); - - /* These should apply perfectly, as we sorted them by - * insert order */ - if (!amount_msat_accumulate(¤t_amt, credit)) - db_fatal(db, "Overflow when adding onchain fees"); - - if (!amount_msat_sub(¤t_amt, current_amt, debit)) - db_fatal(db, "Underflow when subtracting onchain fees"); - - } - tal_free(stmt); - - /* If they're already equal, no need to update */ - if (amount_msat_eq(current_amt, amount)) - return; - - if (!amount_msat_sub(&credit, amount, current_amt)) { - credit = AMOUNT_MSAT(0); - if (!amount_msat_sub(&debit, current_amt, amount)) - db_fatal(db, "shouldn't happen, unable to subtract"); - } else - debit = AMOUNT_MSAT(0); - - stmt = db_prepare_v2(db, SQL("INSERT INTO onchain_fees" - " (" - " account_id" - ", txid" - ", credit" - ", debit" - ", timestamp" - ", update_count" - ") VALUES" - " (?, ?, ?, ?, ?, ?);")); - - db_bind_u64(stmt, acct_id); - db_bind_txid(stmt, txid); - db_bind_amount_msat(stmt, credit); - db_bind_amount_msat(stmt, debit); - db_bind_u64(stmt, timestamp); - db_bind_int(stmt, ++update_count); - db_exec_prepared_v2(take(stmt)); -} - -char *update_channel_onchain_fees(const tal_t *ctx, - struct db *db, - struct account *acct) -{ - struct chain_event *close_ev, **events; - struct amount_msat onchain_amt; - - assert(acct->onchain_resolved_block); - close_ev = find_chain_event_by_id(ctx, db, - *acct->closed_event_db_id); - events = find_chain_events_bytxid(ctx, db, - close_ev->spending_txid); - - /* Starting balance is close-ev's debit amount */ - onchain_amt = AMOUNT_MSAT(0); - for (size_t i = 0; i < tal_count(events); i++) { - struct chain_event *ev = events[i]; - - /* Ignore: - - htlc_fufill (to me) - - anchors (already exlc from output) - - to_external (if !htlc_fulfill) - */ - if (is_channel_account(ev->acct_name) - && streq("htlc_fulfill", ev->tag)) - continue; - - if (streq("anchor", ev->tag)) - continue; - - /* Ignore stuff which is paid to - * the peer's account (external), - * except for fulfilled htlcs (which originated - * in our balance) */ - if (is_external_account(ev->acct_name) - && !streq("htlc_fulfill", ev->tag)) - continue; - - /* anything else we count? */ - if (!amount_msat_accumulate(&onchain_amt, ev->credit)) - return tal_fmt(ctx, "Unable to add" - "onchain + %s's credit", - ev->tag); - } - - if (amount_msat_less_eq(onchain_amt, close_ev->debit)) { - struct amount_msat fees; - if (!amount_msat_sub(&fees, close_ev->debit, - onchain_amt)) - return tal_fmt(ctx, "Unable to sub" - "onchain sum from %s", - close_ev->tag); - - insert_chain_fees_diff(db, acct->db_id, - close_ev->spending_txid, - fees, - close_ev->timestamp); - } - - return NULL; -} - -static char *is_closed_channel_txid(const tal_t *ctx, struct db *db, - struct chain_event *ev, - struct bitcoin_txid *txid, - bool *is_channel_close_tx) -{ - struct account *acct; - struct chain_event *closed; - u8 *inner_ctx = tal(NULL, u8); - - /* Figure out if this is a channel close tx */ - acct = find_account(inner_ctx, db, ev->acct_name); - assert(acct); - - /* There's a separate process for figuring out - * our onchain fees for channel closures */ - if (!acct->closed_event_db_id) { - *is_channel_close_tx = false; - tal_free(inner_ctx); - return NULL; - } - - /* is the closed utxo the same as the one - * we're trying to find fees for now */ - closed = find_chain_event_by_id(inner_ctx, db, - *acct->closed_event_db_id); - if (!closed) { - *is_channel_close_tx = false; - tal_free(inner_ctx); - return tal_fmt(ctx, "Unable to find" - " db record (chain_evt)" - " with id %"PRIu64, - *acct->closed_event_db_id); - } - - if (!closed->spending_txid) { - *is_channel_close_tx = false; - tal_free(inner_ctx); - return tal_fmt(ctx, "Marked a closing" - " event that's not" - " actually a spend"); - } - - *is_channel_close_tx = - bitcoin_txid_eq(txid, closed->spending_txid); - tal_free(inner_ctx); - return NULL; -} - -void maybe_record_rebalance(struct db *db, - struct channel_event *out) + const struct bkpr *bkpr, + struct command *cmd, + const struct sha256 *id) +{ + return channel_events_from_sql(ctx, cmd, + SELECT_CHANNEL_EVENTS + " WHERE payment_hash = X'%s'" + " AND created_index <= %"PRIu64 + " ORDER BY timestamp, created_index", + fmt_sha256(tmpctx, id), + bkpr->channelmoves_index); +} + +struct chain_event **find_chain_events_bytxid(const tal_t *ctx, + const struct bkpr *bkpr, + struct command *cmd, + const struct bitcoin_txid *txid) +{ + return chain_events_from_sql(ctx, bkpr, cmd, + SELECT_CHAIN_EVENTS + " WHERE created_index <= %"PRIu64 + " AND (spending_txid = X'%s'" + " OR (utxo LIKE '%s%%' AND spending_txid IS NULL))" + " ORDER BY account_id, created_index", + bkpr->chainmoves_index, + fmt_bitcoin_txid(tmpctx, txid), /* spending_txid match */ + fmt_bitcoin_txid(tmpctx, txid)); /* utxo prefix (txid:*) */ +} + +void maybe_record_rebalance(struct command *cmd, + struct bkpr *bkpr, + const struct channel_event *out) { /* If there's a matching credit event, this is * a rebalance. Mark everything with the payment_id * and amt as such. If you repeat a payment_id * with the same amt, they'll be marked as rebalances * also */ - struct db_stmt *stmt; + const char *buf; + const jsmntok_t *res, *rows, *row, *val; + size_t i; struct amount_msat credit; bool ok; @@ -1769,351 +545,64 @@ void maybe_record_rebalance(struct db *db, ok = amount_msat_sub(&credit, out->debit, out->fees); assert(ok); - stmt = db_prepare_v2(db, SQL("SELECT " - " e.id" - " FROM channel_events e" - " WHERE e.payment_id = ?" - " AND e.credit = ?" - " AND e.rebalance_id IS NULL")); - - db_bind_sha256(stmt, out->payment_id); - db_bind_amount_msat(stmt, credit); - db_query_prepared(stmt); - - if (!db_step(stmt)) { - /* No matching invoice found */ - tal_free(stmt); - return; - } - - /* We just take the first one */ - out->rebalance_id = tal(out, u64); - *out->rebalance_id = db_col_u64(stmt, "e.id"); - tal_free(stmt); - - /* Set rebalance flag on both records */ - stmt = db_prepare_v2(db, SQL("UPDATE channel_events SET" - " rebalance_id = ?" - " WHERE" - " id = ?")); - db_bind_u64(stmt, *out->rebalance_id); - db_bind_u64(stmt, out->db_id); - db_exec_prepared_v2(take(stmt)); - - stmt = db_prepare_v2(db, SQL("UPDATE channel_events SET" - " rebalance_id = ?" - " WHERE" - " id = ?")); - db_bind_u64(stmt, out->db_id); - db_bind_u64(stmt, *out->rebalance_id); - db_exec_prepared_v2(take(stmt)); -} - -struct rebalance **list_rebalances(const tal_t *ctx, struct db *db) -{ - struct rebalance **result; - struct db_stmt *stmt; - - stmt = db_prepare_v2(db, SQL("SELECT " - " in_e.id" - ", out_e.id" - ", in_acct.name" - ", out_acct.name" - ", in_e.credit" - ", out_e.fees" - " FROM channel_events in_e" - " LEFT OUTER JOIN channel_events out_e" - " ON in_e.rebalance_id = out_e.id" - " LEFT OUTER JOIN accounts out_acct" - " ON out_acct.id = out_e.account_id" - " LEFT OUTER JOIN accounts in_acct" - " ON in_acct.id = in_e.account_id" - " WHERE in_e.rebalance_id IS NOT NULL" - " AND in_e.credit > 0")); - db_query_prepared(stmt); - result = tal_arr(ctx, struct rebalance *, 0); - while (db_step(stmt)) { - struct rebalance *r = stmt2rebalance(result, stmt); - tal_arr_expand(&result, r); - } - tal_free(stmt); - return result; -} - -char *maybe_update_onchain_fees(const tal_t *ctx, struct db *db, - struct bitcoin_txid *txid) -{ - size_t no_accts = 0, plus_ones; - u64 last_id = 0, wallet_id, extern_id; - bool contains_wallet = false, skip_wallet = false; - struct chain_event **events; - struct amount_msat deposit_msat = AMOUNT_MSAT(0), - withdraw_msat = AMOUNT_MSAT(0), - fees_msat, fee_part_msat; - char *err = NULL; - u8 *inner_ctx = tal(NULL, u8); - - /* Find all the deposits/withdrawals for this txid */ - events = find_chain_events_bytxid(inner_ctx, db, txid); - wallet_id = find_acct_id(db, ACCOUNT_NAME_WALLET); - extern_id = find_acct_id(db, ACCOUNT_NAME_EXTERNAL); - - /* If we don't even have two events, skip */ - if (tal_count(events) < 2) - goto finished; - - for (size_t i = 0; i < tal_count(events); i++) { - bool is_channel_close_tx; - err = is_closed_channel_txid(ctx, db, - events[i], txid, - &is_channel_close_tx); - - if (err) - goto finished; - - /* We skip channel close txs here! */ - if (is_channel_close_tx) - goto finished; - - if (events[i]->spending_txid) { - if (!amount_msat_accumulate(&withdraw_msat, - events[i]->debit)) { - err = tal_fmt(ctx, "Overflow adding withdrawal debits for" - " txid: %s", - fmt_bitcoin_txid(ctx, - txid)); - goto finished; - } - } else { - if (!amount_msat_accumulate(&deposit_msat, - events[i]->credit)) { - err = tal_fmt(ctx, "Overflow adding deposit credits for" - " txid: %s", - fmt_bitcoin_txid(ctx, - txid)); - goto finished; - } - } - - /* While we're here, also count number of accts - * that were involved! Two little tricks here. - * - * One) we sorted the output - * by acct id, so we can cheat how we count: if - * it's a different acct_id than the last seen, we inc - * the counter. - * - * Two) who "gets" fee attribution is complicated - * and requires knowing if the wallet/external accts - * were involved (everything else is channel accts) - * */ - if (last_id != events[i]->acct_db_id) { - last_id = events[i]->acct_db_id; - /* Don't count external accts */ - if (last_id != extern_id) - no_accts++; - - contains_wallet |= (last_id == wallet_id); - } - } - - /* Only affects external accounts, we can ignore */ - if (no_accts == 0) - goto finished; - - /* If either is zero, keep waiting */ - if (amount_msat_is_zero(withdraw_msat) - || amount_msat_is_zero(deposit_msat)) - goto finished; - - /* If our withdraws < deposits, wait for more data */ - if (amount_msat_less(withdraw_msat, deposit_msat)) - goto finished; - - if (!amount_msat_sub(&fees_msat, withdraw_msat, deposit_msat)) { - err = tal_fmt(ctx, "Err subtracting withdraw %s from deposit %s" - " for txid %s", - fmt_amount_msat(ctx, withdraw_msat), - fmt_amount_msat(ctx, deposit_msat), - fmt_bitcoin_txid(ctx, txid)); - goto finished; - } - - /* Now we need to figure out how to allocate fees to each account - * that was involved in the tx. This is a lil complex, buckle up*/ - - /* If the wallet's involved + there were any other accounts, decr by one */ - if (no_accts > 1 && contains_wallet) { - skip_wallet = true; - no_accts--; - } - - /* Now we divide by the number of accts involved, to figure out the - * value to log for each account! */ - fee_part_msat = amount_msat_div(fees_msat, no_accts); - - /* So we don't lose any msats b/c of rounding, find the number of - * accts to add an extra msat onto */ - plus_ones = fees_msat.millisatoshis % no_accts; /* Raw: mod calc */ - - /* Now we log (or update the existing record) for each acct */ - last_id = 0; - for (size_t i = 0; i < tal_count(events); i++) { - struct amount_msat fees; - - if (last_id == events[i]->acct_db_id) - continue; - - last_id = events[i]->acct_db_id; - - /* We *never* assign fees to external accounts; - * if external funds were contributed to a tx - * we wouldn't record it -- fees are solely ours */ - if (last_id == extern_id) - continue; + /* Look for a matching credit-side event for the same payment */ + res = sql_req(tmpctx, cmd, &buf, + "SELECT created_index" + " FROM channelmoves" + " WHERE payment_hash = X'%s'" + " AND credit_msat = %"PRIu64 + " AND created_index <= %"PRIu64, + fmt_sha256(tmpctx, out->payment_id), + credit.millisatoshis /* Raw: sql query */, + bkpr->channelmoves_index); + + rows = json_get_member(buf, res, "rows"); + json_for_each_arr(i, row, rows) { + u64 id; + val = row + 1; /* single column */ + ok = json_to_u64(buf, val, &id); + assert(ok); - /* We only attribute fees to the wallet - * if the wallet is the only game in town */ - if (skip_wallet && last_id == wallet_id) { - /* But we might need to clean up any fees assigned - * to the wallet from a previous round, where it - * *was* the only game in town */ - insert_chain_fees_diff(db, last_id, txid, - AMOUNT_MSAT(0), - events[i]->timestamp); + /* Already has one? */ + if (find_rebalance(bkpr, id)) continue; - } - - /* Add an extra msat onto plus_ones accts - * so we don't lose any precision in - * our accounting */ - if (plus_ones > 0) { - plus_ones--; - if (!amount_msat_add(&fees, fee_part_msat, - AMOUNT_MSAT(1))) { - err = "Overflow adding 1 ... yeah right"; - /* We're gonna keep going, yolo */ - fees = fee_part_msat; - } - } else - fees = fee_part_msat; - - insert_chain_fees_diff(db, last_id, txid, fees, - events[i]->timestamp); + add_rebalance_pair(cmd, bkpr, out->db_id, id); + break; } - -finished: - tal_free(inner_ctx); - return err; } -void maybe_closeout_external_deposits(struct db *db, - const struct bitcoin_txid *txid, +void maybe_closeout_external_deposits(struct command *cmd, + struct bkpr *bkpr, + const struct bitcoin_txid *txid, u32 blockheight) { - struct db_stmt *stmt; - - assert(txid); - stmt = db_prepare_v2(db, SQL("SELECT " - " e.id" - " FROM chain_events e" - " LEFT OUTER JOIN accounts a" - " ON e.account_id = a.id" - " WHERE e.blockheight = ?" - " AND e.utxo_txid = ?" - " AND a.name = ?")); - - /* Blockheight for unconfirmeds is zero */ - db_bind_int(stmt, 0); - db_bind_txid(stmt, txid); - db_bind_text(stmt, ACCOUNT_NAME_EXTERNAL); - db_query_prepared(stmt); - - while (db_step(stmt)) { - struct db_stmt *update_stmt; - u64 id; - - id = db_col_u64(stmt, "e.id"); - update_stmt = db_prepare_v2(db, SQL("UPDATE chain_events SET" - " blockheight = ?" - " WHERE id = ?")); + const char *buf; + const jsmntok_t *res, *rows, *row; + size_t i; + + /* Find any unconfirmed external deposits for this txid. */ + res = sql_req(tmpctx, cmd, &buf, + "SELECT utxo" + " FROM chainmoves" + " WHERE blockheight = 0" + " AND utxo LIKE '%s:%%'" + " AND account_id = '%s'" + " AND created_index <= %"PRIu64, + /* utxo is ':' so we prefix-match on txid: */ + fmt_bitcoin_txid(tmpctx, txid), + sql_string(tmpctx, ACCOUNT_NAME_EXTERNAL), + bkpr->chainmoves_index); + + rows = json_get_member(buf, res, "rows"); + json_for_each_arr(i, row, rows) { + const jsmntok_t *val = row + 1; /* single column */ + struct bitcoin_outpoint outp; + bool ok; - db_bind_int(update_stmt, blockheight); - db_bind_u64(update_stmt, id); - db_exec_prepared_v2(take(update_stmt)); + ok = json_to_outpoint(buf, val, &outp); + assert(ok); + add_blockheight(cmd, bkpr, &outp.txid, blockheight); } - - tal_free(stmt); } -bool log_chain_event(struct db *db, - const struct account *acct, - struct chain_event *e) -{ - struct db_stmt *stmt; - - /* We're responsible for de-duping chain events! */ - if (find_chain_event(tmpctx, db, acct, - &e->outpoint, e->spending_txid, - e->tag)) - return false; - - stmt = db_prepare_v2(db, SQL("INSERT INTO chain_events" - " (" - " account_id" - ", origin" - ", tag" - ", credit" - ", debit" - ", output_value" - ", timestamp" - ", blockheight" - ", utxo_txid" - ", outnum" - ", payment_id" - ", spending_txid" - ", stealable" - ", ev_desc" - ", spliced" - ")" - " VALUES " - "(?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?);")); - - db_bind_u64(stmt, acct->db_id); - if (e->origin_acct) - db_bind_text(stmt, e->origin_acct); - else - db_bind_null(stmt); - db_bind_text(stmt, e->tag); - db_bind_amount_msat(stmt, e->credit); - db_bind_amount_msat(stmt, e->debit); - db_bind_amount_msat(stmt, e->output_value); - db_bind_u64(stmt, e->timestamp); - db_bind_int(stmt, e->blockheight); - db_bind_txid(stmt, &e->outpoint.txid); - db_bind_int(stmt, e->outpoint.n); - - if (e->payment_id) - db_bind_sha256(stmt, e->payment_id); - else - db_bind_null(stmt); - - if (e->spending_txid) - db_bind_txid(stmt, e->spending_txid); - else - db_bind_null(stmt); - - db_bind_int(stmt, e->stealable ? 1 : 0); - if (e->desc) - db_bind_text(stmt, e->desc); - else - db_bind_null(stmt); - db_bind_int(stmt, e->splice_close ? 1 : 0); - db_exec_prepared_v2(stmt); - e->db_id = db_last_insert_id_v2(stmt); - e->acct_db_id = acct->db_id; - e->acct_name = tal_strdup(e, acct->name); - tal_free(stmt); - return true; -} diff --git a/plugins/bkpr/recorder.h b/plugins/bkpr/recorder.h index 1e508889c09b..3c807a7e14c1 100644 --- a/plugins/bkpr/recorder.h +++ b/plugins/bkpr/recorder.h @@ -8,7 +8,6 @@ struct account; struct bitcoin_txid; struct chain_event; struct channel_event; -struct db; struct plugin; enum mvt_tag; struct onchain_fee; @@ -16,8 +15,7 @@ struct onchain_fee; #define SQLITE_MAX_UINT 0x7FFFFFFFFFFFFFFF struct fee_sum { - u64 acct_db_id; - char *acct_name; + const char *acct_name; struct bitcoin_txid *txid; struct amount_msat fees_paid; }; @@ -32,103 +30,89 @@ struct txo_set { struct txo_pair **pairs; }; -struct rebalance { - u64 in_ev_id; - u64 out_ev_id; - char *in_acct_name; - char *out_acct_name; - struct amount_msat rebal_msat; - struct amount_msat fee_msat; -}; - -/* Get all accounts */ -struct account **list_accounts(const tal_t *ctx, struct db *db); - -/* Get all onchain fee records for this account */ -struct onchain_fee **account_onchain_fees(const tal_t *ctx, - struct db *db, - struct account *acct); - /* Get all channel events for this account */ struct channel_event **account_get_channel_events(const tal_t *ctx, - struct db *db, + const struct bkpr *bkpr, + struct command *cmd, struct account *acct); /* Get all channel events for a payment id, order by timestamp */ struct channel_event **get_channel_events_by_id(const tal_t *ctx, - struct db *db, - struct sha256 *id); + const struct bkpr *bkpr, + struct command *cmd, + const struct sha256 *id); /* Get all channel events, ordered by timestamp */ -struct channel_event **list_channel_events(const tal_t *ctx, struct db *db); +struct channel_event **list_channel_events(const tal_t *ctx, + const struct bkpr *bkpr, + struct command *cmd); /* Get all channel events, order by timestamp. * * @ctx - context to allocate from - * @db - database to query * @start_time - UNIX timestamp to query after (exclusive) * @end_time - UNIX timestamp to query until (inclusive) */ struct channel_event **list_channel_events_timebox(const tal_t *ctx, - struct db *db, + const struct bkpr *bkpr, + struct command *cmd, u64 start_time, u64 end_time); /* Get all chain events for this account */ struct chain_event **account_get_chain_events(const tal_t *ctx, - struct db *db, + const struct bkpr *bkpr, + struct command *cmd, struct account *acct); /* Get all chain events for a transaction id, order by timestamp */ -struct chain_event **find_chain_events_bytxid(const tal_t *ctx, struct db *db, - struct bitcoin_txid *txid); +struct chain_event **find_chain_events_bytxid(const tal_t *ctx, + const struct bkpr *bkpr, + struct command *cmd, + const struct bitcoin_txid *txid); /* Get all chain events, order by timestamp. */ -struct chain_event **list_chain_events(const tal_t *ctx, struct db *db); +struct chain_event **list_chain_events(const tal_t *ctx, + const struct bkpr *bkpr, + struct command *cmd); /* Get all chain events, order by timestamp. * * @ctx - context to allocate from - * @db - database to query * @start_time - UNIX timestamp to query after (exclusive) * @end_time - UNIX timestamp to query until (inclusive) */ struct chain_event **list_chain_events_timebox(const tal_t *ctx, - struct db *db, + const struct bkpr *bkpr, + struct command *cmd, 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); + const struct bkpr *bkpr, + struct command *cmd, + 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); + const struct bkpr *bkpr, + struct command *cmd, + const struct bitcoin_outpoint *outpoint); /* Get total credits and debits for this account: returns false if no entries at all * (in which case, credit and debit will both be AMOUNT_MSAT(0)). */ -bool account_get_credit_debit(struct plugin *plugin, - struct db *db, +bool account_get_credit_debit(const struct bkpr *bkpr, + struct command *cmd, const char *acct_name, struct amount_msat *credit, struct amount_msat *debit); -/* Get chain fees for account */ -struct onchain_fee **account_get_chain_fees(const tal_t *ctx, struct db *db, - struct account *acct); - -/* Get all chain fees for a transaction id, order by timestamp */ -struct onchain_fee **get_chain_fees_by_txid(const tal_t *ctx, struct db *db, - struct bitcoin_txid *txid); - /* Find a chain event by its database id */ struct chain_event *find_chain_event_by_id(const tal_t *ctx, - struct db *db, + const struct bkpr *bkpr, + struct command *cmd, u64 event_db_id); /* Find the utxos for this account. @@ -137,116 +121,40 @@ struct chain_event *find_chain_event_by_id(const tal_t *ctx, * (all outputs terminate either to wallet or external) */ bool find_txo_chain(const tal_t *ctx, - struct db *db, - struct account *acct, + const struct bkpr *bkpr, + struct command *cmd, + const struct account *acct, struct txo_set ***sets); -/* List all chain fees, for all accounts */ -struct onchain_fee **list_chain_fees(const tal_t *ctx, struct db *db); - -/* Get all chain fees, order by timestamp. - * - * @ctx - context to allocate from - * @db - database to query - * @start_time - UNIX timestamp to query after (exclusive) - * @end_time - UNIX timestamp to query until (inclusive) - */ -struct onchain_fee **list_chain_fees_timebox(const tal_t *ctx, struct db *db, - u64 start_time, u64 end_time); - -/* Returns a list of sums of the fees we've recorded for every txid - * for the given account */ -struct fee_sum **find_account_onchain_fees(const tal_t *ctx, - struct db *db, - struct account *acct); - -/* Final all the onchain fees */ -struct fee_sum **calculate_onchain_fee_sums(const tal_t *ctx, struct db *db); - -/* Find the last timestamp for the onchain fees for this txid + account */ -u64 onchain_fee_last_timestamp(struct db *db, - u64 acct_db_id, - struct bitcoin_txid *txid); -/* Add the given account to the database */ -void account_add(struct db *db, struct account *acct); -/* Given an account name, find that account record */ -struct account *find_account(const tal_t *ctx, - struct db *db, - const char *name); - /* Find the account that was closed by this txid. * Returns NULL if none */ -struct account *find_close_account(const tal_t *ctx, - struct db *db, - struct bitcoin_txid *txid); - -/* Some events update account information */ -void maybe_update_account(struct db *db, - struct account *acct, - struct chain_event *e, - const enum mvt_tag *tags, - u32 closed_count, - struct node_id *peer_id); - -/* Update our onchain fees now? */ -char *maybe_update_onchain_fees(const tal_t *ctx, - struct db *db, - struct bitcoin_txid *txid); - -/* We calculate onchain fees for channel closes a bit different */ -char *update_channel_onchain_fees(const tal_t *ctx, - struct db *db, - struct account *acct); +const char *find_close_account_name(const tal_t *ctx, + const struct bkpr *bkpr, + struct command *cmd, + const struct bitcoin_txid *txid); /* Have all the outputs for this account's close tx - * been resolved onchain? If so, update the account with the + * been resolved onchain? If so, return the * highest blockheight that has a resolving tx in it. * * The point of this is to allow us to prune data, eventually */ -void maybe_mark_account_onchain(struct db *db, struct account *acct); - -/* We fetch invoice desc data after the fact and then update it - * Updates both the chain_event and channel_event tables for all - * matching payment_hashes - * */ -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); +u64 account_onchain_closeheight(const struct bkpr *bkpr, + struct command *cmd, + const struct account *acct); /* When we make external deposits from the wallet, we don't * count them until any output that was spent *into* them is * confirmed onchain. * - * This method updates the blockheight on these events to the + * This method updates bkpr->blockheights to show the * height an input was spent into */ -void maybe_closeout_external_deposits(struct db *db, +void maybe_closeout_external_deposits(struct command *cmd, + struct bkpr *bkpr, const struct bitcoin_txid *txid, u32 blockheight); -/* Keep track of rebalancing payments (payments paid to/from ourselves. - * Returns true if was rebalance */ -void maybe_record_rebalance(struct db *db, - struct channel_event *out); - -/* List all rebalances */ -struct rebalance **list_rebalances(const tal_t *ctx, struct db *db); - -/* Log a channel event */ -void log_channel_event(struct db *db, - const struct account *acct, - struct channel_event *e); - -/* Log a chain event. - * Returns true if inserted, false if already exists; - * ctx is for allocating objects onto chain_event `e` */ -bool log_chain_event(struct db *db, - const struct account *acct, - struct chain_event *e); - +/* Keep track of rebalancing payments (payments paid to/from ourselves. */ +void maybe_record_rebalance(struct command *cmd, + struct bkpr *bkpr, + const struct channel_event *out); #endif /* LIGHTNING_PLUGINS_BKPR_RECORDER_H */ diff --git a/plugins/bkpr/sql.c b/plugins/bkpr/sql.c new file mode 100644 index 000000000000..61f0001d8df4 --- /dev/null +++ b/plugins/bkpr/sql.c @@ -0,0 +1,218 @@ +#include "config.h" + +#include +#include +#include +#include +#include +#include + +const jsmntok_t *sql_req(const tal_t *ctx, + struct command *cmd, + const char **buf, + const char *fmt, ...) +{ + va_list ap; + const jsmntok_t *ret; + + va_start(ap, fmt); + ret = sql_reqv(ctx, cmd, buf, fmt, ap); + va_end(ap); + return ret; +} + +const jsmntok_t *sql_reqv(const tal_t *ctx, + struct command *cmd, + const char **buf, + const char *fmt, va_list ap) +{ + struct json_out *params; + + params = json_out_obj(NULL, "query", take(tal_vfmt(NULL, fmt, ap))); + + return jsonrpc_request_sync(ctx, cmd, "sql", take(params), buf); +} + +static struct channel_event **channel_events(const tal_t *ctx, + const char *buf, + const jsmntok_t *result) +{ + struct channel_event **evs; + size_t i; + const jsmntok_t *row, *rows = json_get_member(buf, result, "rows"); + + evs = tal_arr(ctx, struct channel_event *, rows->size); + json_for_each_arr(i, row, rows) { + bool ok = true; + struct channel_event *ev; + u64 created_index; + const char *account_name; + const char *primary_tag; + struct amount_msat credit, debit, fees; + struct sha256 *payment_id, payment_hash; + u64 part_id, timestamp; + + const jsmntok_t *val = row + 1; + assert(row->size == 9); + ok &= json_to_u64(buf, val, &created_index); + val = json_next(val); + account_name = json_strdup(NULL, buf, val); + val = json_next(val); + primary_tag = json_strdup(NULL, buf, val); + val = json_next(val); + ok &= json_to_msat(buf, val, &credit); + val = json_next(val); + ok &= json_to_msat(buf, val, &debit); + val = json_next(val); + ok &= json_to_msat(buf, val, &fees); + val = json_next(val); + if (json_tok_is_null(buf, val)) + payment_id = NULL; + else { + ok &= json_to_sha256(buf, val, &payment_hash); + payment_id = &payment_hash; + } + val = json_next(val); + if (json_tok_is_null(buf, val)) + part_id = 0; + else { + ok &= json_to_u64(buf, val, &part_id); + } + val = json_next(val); + ok &= json_to_u64(buf, val, ×tamp); + assert(ok); + ev = new_channel_event(evs, + take(primary_tag), + credit, debit, fees, + payment_id, + part_id, + timestamp); + ev->acct_name = tal_steal(ev, account_name); + ev->db_id = created_index; + evs[i] = ev; + } + return evs; +} + +static struct chain_event **chain_events(const tal_t *ctx, + const struct bkpr *bkpr, + const char *buf, + const jsmntok_t *result) +{ + struct chain_event **evs; + size_t i; + const jsmntok_t *row, *rows = json_get_member(buf, result, "rows"); + + evs = tal_arr(ctx, struct chain_event *, rows->size); + json_for_each_arr(i, row, rows) { + bool ok = true; + struct chain_event *ev = tal(evs, struct chain_event); + int flag; + + const jsmntok_t *val = row + 1; + assert(row->size == 14); + ok &= json_to_u64(buf, val, &ev->db_id); + val = json_next(val); + ev->acct_name = json_strdup(ev, buf, val); + val = json_next(val); + if (json_tok_is_null(buf, val)) + ev->origin_acct = NULL; + else + ev->origin_acct = json_strdup(ev, buf, val); + val = json_next(val); + ev->tag = json_strdup(ev, buf, val); + val = json_next(val); + ok &= json_to_msat(buf, val, &ev->credit); + val = json_next(val); + ok &= json_to_msat(buf, val, &ev->debit); + val = json_next(val); + ok &= json_to_msat(buf, val, &ev->output_value); + val = json_next(val); + ok &= json_to_u64(buf, val, &ev->timestamp); + val = json_next(val); + ok &= json_to_u32(buf, val, &ev->blockheight); + val = json_next(val); + ok &= json_to_outpoint(buf, val, &ev->outpoint); + /* We may know better! */ + if (ev->blockheight == 0) + ev->blockheight = find_blockheight(bkpr, &ev->outpoint.txid); + val = json_next(val); + if (json_tok_is_null(buf, val)) + ev->spending_txid = NULL; + else { + ev->spending_txid = tal(ev, struct bitcoin_txid); + ok &= json_to_txid(buf, val, ev->spending_txid); + } + val = json_next(val); + if (json_tok_is_null(buf, val)) + ev->payment_id = NULL; + else { + ev->payment_id = tal(ev, struct sha256); + ok &= json_to_sha256(buf, val, ev->payment_id); + } + val = json_next(val); + /* These are 0/1 not true/false */ + ok &= json_to_int(buf, val, &flag); + ev->stealable = flag; + val = json_next(val); + /* These are 0/1 not true/false */ + ok &= json_to_int(buf, val, &flag); + ev->splice_close = flag; + assert(ok); + evs[i] = ev; + } + return evs; +} + +struct channel_event ** +channel_events_from_sql(const tal_t *ctx, + struct command *cmd, + const char *fmt, ...) +{ + va_list ap; + const jsmntok_t *toks; + const char *buf; + + va_start(ap, fmt); + toks = sql_reqv(tmpctx, cmd, &buf, fmt, ap); + va_end(ap); + + return channel_events(ctx, buf, toks); +} + +struct chain_event ** +chain_events_from_sql(const tal_t *ctx, + const struct bkpr *bkpr, + struct command *cmd, + const char *fmt, ...) +{ + va_list ap; + const jsmntok_t *toks; + const char *buf; + + va_start(ap, fmt); + toks = sql_reqv(tmpctx, cmd, &buf, fmt, ap); + va_end(ap); + + return chain_events(ctx, bkpr, buf, toks); +} + +const char *sql_string(const tal_t *ctx, const char *str) +{ + char *ret; + size_t out; + + if (!strchr(str, '\'')) + return str; + + /* Worst case size */ + ret = tal_arr(ctx, char, strlen(str) * 2 + 1); + out = 0; + for (size_t in = 0; str[in]; in++) { + ret[out++] = str[in]; + if (str[in] == '\'') + ret[out++] = str[in]; + } + ret[out] = '\0'; + return ret; +} diff --git a/plugins/bkpr/sql.h b/plugins/bkpr/sql.h new file mode 100644 index 000000000000..5d248972244e --- /dev/null +++ b/plugins/bkpr/sql.h @@ -0,0 +1,69 @@ +#ifndef LIGHTNING_PLUGINS_BKPR_SQL_H +#define LIGHTNING_PLUGINS_BKPR_SQL_H + +#include "config.h" +#include +#include + +struct command; +struct command_result; + +#define SELECT_CHANNEL_EVENTS \ + "SELECT" \ + " created_index" \ + ", account_id" \ + ", primary_tag" \ + ", credit_msat" \ + ", debit_msat" \ + ", fees_msat" \ + ", payment_hash" \ + ", part_id" \ + ", timestamp" \ + " FROM channelmoves " + +#define SELECT_CHAIN_EVENTS \ + "SELECT" \ + " created_index" \ + ", account_id" \ + ", originating_account" \ + ", primary_tag" \ + ", credit_msat" \ + ", debit_msat" \ + ", output_msat" \ + ", timestamp" \ + ", blockheight" \ + ", utxo" \ + ", spending_txid" \ + ", payment_hash" \ + ", EXISTS (SELECT 1 FROM chainmoves_extra_tags et" \ + " WHERE et.row = cm.rowid" \ + " AND et.extra_tags = 'stealable') AS stealable" \ + ", EXISTS (SELECT 1 FROM chainmoves_extra_tags et" \ + " WHERE et.row = cm.rowid" \ + " AND et.extra_tags = 'splice') AS spliced" \ + " FROM chainmoves cm " + +const jsmntok_t *PRINTF_FMT(4, 5) sql_req(const tal_t *ctx, + struct command *cmd, const char **buf, + const char *fmt, ...); + +const jsmntok_t *sql_reqv(const tal_t *ctx, + struct command *cmd, const char **buf, + const char *fmt, va_list ap); + +struct channel_event ** +PRINTF_FMT(3, 4) channel_events_from_sql(const tal_t *ctx, + struct command *cmd, + const char *fmt, ...); + +struct chain_event ** +PRINTF_FMT(4, 5) chain_events_from_sql(const tal_t *ctx, + const struct bkpr *bkpr, + struct command *cmd, + const char *fmt, ...); + +/* FIXME: The sql plugin should support bound parameters to avoid this! */ +/* Return with escaped quotes, if any */ +const char *sql_string(const tal_t *ctx, const char *str); + +#endif /* LIGHTNING_PLUGINS_BKPR_SQL_H */ diff --git a/plugins/bkpr/test/Makefile b/plugins/bkpr/test/Makefile index eae658005e0c..80fed1875c0a 100644 --- a/plugins/bkpr/test/Makefile +++ b/plugins/bkpr/test/Makefile @@ -13,7 +13,9 @@ BOOKKEEPER_TEST_COMMON_OBJS := \ common/channel_type.o \ common/coin_mvt.o \ common/features.o \ + common/json_parse.o \ common/json_stream.o \ + common/json_parse_simple.o \ common/key_derive.o \ common/memleak.o \ common/node_id.o \ @@ -27,9 +29,8 @@ BOOKKEEPER_TEST_COMMON_OBJS := \ db/db_sqlite3.o \ db/exec.o \ db/utils.o \ - plugins/bkpr/account.o \ - plugins/bkpr/db_sqlite3_sqlgen.o \ - plugins/bkpr/recorder.o + wire/towire.o \ + wire/fromwire.o $(BOOKKEEPER_TEST_PROGRAMS): $(BITCOIN_OBJS) $(BOOKKEEPER_TEST_COMMON_OBJS) $(BOOKKEEPER_TEST_OBJS): $(BOOKKEEPER_HEADER) $(BOOKKEEPER_SRC) $(BOOKKEEPER_TEST_COMMON_OBJS) diff --git a/plugins/bkpr/test/run-bkpr_db.c b/plugins/bkpr/test/run-bkpr_db.c deleted file mode 100644 index 46c3b4c9fdc2..000000000000 --- a/plugins/bkpr/test/run-bkpr_db.c +++ /dev/null @@ -1,313 +0,0 @@ -#include "config.h" - -#include -#include - -#include "common/json_filter.c" -#include "plugins/bkpr/db.c" -#include "plugins/libplugin.c" - -#include "test_utils.h" - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -/* AUTOGENERATED MOCKS START */ -/* Generated stub for command_fail_badparam */ -struct command_result *command_fail_badparam(struct command *cmd UNNEEDED, - const char *paramname UNNEEDED, - const char *buffer UNNEEDED, - const jsmntok_t *tok UNNEEDED, - const char *msg UNNEEDED) -{ fprintf(stderr, "command_fail_badparam called!\n"); abort(); } -/* Generated stub for daemon_developer_mode */ -bool daemon_developer_mode(char *argv[]) -{ fprintf(stderr, "daemon_developer_mode called!\n"); abort(); } -/* Generated stub for daemon_setup */ -void daemon_setup(const char *argv0 UNNEEDED, - void (*backtrace_print)(const char *fmt UNNEEDED, ...) UNNEEDED, - void (*backtrace_exit)(void)) -{ fprintf(stderr, "daemon_setup called!\n"); abort(); } -/* Generated stub for deprecated_ok_ */ -bool deprecated_ok_(bool deprecated_apis UNNEEDED, - const char *feature UNNEEDED, - const char *start UNNEEDED, - const char *end UNNEEDED, - const char **begs UNNEEDED, - void (*complain)(const char *feat UNNEEDED, bool allowing UNNEEDED, void *) UNNEEDED, - void *cbarg UNNEEDED) -{ fprintf(stderr, "deprecated_ok_ called!\n"); abort(); } -/* Generated stub for first_fee_state */ -enum htlc_state first_fee_state(enum side opener UNNEEDED) -{ fprintf(stderr, "first_fee_state called!\n"); abort(); } -/* Generated stub for fmt_wireaddr_without_port */ -char *fmt_wireaddr_without_port(const tal_t *ctx UNNEEDED, const struct wireaddr *a UNNEEDED) -{ fprintf(stderr, "fmt_wireaddr_without_port called!\n"); abort(); } -/* Generated stub for fromwire */ -const u8 *fromwire(const u8 **cursor UNNEEDED, size_t *max UNNEEDED, void *copy UNNEEDED, size_t n UNNEEDED) -{ fprintf(stderr, "fromwire called!\n"); abort(); } -/* Generated stub for fromwire_bool */ -bool fromwire_bool(const u8 **cursor UNNEEDED, size_t *max UNNEEDED) -{ fprintf(stderr, "fromwire_bool called!\n"); abort(); } -/* Generated stub for fromwire_fail */ -void *fromwire_fail(const u8 **cursor UNNEEDED, size_t *max UNNEEDED) -{ fprintf(stderr, "fromwire_fail called!\n"); abort(); } -/* Generated stub for fromwire_secp256k1_ecdsa_signature */ -void fromwire_secp256k1_ecdsa_signature(const u8 **cursor UNNEEDED, size_t *max UNNEEDED, - secp256k1_ecdsa_signature *signature UNNEEDED) -{ fprintf(stderr, "fromwire_secp256k1_ecdsa_signature called!\n"); abort(); } -/* Generated stub for fromwire_sha256 */ -void fromwire_sha256(const u8 **cursor UNNEEDED, size_t *max UNNEEDED, struct sha256 *sha256 UNNEEDED) -{ fprintf(stderr, "fromwire_sha256 called!\n"); abort(); } -/* Generated stub for fromwire_tal_arrn */ -u8 *fromwire_tal_arrn(const tal_t *ctx UNNEEDED, - const u8 **cursor UNNEEDED, size_t *max UNNEEDED, size_t num UNNEEDED) -{ fprintf(stderr, "fromwire_tal_arrn called!\n"); abort(); } -/* Generated stub for fromwire_u16 */ -u16 fromwire_u16(const u8 **cursor UNNEEDED, size_t *max UNNEEDED) -{ fprintf(stderr, "fromwire_u16 called!\n"); abort(); } -/* Generated stub for fromwire_u32 */ -u32 fromwire_u32(const u8 **cursor UNNEEDED, size_t *max UNNEEDED) -{ fprintf(stderr, "fromwire_u32 called!\n"); abort(); } -/* Generated stub for fromwire_u64 */ -u64 fromwire_u64(const u8 **cursor UNNEEDED, size_t *max UNNEEDED) -{ fprintf(stderr, "fromwire_u64 called!\n"); abort(); } -/* Generated stub for fromwire_u8 */ -u8 fromwire_u8(const u8 **cursor UNNEEDED, size_t *max UNNEEDED) -{ fprintf(stderr, "fromwire_u8 called!\n"); abort(); } -/* Generated stub for fromwire_u8_array */ -void fromwire_u8_array(const u8 **cursor UNNEEDED, size_t *max UNNEEDED, u8 *arr UNNEEDED, size_t num UNNEEDED) -{ fprintf(stderr, "fromwire_u8_array called!\n"); abort(); } -/* Generated stub for fromwire_wireaddr */ -bool fromwire_wireaddr(const u8 **cursor UNNEEDED, size_t *max UNNEEDED, struct wireaddr *addr UNNEEDED) -{ fprintf(stderr, "fromwire_wireaddr called!\n"); abort(); } -/* Generated stub for fromwire_wirestring */ -char *fromwire_wirestring(const tal_t *ctx UNNEEDED, const u8 **cursor UNNEEDED, size_t *max UNNEEDED) -{ fprintf(stderr, "fromwire_wirestring called!\n"); abort(); } -/* Generated stub for htlc_state_flags */ -int htlc_state_flags(enum htlc_state state UNNEEDED) -{ fprintf(stderr, "htlc_state_flags called!\n"); abort(); } -/* Generated stub for htlc_state_name */ -const char *htlc_state_name(enum htlc_state s UNNEEDED) -{ fprintf(stderr, "htlc_state_name called!\n"); abort(); } -/* Generated stub for is_asterix_notification */ -bool is_asterix_notification(const char *notification_name UNNEEDED, - const char *subscriptions UNNEEDED) -{ fprintf(stderr, "is_asterix_notification called!\n"); abort(); } -/* Generated stub for json_get_id */ -const char *json_get_id(const tal_t *ctx UNNEEDED, - const char *buffer UNNEEDED, const jsmntok_t *obj UNNEEDED) -{ fprintf(stderr, "json_get_id called!\n"); abort(); } -/* Generated stub for json_get_membern */ -const jsmntok_t *json_get_membern(const char *buffer UNNEEDED, - const jsmntok_t tok[] UNNEEDED, - const char *label UNNEEDED, size_t len UNNEEDED) -{ fprintf(stderr, "json_get_membern called!\n"); abort(); } -/* Generated stub for json_next */ -const jsmntok_t *json_next(const jsmntok_t *tok UNNEEDED) -{ fprintf(stderr, "json_next called!\n"); abort(); } -/* Generated stub for json_parse_input */ -bool json_parse_input(jsmn_parser *parser UNNEEDED, - jsmntok_t **toks UNNEEDED, - const char *input UNNEEDED, int len UNNEEDED, - bool *complete UNNEEDED) -{ fprintf(stderr, "json_parse_input called!\n"); abort(); } -/* Generated stub for json_parse_simple */ -jsmntok_t *json_parse_simple(const tal_t *ctx UNNEEDED, const char *input UNNEEDED, int len UNNEEDED) -{ fprintf(stderr, "json_parse_simple called!\n"); abort(); } -/* Generated stub for json_scan */ -const char *json_scan(const tal_t *ctx UNNEEDED, - const char *buffer UNNEEDED, - const jsmntok_t *tok UNNEEDED, - const char *guide UNNEEDED, - ...) -{ fprintf(stderr, "json_scan called!\n"); abort(); } -/* Generated stub for json_scanv */ -const char *json_scanv(const tal_t *ctx UNNEEDED, - const char *buffer UNNEEDED, - const jsmntok_t *tok UNNEEDED, - const char *guide UNNEEDED, - va_list ap UNNEEDED) -{ fprintf(stderr, "json_scanv called!\n"); abort(); } -/* Generated stub for json_strdup */ -char *json_strdup(const tal_t *ctx UNNEEDED, const char *buffer UNNEEDED, const jsmntok_t *tok UNNEEDED) -{ fprintf(stderr, "json_strdup called!\n"); abort(); } -/* Generated stub for json_to_bool */ -bool json_to_bool(const char *buffer UNNEEDED, const jsmntok_t *tok UNNEEDED, bool *b UNNEEDED) -{ fprintf(stderr, "json_to_bool called!\n"); abort(); } -/* Generated stub for json_to_int */ -bool json_to_int(const char *buffer UNNEEDED, const jsmntok_t *tok UNNEEDED, int *num UNNEEDED) -{ fprintf(stderr, "json_to_int called!\n"); abort(); } -/* Generated stub for json_to_msat */ -bool json_to_msat(const char *buffer UNNEEDED, const jsmntok_t *tok UNNEEDED, - struct amount_msat *msat UNNEEDED) -{ fprintf(stderr, "json_to_msat called!\n"); abort(); } -/* Generated stub for json_to_node_id */ -bool json_to_node_id(const char *buffer UNNEEDED, const jsmntok_t *tok UNNEEDED, - struct node_id *id UNNEEDED) -{ fprintf(stderr, "json_to_node_id called!\n"); abort(); } -/* Generated stub for json_to_number */ -bool json_to_number(const char *buffer UNNEEDED, const jsmntok_t *tok UNNEEDED, - unsigned int *num UNNEEDED) -{ fprintf(stderr, "json_to_number called!\n"); abort(); } -/* Generated stub for json_to_secret */ -bool json_to_secret(const char *buffer UNNEEDED, const jsmntok_t *tok UNNEEDED, struct secret *dest UNNEEDED) -{ fprintf(stderr, "json_to_secret called!\n"); abort(); } -/* Generated stub for json_to_short_channel_id */ -bool json_to_short_channel_id(const char *buffer UNNEEDED, const jsmntok_t *tok UNNEEDED, - struct short_channel_id *scid UNNEEDED) -{ fprintf(stderr, "json_to_short_channel_id called!\n"); abort(); } -/* Generated stub for json_to_txid */ -bool json_to_txid(const char *buffer UNNEEDED, const jsmntok_t *tok UNNEEDED, - struct bitcoin_txid *txid UNNEEDED) -{ fprintf(stderr, "json_to_txid called!\n"); abort(); } -/* Generated stub for json_to_u16 */ -bool json_to_u16(const char *buffer UNNEEDED, const jsmntok_t *tok UNNEEDED, - uint16_t *num UNNEEDED) -{ fprintf(stderr, "json_to_u16 called!\n"); abort(); } -/* Generated stub for json_tok_bin_from_hex */ -u8 *json_tok_bin_from_hex(const tal_t *ctx UNNEEDED, const char *buffer UNNEEDED, const jsmntok_t *tok UNNEEDED) -{ fprintf(stderr, "json_tok_bin_from_hex called!\n"); abort(); } -/* Generated stub for json_tok_copy */ -jsmntok_t *json_tok_copy(const tal_t *ctx UNNEEDED, const jsmntok_t *tok UNNEEDED) -{ fprintf(stderr, "json_tok_copy called!\n"); abort(); } -/* Generated stub for json_tok_full */ -const char *json_tok_full(const char *buffer UNNEEDED, const jsmntok_t *t UNNEEDED) -{ fprintf(stderr, "json_tok_full called!\n"); abort(); } -/* Generated stub for json_tok_full_len */ -int json_tok_full_len(const jsmntok_t *t UNNEEDED) -{ fprintf(stderr, "json_tok_full_len called!\n"); abort(); } -/* Generated stub for json_tok_remove */ -void json_tok_remove(jsmntok_t **tokens UNNEEDED, - jsmntok_t *obj_or_array UNNEEDED, const jsmntok_t *tok UNNEEDED, size_t num UNNEEDED) -{ fprintf(stderr, "json_tok_remove called!\n"); abort(); } -/* Generated stub for json_tok_strneq */ -bool json_tok_strneq(const char *buffer UNNEEDED, const jsmntok_t *tok UNNEEDED, - const char *str UNNEEDED, size_t len UNNEEDED) -{ fprintf(stderr, "json_tok_strneq called!\n"); abort(); } -/* Generated stub for last_fee_state */ -enum htlc_state last_fee_state(enum side opener UNNEEDED) -{ fprintf(stderr, "last_fee_state called!\n"); abort(); } -/* Generated stub for log_level_name */ -const char *log_level_name(enum log_level level UNNEEDED) -{ fprintf(stderr, "log_level_name called!\n"); abort(); } -/* Generated stub for param_check */ -bool param_check(struct command *cmd UNNEEDED, - const char *buffer UNNEEDED, - const jsmntok_t tokens[] UNNEEDED, ...) -{ fprintf(stderr, "param_check called!\n"); abort(); } -/* Generated stub for toks_alloc */ -jsmntok_t *toks_alloc(const tal_t *ctx UNNEEDED) -{ fprintf(stderr, "toks_alloc called!\n"); abort(); } -/* Generated stub for toks_reset */ -void toks_reset(jsmntok_t *toks UNNEEDED) -{ fprintf(stderr, "toks_reset called!\n"); abort(); } -/* Generated stub for towire */ -void towire(u8 **pptr UNNEEDED, const void *data UNNEEDED, size_t len UNNEEDED) -{ fprintf(stderr, "towire called!\n"); abort(); } -/* Generated stub for towire_bool */ -void towire_bool(u8 **pptr UNNEEDED, bool v UNNEEDED) -{ fprintf(stderr, "towire_bool called!\n"); abort(); } -/* Generated stub for towire_secp256k1_ecdsa_signature */ -void towire_secp256k1_ecdsa_signature(u8 **pptr UNNEEDED, - const secp256k1_ecdsa_signature *signature UNNEEDED) -{ fprintf(stderr, "towire_secp256k1_ecdsa_signature called!\n"); abort(); } -/* Generated stub for towire_sha256 */ -void towire_sha256(u8 **pptr UNNEEDED, const struct sha256 *sha256 UNNEEDED) -{ fprintf(stderr, "towire_sha256 called!\n"); abort(); } -/* Generated stub for towire_u16 */ -void towire_u16(u8 **pptr UNNEEDED, u16 v UNNEEDED) -{ fprintf(stderr, "towire_u16 called!\n"); abort(); } -/* Generated stub for towire_u32 */ -void towire_u32(u8 **pptr UNNEEDED, u32 v UNNEEDED) -{ fprintf(stderr, "towire_u32 called!\n"); abort(); } -/* Generated stub for towire_u64 */ -void towire_u64(u8 **pptr UNNEEDED, u64 v UNNEEDED) -{ fprintf(stderr, "towire_u64 called!\n"); abort(); } -/* Generated stub for towire_u8 */ -void towire_u8(u8 **pptr UNNEEDED, u8 v UNNEEDED) -{ fprintf(stderr, "towire_u8 called!\n"); abort(); } -/* Generated stub for towire_u8_array */ -void towire_u8_array(u8 **pptr UNNEEDED, const u8 *arr UNNEEDED, size_t num UNNEEDED) -{ fprintf(stderr, "towire_u8_array called!\n"); abort(); } -/* Generated stub for towire_wirestring */ -void towire_wirestring(u8 **pptr UNNEEDED, const char *str UNNEEDED) -{ fprintf(stderr, "towire_wirestring called!\n"); abort(); } -/* AUTOGENERATED MOCKS END */ - -static char *tmp_dsn(const tal_t *ctx) -{ - char *dsn, *filename; - int fd = tmpdir_mkstemp(ctx, "lacct-db-XXXXXX", &filename); - if (fd == -1) - return NULL; - close(fd); - - dsn = tal_fmt(NULL, "sqlite3://%s", filename); - tal_free(filename); - - return dsn; -} - -static struct db *create_test_db(void) -{ - struct db *db; - char *dsn; - - dsn = tmp_dsn(NULL); - db = db_open(NULL, dsn, true, db_error, (struct plugin *)NULL); - db->data_version = 0; - db->report_changes_fn = NULL; - - tal_free(dsn); - return db; -} - -static bool test_db_migrate(struct plugin *plugin) -{ - struct db *db = create_test_db(); - - CHECK(db); - db_begin_transaction(db); - CHECK(db_get_version(db) == -1); - CHECK(db_migrate(plugin, db) == true); - db_commit_transaction(db); - - db_begin_transaction(db); - CHECK(db_get_version(db) == ARRAY_SIZE(db_migrations) - 1); - db_commit_transaction(db); - - db_begin_transaction(db); - CHECK(db_migrate(plugin, db) == false); - db_commit_transaction(db); - - tal_free(db); - return true; -} - -int main(int argc, char *argv[]) -{ - bool ok = true; - /* Dummy for migration hooks */ - struct plugin *plugin = tal(NULL, struct plugin); - list_head_init(&plugin->js_list); - - common_setup(argv[0]); - - if (HAVE_SQLITE3) { - ok &= test_db_migrate(plugin); - } - - tal_free(plugin); - common_shutdown(); - trace_cleanup(); - return !ok; -} diff --git a/plugins/bkpr/test/run-recorder.c b/plugins/bkpr/test/run-recorder.c index b1d5c9b9946b..af8802e618bc 100644 --- a/plugins/bkpr/test/run-recorder.c +++ b/plugins/bkpr/test/run-recorder.c @@ -2,31 +2,54 @@ #include "common/json_filter.c" #include "test_utils.h" -#include "plugins/libplugin.c" - #include #include #include +#include +#include #include #include +#include +#include #include +#include #include +#include #include -#include #include #include +#include #include #include #include #include +#include #include #include #include -#include "plugins/bkpr/db.c" +#include "plugins/bkpr/account.c" +#include "plugins/bkpr/blockheights.c" +#include "plugins/bkpr/channel_event.c" +#include "plugins/bkpr/chain_event.c" +#include "plugins/bkpr/recorder.c" +#include "plugins/bkpr/onchain_fee.c" +#include "plugins/bkpr/rebalances.c" +#include "plugins/bkpr/sql.c" +#if HAVE_SQLITE3 + #include +#endif /* AUTOGENERATED MOCKS START */ +/* Generated stub for chain_event_description */ +const char *chain_event_description(const struct bkpr *bkpr UNNEEDED, + const struct chain_event *ce UNNEEDED) +{ fprintf(stderr, "chain_event_description called!\n"); abort(); } +/* Generated stub for channel_event_description */ +const char *channel_event_description(const struct bkpr *bkpr UNNEEDED, + const struct channel_event *ce UNNEEDED) +{ fprintf(stderr, "channel_event_description called!\n"); abort(); } /* Generated stub for command_fail_badparam */ struct command_result *command_fail_badparam(struct command *cmd UNNEEDED, const char *paramname UNNEEDED, @@ -34,221 +57,505 @@ struct command_result *command_fail_badparam(struct command *cmd UNNEEDED, const jsmntok_t *tok UNNEEDED, const char *msg UNNEEDED) { fprintf(stderr, "command_fail_badparam called!\n"); abort(); } -/* Generated stub for daemon_developer_mode */ -bool daemon_developer_mode(char *argv[]) -{ fprintf(stderr, "daemon_developer_mode called!\n"); abort(); } -/* Generated stub for daemon_setup */ -void daemon_setup(const char *argv0 UNNEEDED, - void (*backtrace_print)(const char *fmt UNNEEDED, ...) UNNEEDED, - void (*backtrace_exit)(void)) -{ fprintf(stderr, "daemon_setup called!\n"); abort(); } -/* Generated stub for deprecated_ok_ */ -bool deprecated_ok_(bool deprecated_apis UNNEEDED, - const char *feature UNNEEDED, - const char *start UNNEEDED, - const char *end UNNEEDED, - const char **begs UNNEEDED, - void (*complain)(const char *feat UNNEEDED, bool allowing UNNEEDED, void *) UNNEEDED, - void *cbarg UNNEEDED) -{ fprintf(stderr, "deprecated_ok_ called!\n"); abort(); } +/* Generated stub for command_filter_ptr */ +struct json_filter **command_filter_ptr(struct command *cmd UNNEEDED) +{ fprintf(stderr, "command_filter_ptr called!\n"); abort(); } /* Generated stub for first_fee_state */ enum htlc_state first_fee_state(enum side opener UNNEEDED) { fprintf(stderr, "first_fee_state called!\n"); abort(); } +/* Generated stub for fmt_channel_id */ +char *fmt_channel_id(const tal_t *ctx UNNEEDED, const struct channel_id *channel_id UNNEEDED) +{ fprintf(stderr, "fmt_channel_id called!\n"); abort(); } /* Generated stub for fmt_wireaddr_without_port */ char *fmt_wireaddr_without_port(const tal_t *ctx UNNEEDED, const struct wireaddr *a UNNEEDED) { fprintf(stderr, "fmt_wireaddr_without_port called!\n"); abort(); } -/* Generated stub for fromwire */ -const u8 *fromwire(const u8 **cursor UNNEEDED, size_t *max UNNEEDED, void *copy UNNEEDED, size_t n UNNEEDED) -{ fprintf(stderr, "fromwire called!\n"); abort(); } -/* Generated stub for fromwire_bool */ -bool fromwire_bool(const u8 **cursor UNNEEDED, size_t *max UNNEEDED) -{ fprintf(stderr, "fromwire_bool called!\n"); abort(); } -/* Generated stub for fromwire_fail */ -void *fromwire_fail(const u8 **cursor UNNEEDED, size_t *max UNNEEDED) -{ fprintf(stderr, "fromwire_fail called!\n"); abort(); } -/* Generated stub for fromwire_secp256k1_ecdsa_signature */ -void fromwire_secp256k1_ecdsa_signature(const u8 **cursor UNNEEDED, size_t *max UNNEEDED, - secp256k1_ecdsa_signature *signature UNNEEDED) -{ fprintf(stderr, "fromwire_secp256k1_ecdsa_signature called!\n"); abort(); } -/* Generated stub for fromwire_sha256 */ -void fromwire_sha256(const u8 **cursor UNNEEDED, size_t *max UNNEEDED, struct sha256 *sha256 UNNEEDED) -{ fprintf(stderr, "fromwire_sha256 called!\n"); abort(); } -/* Generated stub for fromwire_tal_arrn */ -u8 *fromwire_tal_arrn(const tal_t *ctx UNNEEDED, - const u8 **cursor UNNEEDED, size_t *max UNNEEDED, size_t num UNNEEDED) -{ fprintf(stderr, "fromwire_tal_arrn called!\n"); abort(); } -/* Generated stub for fromwire_u16 */ -u16 fromwire_u16(const u8 **cursor UNNEEDED, size_t *max UNNEEDED) -{ fprintf(stderr, "fromwire_u16 called!\n"); abort(); } -/* Generated stub for fromwire_u32 */ -u32 fromwire_u32(const u8 **cursor UNNEEDED, size_t *max UNNEEDED) -{ fprintf(stderr, "fromwire_u32 called!\n"); abort(); } -/* Generated stub for fromwire_u64 */ -u64 fromwire_u64(const u8 **cursor UNNEEDED, size_t *max UNNEEDED) -{ fprintf(stderr, "fromwire_u64 called!\n"); abort(); } -/* Generated stub for fromwire_u8 */ -u8 fromwire_u8(const u8 **cursor UNNEEDED, size_t *max UNNEEDED) -{ fprintf(stderr, "fromwire_u8 called!\n"); abort(); } -/* Generated stub for fromwire_u8_array */ -void fromwire_u8_array(const u8 **cursor UNNEEDED, size_t *max UNNEEDED, u8 *arr UNNEEDED, size_t num UNNEEDED) -{ fprintf(stderr, "fromwire_u8_array called!\n"); abort(); } /* Generated stub for fromwire_wireaddr */ bool fromwire_wireaddr(const u8 **cursor UNNEEDED, size_t *max UNNEEDED, struct wireaddr *addr UNNEEDED) { fprintf(stderr, "fromwire_wireaddr called!\n"); abort(); } -/* Generated stub for fromwire_wirestring */ -char *fromwire_wirestring(const tal_t *ctx UNNEEDED, const u8 **cursor UNNEEDED, size_t *max UNNEEDED) -{ fprintf(stderr, "fromwire_wirestring called!\n"); abort(); } /* Generated stub for htlc_state_flags */ int htlc_state_flags(enum htlc_state state UNNEEDED) { fprintf(stderr, "htlc_state_flags called!\n"); abort(); } /* Generated stub for htlc_state_name */ const char *htlc_state_name(enum htlc_state s UNNEEDED) { fprintf(stderr, "htlc_state_name called!\n"); abort(); } -/* Generated stub for is_asterix_notification */ -bool is_asterix_notification(const char *notification_name UNNEEDED, - const char *subscriptions UNNEEDED) -{ fprintf(stderr, "is_asterix_notification called!\n"); abort(); } -/* Generated stub for json_get_id */ -const char *json_get_id(const tal_t *ctx UNNEEDED, - const char *buffer UNNEEDED, const jsmntok_t *obj UNNEEDED) -{ fprintf(stderr, "json_get_id called!\n"); abort(); } -/* Generated stub for json_get_membern */ -const jsmntok_t *json_get_membern(const char *buffer UNNEEDED, - const jsmntok_t tok[] UNNEEDED, - const char *label UNNEEDED, size_t len UNNEEDED) -{ fprintf(stderr, "json_get_membern called!\n"); abort(); } -/* Generated stub for json_next */ -const jsmntok_t *json_next(const jsmntok_t *tok UNNEEDED) -{ fprintf(stderr, "json_next called!\n"); abort(); } -/* Generated stub for json_parse_input */ -bool json_parse_input(jsmn_parser *parser UNNEEDED, - jsmntok_t **toks UNNEEDED, - const char *input UNNEEDED, int len UNNEEDED, - bool *complete UNNEEDED) -{ fprintf(stderr, "json_parse_input called!\n"); abort(); } -/* Generated stub for json_parse_simple */ -jsmntok_t *json_parse_simple(const tal_t *ctx UNNEEDED, const char *input UNNEEDED, int len UNNEEDED) -{ fprintf(stderr, "json_parse_simple called!\n"); abort(); } -/* Generated stub for json_scan */ -const char *json_scan(const tal_t *ctx UNNEEDED, - const char *buffer UNNEEDED, - const jsmntok_t *tok UNNEEDED, - const char *guide UNNEEDED, - ...) -{ fprintf(stderr, "json_scan called!\n"); abort(); } -/* Generated stub for json_scanv */ -const char *json_scanv(const tal_t *ctx UNNEEDED, - const char *buffer UNNEEDED, - const jsmntok_t *tok UNNEEDED, - const char *guide UNNEEDED, - va_list ap UNNEEDED) -{ fprintf(stderr, "json_scanv called!\n"); abort(); } -/* Generated stub for json_strdup */ -char *json_strdup(const tal_t *ctx UNNEEDED, const char *buffer UNNEEDED, const jsmntok_t *tok UNNEEDED) -{ fprintf(stderr, "json_strdup called!\n"); abort(); } -/* Generated stub for json_to_bool */ -bool json_to_bool(const char *buffer UNNEEDED, const jsmntok_t *tok UNNEEDED, bool *b UNNEEDED) -{ fprintf(stderr, "json_to_bool called!\n"); abort(); } -/* Generated stub for json_to_int */ -bool json_to_int(const char *buffer UNNEEDED, const jsmntok_t *tok UNNEEDED, int *num UNNEEDED) -{ fprintf(stderr, "json_to_int called!\n"); abort(); } -/* Generated stub for json_to_msat */ -bool json_to_msat(const char *buffer UNNEEDED, const jsmntok_t *tok UNNEEDED, - struct amount_msat *msat UNNEEDED) -{ fprintf(stderr, "json_to_msat called!\n"); abort(); } -/* Generated stub for json_to_node_id */ -bool json_to_node_id(const char *buffer UNNEEDED, const jsmntok_t *tok UNNEEDED, - struct node_id *id UNNEEDED) -{ fprintf(stderr, "json_to_node_id called!\n"); abort(); } -/* Generated stub for json_to_number */ -bool json_to_number(const char *buffer UNNEEDED, const jsmntok_t *tok UNNEEDED, - unsigned int *num UNNEEDED) -{ fprintf(stderr, "json_to_number called!\n"); abort(); } -/* Generated stub for json_to_secret */ -bool json_to_secret(const char *buffer UNNEEDED, const jsmntok_t *tok UNNEEDED, struct secret *dest UNNEEDED) -{ fprintf(stderr, "json_to_secret called!\n"); abort(); } -/* Generated stub for json_to_short_channel_id */ -bool json_to_short_channel_id(const char *buffer UNNEEDED, const jsmntok_t *tok UNNEEDED, - struct short_channel_id *scid UNNEEDED) -{ fprintf(stderr, "json_to_short_channel_id called!\n"); abort(); } -/* Generated stub for json_to_txid */ -bool json_to_txid(const char *buffer UNNEEDED, const jsmntok_t *tok UNNEEDED, - struct bitcoin_txid *txid UNNEEDED) -{ fprintf(stderr, "json_to_txid called!\n"); abort(); } -/* Generated stub for json_to_u16 */ -bool json_to_u16(const char *buffer UNNEEDED, const jsmntok_t *tok UNNEEDED, - uint16_t *num UNNEEDED) -{ fprintf(stderr, "json_to_u16 called!\n"); abort(); } -/* Generated stub for json_tok_bin_from_hex */ -u8 *json_tok_bin_from_hex(const tal_t *ctx UNNEEDED, const char *buffer UNNEEDED, const jsmntok_t *tok UNNEEDED) -{ fprintf(stderr, "json_tok_bin_from_hex called!\n"); abort(); } -/* Generated stub for json_tok_copy */ -jsmntok_t *json_tok_copy(const tal_t *ctx UNNEEDED, const jsmntok_t *tok UNNEEDED) -{ fprintf(stderr, "json_tok_copy called!\n"); abort(); } -/* Generated stub for json_tok_full */ -const char *json_tok_full(const char *buffer UNNEEDED, const jsmntok_t *t UNNEEDED) -{ fprintf(stderr, "json_tok_full called!\n"); abort(); } -/* Generated stub for json_tok_full_len */ -int json_tok_full_len(const jsmntok_t *t UNNEEDED) -{ fprintf(stderr, "json_tok_full_len called!\n"); abort(); } -/* Generated stub for json_tok_remove */ -void json_tok_remove(jsmntok_t **tokens UNNEEDED, - jsmntok_t *obj_or_array UNNEEDED, const jsmntok_t *tok UNNEEDED, size_t num UNNEEDED) -{ fprintf(stderr, "json_tok_remove called!\n"); abort(); } -/* Generated stub for json_tok_strneq */ -bool json_tok_strneq(const char *buffer UNNEEDED, const jsmntok_t *tok UNNEEDED, - const char *str UNNEEDED, size_t len UNNEEDED) -{ fprintf(stderr, "json_tok_strneq called!\n"); abort(); } /* Generated stub for last_fee_state */ enum htlc_state last_fee_state(enum side opener UNNEEDED) { fprintf(stderr, "last_fee_state called!\n"); abort(); } -/* Generated stub for log_level_name */ -const char *log_level_name(enum log_level level UNNEEDED) -{ fprintf(stderr, "log_level_name called!\n"); abort(); } -/* Generated stub for param_check */ -bool param_check(struct command *cmd UNNEEDED, - const char *buffer UNNEEDED, - const jsmntok_t tokens[] UNNEEDED, ...) -{ fprintf(stderr, "param_check called!\n"); abort(); } -/* Generated stub for toks_alloc */ -jsmntok_t *toks_alloc(const tal_t *ctx UNNEEDED) -{ fprintf(stderr, "toks_alloc called!\n"); abort(); } -/* Generated stub for toks_reset */ -void toks_reset(jsmntok_t *toks UNNEEDED) -{ fprintf(stderr, "toks_reset called!\n"); abort(); } -/* Generated stub for towire */ -void towire(u8 **pptr UNNEEDED, const void *data UNNEEDED, size_t len UNNEEDED) -{ fprintf(stderr, "towire called!\n"); abort(); } -/* Generated stub for towire_bool */ -void towire_bool(u8 **pptr UNNEEDED, bool v UNNEEDED) -{ fprintf(stderr, "towire_bool called!\n"); abort(); } -/* Generated stub for towire_secp256k1_ecdsa_signature */ -void towire_secp256k1_ecdsa_signature(u8 **pptr UNNEEDED, - const secp256k1_ecdsa_signature *signature UNNEEDED) -{ fprintf(stderr, "towire_secp256k1_ecdsa_signature called!\n"); abort(); } -/* Generated stub for towire_sha256 */ -void towire_sha256(u8 **pptr UNNEEDED, const struct sha256 *sha256 UNNEEDED) -{ fprintf(stderr, "towire_sha256 called!\n"); abort(); } -/* Generated stub for towire_u16 */ -void towire_u16(u8 **pptr UNNEEDED, u16 v UNNEEDED) -{ fprintf(stderr, "towire_u16 called!\n"); abort(); } -/* Generated stub for towire_u32 */ -void towire_u32(u8 **pptr UNNEEDED, u32 v UNNEEDED) -{ fprintf(stderr, "towire_u32 called!\n"); abort(); } -/* Generated stub for towire_u64 */ -void towire_u64(u8 **pptr UNNEEDED, u64 v UNNEEDED) -{ fprintf(stderr, "towire_u64 called!\n"); abort(); } -/* Generated stub for towire_u8 */ -void towire_u8(u8 **pptr UNNEEDED, u8 v UNNEEDED) -{ fprintf(stderr, "towire_u8 called!\n"); abort(); } -/* Generated stub for towire_u8_array */ -void towire_u8_array(u8 **pptr UNNEEDED, const u8 *arr UNNEEDED, size_t num UNNEEDED) -{ fprintf(stderr, "towire_u8_array called!\n"); abort(); } -/* Generated stub for towire_wirestring */ -void towire_wirestring(u8 **pptr UNNEEDED, const char *str UNNEEDED) -{ fprintf(stderr, "towire_wirestring called!\n"); abort(); } +/* Generated stub for plugin_err */ +void plugin_err(struct plugin *p UNNEEDED, const char *fmt UNNEEDED, ...) +{ fprintf(stderr, "plugin_err called!\n"); abort(); } +/* Generated stub for plugin_log */ +void plugin_log(struct plugin *p UNNEEDED, enum log_level l UNNEEDED, const char *fmt UNNEEDED, ...) +{ fprintf(stderr, "plugin_log called!\n"); abort(); } /* AUTOGENERATED MOCKS END */ +static sqlite3 *bkpr_db; +static struct command *cmd; + +/* Stolen from old plugins/bkpr/db.c */ +struct migration { + const char *sql; + void (*func)(struct plugin *p, struct db *db); +}; + +static struct migration db_migrations[] = { + {SQL("CREATE TABLE version (version INTEGER);"), NULL}, + {SQL("INSERT INTO version VALUES (1);"), NULL}, + {SQL("CREATE TABLE vars (" + " name TEXT" + ", val TEXT" + ", intval INTEGER" + ", blobval BLOB" + ", PRIMARY KEY (name)" + ");"), + NULL}, + {SQL("INSERT INTO vars (" + " name" + ", intval" + ") VALUES (" + " 'data_version'" + ", 0" + ");"), + NULL}, + {SQL("CREATE TABLE accounts (" + " id INTEGER" + ", name TEXT" + ", peer_id BLOB" + ", opened_event_id INTEGER" + ", closed_event_id INTEGER" + ", onchain_resolved_block INTEGER" + ", is_wallet INTEGER" + ", we_opened INTEGER" + ", leased INTEGER" + ", PRIMARY KEY (id)" + ");"), + NULL}, + {SQL("CREATE TABLE chainmoves (" + " created_index INTEGER" + ", account_id TEXT" + ", primary_tag TEXT" + ", credit_msat INTEGER" + ", debit_msat INTEGER" + ", output_msat INTEGER" + ", timestamp INTEGER" + ", blockheight INTEGER" + ", utxo TEXT" + ", payment_hash BLOB" + ", spending_txid BLOB" + ", PRIMARY KEY (created_index)" + ");"), + NULL}, + {SQL("CREATE TABLE chainmoves_extra_tags (" + " row INTEGER" + ", arrindex INTEGER" + ", extra_tags TEXT)"), NULL}, + {SQL("CREATE TABLE channelmoves (" + " created_index INTEGER" + ", account_id TEXT" + ", primary_tag TEXT" + ", credit_msat INTEGER" + ", debit_msat INTEGER" + ", fees_msat INTEGER" + ", payment_hash BLOB" + ", part_id INTEGER" + ", timestamp INTEGER" + ", PRIMARY KEY (created_index)" + ");"), + NULL}, + {SQL("CREATE TABLE onchain_fees (" + "account_id TEXT" + ", txid BLOB" + ", credit INTEGER" + ", debit INTEGER" + ", currency TEXT" + ", timestamp INTEGER" + ", update_count INT" + ", PRIMARY KEY (account_id, txid, update_count)" + ");"), + NULL}, + {SQL("ALTER TABLE chainmoves ADD originating_account TEXT;"), NULL}, + {SQL("ALTER TABLE accounts ADD closed_count INTEGER DEFAULT 0;"), NULL}, + {SQL("ALTER TABLE chainmoves ADD ignored INTEGER;"), NULL}, + {SQL("ALTER TABLE chainmoves ADD stealable INTEGER;"), NULL}, + {SQL("ALTER TABLE chainmoves ADD ev_desc TEXT DEFAULT NULL;"), NULL}, + {SQL("ALTER TABLE channelmoves ADD ev_desc TEXT DEFAULT NULL;"), NULL}, + {SQL("ALTER TABLE channelmoves ADD rebalance_id INTEGER DEFAULT NULL;"), NULL}, + {SQL("ALTER TABLE chainmoves ADD spliced INTEGER DEFAULT 0;"), NULL}, +}; + +#undef db_begin_transaction +#define db_begin_transaction(db) +#define db_commit_transaction(db) + +static bool db_migrate(struct plugin *p, sqlite3 *db) +{ + sqlite3_stmt *s; + for (size_t i = 0; i < ARRAY_SIZE(db_migrations); i++) { + int rc; + sqlite3_prepare_v2(db, db_migrations[i].sql, -1, &s, NULL); + rc = sqlite3_step(s); + assert(rc == SQLITE_DONE); + sqlite3_finalize(s); + } + return true; +} + +static sqlite3 *db_setup(struct plugin *p, const char *db_dsn) +{ + sqlite3_stmt *stmt; + sqlite3 *sql; + int err; + + err = sqlite3_open_v2(db_dsn, &sql, SQLITE_OPEN_READWRITE|SQLITE_OPEN_CREATE, NULL); + assert(err == SQLITE_OK); + + err = sqlite3_extended_result_codes(sql, 1); + assert(err == SQLITE_OK); + + sqlite3_busy_timeout(sql, 60000); + sqlite3_prepare_v2(sql, + "PRAGMA foreign_keys = ON;", -1, &stmt, NULL); + err = sqlite3_step(stmt); + assert(err == SQLITE_DONE); + sqlite3_finalize(stmt); + + db_migrate(p, sql); + return sql; +} + +static int sqlite3_bind_outpoint(sqlite3_stmt *stmt, int pos, const struct bitcoin_outpoint *o) +{ + return sqlite3_bind_text(stmt, pos, fmt_bitcoin_outpoint(tmpctx, o), -1, SQLITE_TRANSIENT); +} + +static int sqlite3_bind_txid(sqlite3_stmt *stmt, int pos, const struct bitcoin_txid *txid) +{ + return sqlite3_bind_blob(stmt, pos, txid, sizeof(*txid), SQLITE_TRANSIENT); +} + +static int sqlite3_bind_hash(sqlite3_stmt *stmt, int pos, const struct sha256 *hash) +{ + return sqlite3_bind_blob(stmt, pos, hash, sizeof(*hash), SQLITE_TRANSIENT); +} + +/* Stolen from old plugins/bkpr/recorder.c */ +static bool find_chain_event(sqlite3 *db, + const struct account *acct, + const struct bitcoin_outpoint *outpoint, + const struct bitcoin_txid *spending_txid, + const char *tag) + +{ + sqlite3_stmt *stmt; + bool ret; + int rc; + + if (spending_txid) { + sqlite3_prepare_v2(db, + "SELECT 1" + " FROM chainmoves e" + " LEFT OUTER JOIN accounts a" + " ON e.account_id = a.name" + " WHERE " + " e.spending_txid = ?" + " AND e.account_id = ?" + " AND e.utxo = ?", + -1, &stmt, NULL); + rc = sqlite3_bind_txid(stmt, 1, spending_txid); + assert(rc == SQLITE_OK); + } else { + sqlite3_prepare_v2(db, + "SELECT 1" + " FROM chainmoves e" + " LEFT OUTER JOIN accounts a" + " ON e.account_id = a.name" + " WHERE " + " e.primary_tag = ?" + " AND e.account_id = ?" + " AND e.utxo = ?" + " AND e.spending_txid IS NULL", + -1, &stmt, NULL); + rc = sqlite3_bind_text(stmt, 1, tag, -1, SQLITE_TRANSIENT); + assert(rc == SQLITE_OK); + } + + rc = sqlite3_bind_text(stmt, 2, acct->name, -1, SQLITE_TRANSIENT); + assert(rc == SQLITE_OK); + rc = sqlite3_bind_outpoint(stmt, 3, outpoint); + assert(rc == SQLITE_OK); + + ret = (sqlite3_step(stmt) == SQLITE_ROW); + sqlite3_finalize(stmt); + return ret; +} + +static bool log_chain_event(struct bkpr *bkpr, + const struct account *acct, + struct chain_event *e) +{ + int rc; + sqlite3_stmt *stmt; + + /* We're responsible for de-duping chain events! */ + if (find_chain_event(bkpr_db, acct, + &e->outpoint, e->spending_txid, + e->tag)) + return false; + rc = sqlite3_prepare_v2(bkpr_db, + "INSERT INTO chainmoves" + " (" + " account_id" + ", originating_account" + ", primary_tag" + ", credit_msat" + ", debit_msat" + ", output_msat" + ", timestamp" + ", blockheight" + ", utxo" + ", payment_hash" + ", spending_txid" + ")" + " VALUES " + "(?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?);", + -1, &stmt, NULL); + assert(rc == SQLITE_OK); + + rc = sqlite3_bind_text(stmt, 1, acct->name, -1, SQLITE_TRANSIENT); + assert(rc == SQLITE_OK); + if (e->origin_acct) + sqlite3_bind_text(stmt, 2, e->origin_acct, -1, SQLITE_TRANSIENT); + else + sqlite3_bind_null(stmt, 2); + rc = sqlite3_bind_text(stmt, 3, e->tag, -1, SQLITE_TRANSIENT); + assert(rc == SQLITE_OK); + rc = sqlite3_bind_int64(stmt, 4, (sqlite3_int64)e->credit.millisatoshis /* Raw: db */); + assert(rc == SQLITE_OK); + rc = sqlite3_bind_int64(stmt, 5, (sqlite3_int64)e->debit.millisatoshis /* Raw: db */); + assert(rc == SQLITE_OK); + rc = sqlite3_bind_int64(stmt, 6, (sqlite3_int64)e->output_value.millisatoshis /* Raw: db */); + assert(rc == SQLITE_OK); + rc = sqlite3_bind_int64(stmt, 7, (sqlite3_int64)e->timestamp); + assert(rc == SQLITE_OK); + rc = sqlite3_bind_int64(stmt, 8, (sqlite3_int64)e->blockheight); + assert(rc == SQLITE_OK); + rc = sqlite3_bind_outpoint(stmt, 9, &e->outpoint); + assert(rc == SQLITE_OK); + if (e->payment_id) { + rc = sqlite3_bind_hash(stmt, 10, e->payment_id); + } else { + rc = sqlite3_bind_null(stmt,10); + } + assert(rc == SQLITE_OK); + if (e->spending_txid) { + rc = sqlite3_bind_txid(stmt, 11, e->spending_txid); + } else { + rc = sqlite3_bind_null(stmt, 11); + } + assert(rc == SQLITE_OK); + + /* FIXME: Put stealable and spliced into extra_tags! */ + /* Execute */ + rc = sqlite3_step(stmt); + assert(rc == SQLITE_DONE); + + e->db_id = (u64)sqlite3_last_insert_rowid(bkpr_db); + e->acct_name = tal_strdup(e, acct->name); + sqlite3_finalize(stmt); + return true; +} + +/* All chatgpt */ +static void log_channel_event(sqlite3 *db, + const struct account *acct, + struct channel_event *e) +{ + static const char *SQL = + "INSERT INTO channelmoves (" + " account_id, primary_tag, credit_msat, debit_msat, fees_msat, payment_hash, part_id, timestamp" + ") VALUES (?, ?, ?, ?, ?, ?, ?, ?);"; + + sqlite3_stmt *stmt; + int rc = sqlite3_prepare_v2(db, SQL, -1, &stmt, NULL); + assert(rc == SQLITE_OK); + + /* 1 account_id (TEXT) */ + rc = sqlite3_bind_text(stmt, 1, acct->name, -1, SQLITE_TRANSIENT); + assert(rc == SQLITE_OK); + + /* 2 tag (TEXT) */ + rc = sqlite3_bind_text(stmt, 2, e->tag, -1, SQLITE_TRANSIENT); + assert(rc == SQLITE_OK); + + /* 3 credit (INTEGER) */ + rc = sqlite3_bind_int64(stmt, 3, (sqlite3_int64)e->credit.millisatoshis /* Raw: db */); + assert(rc == SQLITE_OK); + + /* 4 debit (INTEGER) */ + rc = sqlite3_bind_int64(stmt, 4, (sqlite3_int64)e->debit.millisatoshis /* Raw: db */); + assert(rc == SQLITE_OK); + + /* 5 fees (INTEGER) */ + rc = sqlite3_bind_int64(stmt, 5, (sqlite3_int64)e->fees.millisatoshis /* Raw: db */); + assert(rc == SQLITE_OK); + + /* 6 payment_hash (BLOB 32) or NULL */ + if (e->payment_id) { + rc = sqlite3_bind_blob(stmt, 6, (const void *)e->payment_id, + (int)sizeof(*e->payment_id), SQLITE_TRANSIENT); + } else { + rc = sqlite3_bind_null(stmt, 6); + } + assert(rc == SQLITE_OK); + + /* 7 part_id (INTEGER) */ + rc = sqlite3_bind_int64(stmt, 7, (sqlite3_int64)e->part_id); + assert(rc == SQLITE_OK); + + /* 8 timestamp (INTEGER) */ + rc = sqlite3_bind_int64(stmt, 8, (sqlite3_int64)e->timestamp); + assert(rc == SQLITE_OK); + + rc = sqlite3_step(stmt); + assert(rc == SQLITE_DONE); + + e->db_id = (u64)sqlite3_last_insert_rowid(db); + e->acct_name = tal_strdup(e, acct->name); + + sqlite3_finalize(stmt); +} + +struct command_result *jsonrpc_set_datastore_(struct command *cmd UNNEEDED, + const char *path UNNEEDED, + const void *value UNNEEDED, + int len_or_str UNNEEDED, + const char *mode UNNEEDED, + struct command_result *(*cb)(struct command *command, + const char *method, + const char *buf, + const jsmntok_t *result, + void *arg), + struct command_result *(*errcb)(struct command *command UNNEEDED, + const char *method UNNEEDED, + const char *buf UNNEEDED, + const jsmntok_t *result UNNEEDED, + void *arg) UNNEEDED, + void *arg) + +{ + return cb(cmd, NULL, NULL, NULL, arg); +} + +struct command_result *ignore_datastore_reply(struct command *cmd, + const char *method UNNEEDED, + const char *buf UNNEEDED, + const jsmntok_t *result UNNEEDED, + void *arg UNNEEDED) +{ + return NULL; +} + +struct json_out *json_out_obj(const tal_t *ctx, + const char *fieldname, + const char *str) +{ + struct json_out *jout = json_out_new(ctx); + json_out_start(jout, NULL, '{'); + if (str) + json_out_addstr(jout, fieldname, str); + if (taken(str)) + tal_free(str); + json_out_end(jout, '}'); + json_out_finished(jout); + + return jout; +} + +const jsmntok_t *jsonrpc_request_sync(const tal_t *ctx, + struct command *cmd, + const char *method, + const struct json_out *params TAKES, + const char **resp) +{ +#if HAVE_SQLITE3 + sqlite3_stmt *s; + jsmntok_t *toks; + jsmn_parser parser; + size_t len; + char *buf; + const char *p, *querystr; + bool complete; + int rc; + + if (streq(method, "listdatastore")) { + buf = tal_fmt(ctx, "{\"datastore\": []}"); + goto done; + } + + assert(streq(method, "sql")); + + /* Grab select query */ + jsmn_init(&parser); + p = json_out_contents(params, &len); + toks = toks_alloc(tmpctx); + assert(json_parse_input(&parser, &toks, p, len, &complete)); + assert(complete); + /* { "query": } */ + querystr = json_strdup(tmpctx, p, toks + 2); + + sqlite3_prepare_v2(bkpr_db, querystr, + -1, &s, NULL); + + /* Make JSON-style result */ + buf = tal_fmt(ctx, "{\"rows\": ["); + while ((rc = sqlite3_step(s)) == SQLITE_ROW) { + size_t num_cols = sqlite3_column_count(s); + + tal_append_fmt(&buf, "["); + for (size_t i = 0; i < num_cols; i++) { + switch (sqlite3_column_type(s, i)) { + case SQLITE_NULL: + tal_append_fmt(&buf, "null"); + break; + case SQLITE_INTEGER: + tal_append_fmt(&buf, "%"PRIu64, (u64)sqlite3_column_int64(s, i)); + break; + case SQLITE_TEXT: + tal_append_fmt(&buf, "\"%s\"", sqlite3_column_text(s, i)); + break; + case SQLITE_BLOB: { + const void *blob = sqlite3_column_blob(s, i); + int bloblen = sqlite3_column_bytes(s, i); + tal_append_fmt(&buf, "\"%s\"", tal_hexstr(tmpctx, blob, bloblen)); + break; + } + default: + abort(); + } + if (i != num_cols - 1) + tal_append_fmt(&buf, ","); + } + tal_append_fmt(&buf, "]"); + } + assert(rc == SQLITE_DONE); + tal_append_fmt(&buf, "]}"); + sqlite3_finalize(s); + +done: + jsmn_init(&parser); + toks = toks_alloc(ctx); + assert(json_parse_input(&parser, &toks, buf, strlen(buf), &complete)); + assert(complete); + + if (taken(params)) + tal_free(params); + *resp = buf; + return toks; +#else + return NULL; +#endif +} + static char *tmp_dsn(const tal_t *ctx) { char *dsn, *filename; @@ -257,15 +564,31 @@ static char *tmp_dsn(const tal_t *ctx) return NULL; close(fd); - dsn = tal_fmt(ctx, "sqlite3://%s", filename); + dsn = tal_fmt(ctx, "%s", filename); tal_free(filename); return dsn; } +static struct bkpr *bkpr_setup(const tal_t *ctx) +{ + struct bkpr *bkpr = tal(ctx, struct bkpr); + + if (bkpr_db) + assert(sqlite3_close(bkpr_db) == SQLITE_OK); + + bkpr_db = db_setup(NULL, tmp_dsn(ctx)); + bkpr->accounts = init_accounts(ctx, NULL); + bkpr->onchain_fees = init_onchain_fees(bkpr, NULL); + bkpr->rebalances = init_rebalances(bkpr, NULL); + + /* We need to see everything */ + bkpr->chainmoves_index = bkpr->channelmoves_index = UINT64_MAX; + return bkpr; +} + static bool accountseq(struct account *a1, struct account *a2) { - CHECK(a1->db_id == a2->db_id); CHECK(streq(a1->name, a2->name)); CHECK((a1->peer_id != NULL) == (a2->peer_id != NULL)); if (a1->peer_id) @@ -291,31 +614,24 @@ static bool accountseq(struct account *a1, struct account *a2) static bool channel_events_eq(struct channel_event *e1, struct channel_event *e2) { CHECK(e1->db_id == e2->db_id); - CHECK(e1->acct_db_id == e2->acct_db_id); + CHECK(streq(e1->acct_name, e2->acct_name)); CHECK(streq(e1->tag, e2->tag)); CHECK(amount_msat_eq(e1->credit, e2->credit)); CHECK(amount_msat_eq(e1->debit, e2->debit)); CHECK(amount_msat_eq(e1->fees, e2->fees)); - CHECK((e1->rebalance_id != NULL) == (e2->rebalance_id != NULL)); - if (e1->rebalance_id) - CHECK(*e1->rebalance_id == *e2->rebalance_id); CHECK((e1->payment_id != NULL) == (e2->payment_id != NULL)); if (e1->payment_id) CHECK(sha256_eq(e1->payment_id, e2->payment_id)); CHECK(e1->part_id == e2->part_id); CHECK(e1->timestamp == e2->timestamp); - CHECK((e1->desc != NULL) == (e2->desc != NULL)); - if (e1->desc) - CHECK(streq(e1->desc, e2->desc)); - return true; } static bool chain_events_eq(struct chain_event *e1, struct chain_event *e2) { CHECK(e1->db_id == e2->db_id); - CHECK(e1->acct_db_id == e2->acct_db_id); + CHECK(streq(e1->acct_name, e2->acct_name)); CHECK((e1->origin_acct != NULL) == (e2->origin_acct != NULL)); if (e1->origin_acct) CHECK(streq(e1->origin_acct, e2->origin_acct)); @@ -336,10 +652,6 @@ static bool chain_events_eq(struct chain_event *e1, struct chain_event *e2) if (e1->payment_id) CHECK(sha256_eq(e1->payment_id, e2->payment_id)); - CHECK((e1->desc != NULL) == (e2->desc != NULL)); - if (e1->desc) - CHECK(streq(e1->desc, e2->desc)); - CHECK(e1->splice_close == e2->splice_close); return true; @@ -361,8 +673,6 @@ static struct channel_event *make_channel_event(const tal_t *ctx, ev->timestamp = 1919191; ev->part_id = 19; ev->tag = tag; - ev->desc = tal_fmt(ev, "description"); - ev->rebalance_id = NULL; return ev; } @@ -391,7 +701,6 @@ static struct chain_event *make_chain_event(const tal_t *ctx, ev->blockheight = blockheight; ev->stealable = false; ev->splice_close = false; - ev->desc = tal_fmt(ev, "hello hello"); memset(&ev->outpoint.txid, outpoint_char, sizeof(struct bitcoin_txid)); ev->outpoint.n = outnum; @@ -407,9 +716,9 @@ static struct chain_event *make_chain_event(const tal_t *ctx, return ev; } -static bool test_onchain_fee_wallet_spend(const tal_t *ctx, struct plugin *p) +static bool test_onchain_fee_wallet_spend(const tal_t *ctx) { - struct db *db = db_setup(ctx, p, tmp_dsn(ctx)); + struct bkpr *bkpr = bkpr_setup(ctx); struct node_id node_id, peer_id; struct account *wal_acct, *ext_acct; struct bitcoin_txid txid; @@ -419,13 +728,15 @@ static bool test_onchain_fee_wallet_spend(const tal_t *ctx, struct plugin *p) memset(&node_id, 2, sizeof(struct node_id)); memset(&peer_id, 3, sizeof(struct node_id)); - wal_acct = new_account(ctx, tal_fmt(ctx, ACCOUNT_NAME_WALLET), &peer_id); - ext_acct = new_account(ctx, tal_fmt(ctx, ACCOUNT_NAME_EXTERNAL), &peer_id); + wal_acct = new_account(bkpr->accounts, tal_fmt(ctx, ACCOUNT_NAME_WALLET)); + wal_acct->peer_id = &node_id; + ext_acct = new_account(bkpr->accounts, tal_fmt(ctx, ACCOUNT_NAME_EXTERNAL)); + ext_acct->peer_id = &peer_id; memset(&txid, '1', sizeof(struct bitcoin_txid)); db_begin_transaction(db); - account_add(db, wal_acct); - account_add(db, ext_acct); + account_datastore_set(cmd, wal_acct, "must-create"); + account_datastore_set(cmd, ext_acct, "must-create"); db_commit_transaction(db); @@ -436,38 +747,35 @@ static bool test_onchain_fee_wallet_spend(const tal_t *ctx, struct plugin *p) * deposit 1111 0 700 external */ db_begin_transaction(db); - log_chain_event(db, wal_acct, + log_chain_event(bkpr, wal_acct, make_chain_event(ctx, "withdrawal", AMOUNT_MSAT(0), AMOUNT_MSAT(1000), AMOUNT_MSAT(1000), blockheight, 'X', 0, '1')); - maybe_update_onchain_fees(ctx, db, &txid); + maybe_update_onchain_fees(ctx, cmd, bkpr, &txid); - log_chain_event(db, wal_acct, + log_chain_event(bkpr, wal_acct, make_chain_event(ctx, "deposit", AMOUNT_MSAT(200), AMOUNT_MSAT(0), AMOUNT_MSAT(200), blockheight, '1', 1, '*')); - maybe_update_onchain_fees(ctx, db, &txid); + maybe_update_onchain_fees(ctx, cmd, bkpr, &txid); - log_chain_event(db, ext_acct, + log_chain_event(bkpr, ext_acct, make_chain_event(ctx, "deposit", AMOUNT_MSAT(700), AMOUNT_MSAT(0), AMOUNT_MSAT(700), blockheight, '1', 0, '*')); - maybe_update_onchain_fees(ctx, db, &txid); - db_commit_transaction(db); - - db_begin_transaction(db); - ofs = list_chain_fees(ctx, db); + maybe_update_onchain_fees(ctx, cmd, bkpr, &txid); db_commit_transaction(db); + ofs = list_chain_fees(ctx, bkpr); CHECK(tal_count(ofs) == 2); /* we expect 800, then -700 */ @@ -482,9 +790,9 @@ static bool test_onchain_fee_wallet_spend(const tal_t *ctx, struct plugin *p) return true; } -static bool test_onchain_fee_chan_close(const tal_t *ctx, struct plugin *p) +static bool test_onchain_fee_chan_close(const tal_t *ctx) { - struct db *db = db_setup(ctx, p, tmp_dsn(ctx)); + struct bkpr *bkpr = bkpr_setup(ctx); struct node_id node_id, peer_id; struct account *acct, *wal_acct, *ext_acct; struct onchain_fee **ofs, **ofs1; @@ -500,14 +808,17 @@ static bool test_onchain_fee_chan_close(const tal_t *ctx, struct plugin *p) /* to_us, to_them, 1 htlc, 2 anchors */ close_output_count = 5; - wal_acct = new_account(ctx, tal_fmt(ctx, ACCOUNT_NAME_WALLET), &peer_id); - ext_acct = new_account(ctx, tal_fmt(ctx, ACCOUNT_NAME_EXTERNAL), &peer_id); - acct = new_account(ctx, tal_fmt(ctx, "chan-1"), &peer_id); + wal_acct = new_account(bkpr->accounts, tal_fmt(ctx, ACCOUNT_NAME_WALLET)); + wal_acct->peer_id = &node_id; + ext_acct = new_account(bkpr->accounts, tal_fmt(ctx, ACCOUNT_NAME_EXTERNAL)); + ext_acct->peer_id = &peer_id; + acct = new_account(bkpr->accounts, tal_fmt(ctx, "chan-1")); + acct->peer_id = &peer_id; db_begin_transaction(db); - account_add(db, wal_acct); - account_add(db, ext_acct); - account_add(db, acct); + account_datastore_set(cmd, wal_acct, "must-create"); + account_datastore_set(cmd, ext_acct, "must-create"); + account_datastore_set(cmd, acct, "must-create"); db_commit_transaction(db); /* Close a channel */ @@ -533,9 +844,9 @@ static bool test_onchain_fee_chan_close(const tal_t *ctx, struct plugin *p) AMOUNT_MSAT(1660), blockheight, 'X', 0, '*'); - log_chain_event(db, acct, ev); + log_chain_event(bkpr, acct, ev); tags[0] = MVT_CHANNEL_OPEN; - maybe_update_account(db, acct, ev, tags, 0, NULL); + maybe_update_account(cmd, acct, ev, tags, 0, NULL); ev = make_chain_event(ctx, "channel_close", AMOUNT_MSAT(0), @@ -543,13 +854,13 @@ static bool test_onchain_fee_chan_close(const tal_t *ctx, struct plugin *p) AMOUNT_MSAT(1660), blockheight, 'X', 0, '1'); - log_chain_event(db, acct, ev); + log_chain_event(bkpr, acct, ev); /* Update the account to have the right info! */ tags[0] = MVT_CHANNEL_CLOSE; - maybe_update_account(db, acct, ev, tags, close_output_count, NULL); + maybe_update_account(cmd, acct, ev, tags, close_output_count, NULL); - log_chain_event(db, acct, + log_chain_event(bkpr, acct, make_chain_event(ctx, "delayed_to_us", AMOUNT_MSAT(200), AMOUNT_MSAT(0), @@ -557,16 +868,16 @@ static bool test_onchain_fee_chan_close(const tal_t *ctx, struct plugin *p) blockheight, '1', 1, '*')); memset(&txid, '1', sizeof(struct bitcoin_txid)); - maybe_update_onchain_fees(ctx, db, &txid); + maybe_update_onchain_fees(ctx, cmd, bkpr, &txid); - log_chain_event(db, wal_acct, + log_chain_event(bkpr, wal_acct, make_chain_event(ctx, "anchor", AMOUNT_MSAT(30), AMOUNT_MSAT(0), AMOUNT_MSAT(30), blockheight, '1', 0, '*')); - log_chain_event(db, ext_acct, + log_chain_event(bkpr, ext_acct, make_chain_event(ctx, "anchor", AMOUNT_MSAT(30), AMOUNT_MSAT(0), @@ -574,13 +885,13 @@ static bool test_onchain_fee_chan_close(const tal_t *ctx, struct plugin *p) blockheight, '1', 4, '*')); memset(&txid, '1', sizeof(struct bitcoin_txid)); - maybe_update_onchain_fees(ctx, db, &txid); + maybe_update_onchain_fees(ctx, cmd, bkpr, &txid); /* Should be no fees yet */ - ofs = list_chain_fees(ctx, db); + ofs = list_chain_fees(ctx, bkpr); CHECK(tal_count(ofs) == 0); - log_chain_event(db, acct, + log_chain_event(bkpr, acct, make_chain_event(ctx, "htlc_timeout", AMOUNT_MSAT(600), AMOUNT_MSAT(0), @@ -588,7 +899,7 @@ static bool test_onchain_fee_chan_close(const tal_t *ctx, struct plugin *p) blockheight, '1', 2, '*')); - log_chain_event(db, ext_acct, + log_chain_event(bkpr, ext_acct, make_chain_event(ctx, "to_them", AMOUNT_MSAT(300), AMOUNT_MSAT(0), @@ -597,12 +908,12 @@ static bool test_onchain_fee_chan_close(const tal_t *ctx, struct plugin *p) '1', 3, '*')); memset(&txid, '1', sizeof(struct bitcoin_txid)); - maybe_update_onchain_fees(ctx, db, &txid); + maybe_update_onchain_fees(ctx, cmd, bkpr, &txid); db_commit_transaction(db); /* txid 2222 */ db_begin_transaction(db); - log_chain_event(db, acct, + log_chain_event(bkpr, acct, make_chain_event(ctx, "to_wallet", AMOUNT_MSAT(0), AMOUNT_MSAT(200), @@ -610,7 +921,7 @@ static bool test_onchain_fee_chan_close(const tal_t *ctx, struct plugin *p) blockheight + 1, '1', 1, '2')); - log_chain_event(db, wal_acct, + log_chain_event(bkpr, wal_acct, make_chain_event(ctx, "deposit", AMOUNT_MSAT(150), AMOUNT_MSAT(0), @@ -618,25 +929,23 @@ static bool test_onchain_fee_chan_close(const tal_t *ctx, struct plugin *p) blockheight + 1, '2', 0, '*')); memset(&txid, '2', sizeof(struct bitcoin_txid)); - maybe_update_onchain_fees(ctx, db, &txid); + maybe_update_onchain_fees(ctx, cmd, bkpr, &txid); CHECK(acct->onchain_resolved_block == 0); - maybe_mark_account_onchain(db, acct); + assert(account_onchain_closeheight(bkpr, cmd, acct) == 0); CHECK(acct->onchain_resolved_block == 0); db_commit_transaction(db); /* Expect: 1 onchain fee records, all for chan-1 */ - db_begin_transaction(db); - ofs = list_chain_fees(ctx, db); - ofs1 = account_onchain_fees(ctx, db, acct); - db_commit_transaction(db); + ofs = list_chain_fees(ctx, bkpr); + ofs1 = account_get_chain_fees(tmpctx, bkpr, acct->name); CHECK(tal_count(ofs) == tal_count(ofs1)); CHECK(tal_count(ofs) == 1); /* txid 4444 */ db_begin_transaction(db); - log_chain_event(db, wal_acct, + log_chain_event(bkpr, wal_acct, make_chain_event(ctx, "deposit", AMOUNT_MSAT(350), AMOUNT_MSAT(0), @@ -645,10 +954,10 @@ static bool test_onchain_fee_chan_close(const tal_t *ctx, struct plugin *p) '4', 0, '*')); memset(&txid, '4', sizeof(struct bitcoin_txid)); - maybe_update_onchain_fees(ctx, db, &txid); + maybe_update_onchain_fees(ctx, cmd, bkpr, &txid); /* txid 3333 */ - log_chain_event(db, acct, + log_chain_event(bkpr, acct, make_chain_event(ctx, "htlc_timeout", AMOUNT_MSAT(0), AMOUNT_MSAT(600), @@ -656,10 +965,10 @@ static bool test_onchain_fee_chan_close(const tal_t *ctx, struct plugin *p) blockheight + 2, '1', 2, '3')); - maybe_mark_account_onchain(db, acct); + assert(account_onchain_closeheight(bkpr, cmd, acct) == 0); CHECK(acct->onchain_resolved_block == 0); - log_chain_event(db, acct, + log_chain_event(bkpr, acct, make_chain_event(ctx, "htlc_tx", AMOUNT_MSAT(450), AMOUNT_MSAT(0), @@ -668,9 +977,9 @@ static bool test_onchain_fee_chan_close(const tal_t *ctx, struct plugin *p) '3', 0, '*')); memset(&txid, '3', sizeof(struct bitcoin_txid)); - maybe_update_onchain_fees(ctx, db, &txid); + maybe_update_onchain_fees(ctx, cmd, bkpr, &txid); - log_chain_event(db, acct, + log_chain_event(bkpr, acct, make_chain_event(ctx, "to_wallet", AMOUNT_MSAT(0), AMOUNT_MSAT(450), @@ -679,15 +988,13 @@ static bool test_onchain_fee_chan_close(const tal_t *ctx, struct plugin *p) '3', 0, '4')); memset(&txid, '4', sizeof(struct bitcoin_txid)); - maybe_update_onchain_fees(ctx, db, &txid); + maybe_update_onchain_fees(ctx, cmd, bkpr, &txid); db_commit_transaction(db); /* Expect: onchain fee records for tx except channel close */ - db_begin_transaction(db); - ofs = list_chain_fees(ctx, db); - ofs1 = account_onchain_fees(ctx, db, acct); - db_commit_transaction(db); + ofs = list_chain_fees(ctx, bkpr); + ofs1 = account_get_chain_fees(tmpctx, bkpr, acct->name); CHECK(tal_count(ofs) == tal_count(ofs1)); CHECK(tal_count(ofs) == 3); @@ -695,12 +1002,12 @@ static bool test_onchain_fee_chan_close(const tal_t *ctx, struct plugin *p) /* Now we update the channel's onchain fees */ CHECK(acct->onchain_resolved_block == 0); db_begin_transaction(db); - maybe_mark_account_onchain(db, acct); + account_update_closeheight(cmd, acct, account_onchain_closeheight(bkpr, cmd, acct)); CHECK(acct->onchain_resolved_block == blockheight + 2); - err = update_channel_onchain_fees(ctx, db, acct); + err = update_channel_onchain_fees(ctx, cmd, bkpr, acct); CHECK_MSG(!err, err); - ofs = account_onchain_fees(ctx, db, acct); db_commit_transaction(db); + ofs = account_get_chain_fees(tmpctx, bkpr, acct->name); /* Expect: fees as follows * @@ -711,7 +1018,7 @@ static bool test_onchain_fee_chan_close(const tal_t *ctx, struct plugin *p) */ CHECK(tal_count(ofs) == 4); for (size_t i = 0; i < tal_count(ofs); i++) { - CHECK(ofs[i]->acct_db_id == acct->db_id); + CHECK(streq(ofs[i]->acct_name, acct->name)); memset(&txid, '1', sizeof(struct bitcoin_txid)); if (bitcoin_txid_eq(&txid, &ofs[i]->txid)) { @@ -748,9 +1055,9 @@ static bool test_onchain_fee_chan_close(const tal_t *ctx, struct plugin *p) return true; } -static bool test_onchain_fee_chan_open(const tal_t *ctx, struct plugin *p) +static bool test_onchain_fee_chan_open(const tal_t *ctx) { - struct db *db = db_setup(ctx, p, tmp_dsn(ctx)); + struct bkpr *bkpr = bkpr_setup(ctx); struct node_id node_id, peer_id; struct account *acct, *acct2, *wal_acct, *ext_acct; struct bitcoin_txid txid; @@ -760,20 +1067,24 @@ static bool test_onchain_fee_chan_open(const tal_t *ctx, struct plugin *p) memset(&node_id, 2, sizeof(struct node_id)); memset(&peer_id, 3, sizeof(struct node_id)); - wal_acct = new_account(ctx, tal_fmt(ctx, ACCOUNT_NAME_WALLET), &peer_id); - ext_acct = new_account(ctx, tal_fmt(ctx, ACCOUNT_NAME_EXTERNAL), &peer_id); - acct = new_account(ctx, tal_fmt(ctx, "chan-1"), &peer_id); - acct2 = new_account(ctx, tal_fmt(ctx, "chan-2"), &peer_id); + wal_acct = new_account(bkpr->accounts, tal_fmt(ctx, ACCOUNT_NAME_WALLET)); + wal_acct->peer_id = &peer_id; + ext_acct = new_account(bkpr->accounts, tal_fmt(ctx, ACCOUNT_NAME_EXTERNAL)); + ext_acct->peer_id = &peer_id; + acct = new_account(bkpr->accounts, tal_fmt(ctx, "chan-1")); + acct->peer_id = &peer_id; + acct2 = new_account(bkpr->accounts, tal_fmt(ctx, "chan-2")); + acct2->peer_id = &peer_id; db_begin_transaction(db); - account_add(db, wal_acct); - account_add(db, ext_acct); - account_add(db, acct); - account_add(db, acct2); + account_datastore_set(cmd, wal_acct, "must-create"); + account_datastore_set(cmd, ext_acct, "must-create"); + account_datastore_set(cmd, acct, "must-create"); + account_datastore_set(cmd, acct2, "must-create"); db_commit_transaction(db); /* Assumption that we rely on later */ - CHECK(acct->db_id < acct2->db_id); + CHECK(strcmp(acct->name, acct2->name) < 0); /* Open two channels from wallet */ /* tag utxo_id vout txid debits credits acct_id @@ -785,14 +1096,14 @@ static bool test_onchain_fee_chan_open(const tal_t *ctx, struct plugin *p) */ memset(&txid, 'A', sizeof(struct bitcoin_txid)); db_begin_transaction(db); - log_chain_event(db, wal_acct, + log_chain_event(bkpr, wal_acct, make_chain_event(ctx, "withdrawal", AMOUNT_MSAT(0), AMOUNT_MSAT(1000), AMOUNT_MSAT(1000), blockheight, 'X', 0, 'A')); - log_chain_event(db, wal_acct, + log_chain_event(bkpr, wal_acct, make_chain_event(ctx, "withdrawal", AMOUNT_MSAT(0), AMOUNT_MSAT(3001), @@ -800,41 +1111,39 @@ static bool test_onchain_fee_chan_open(const tal_t *ctx, struct plugin *p) blockheight, 'Y', 0, 'A')); - log_chain_event(db, acct, + log_chain_event(bkpr, acct, make_chain_event(ctx, "deposit", AMOUNT_MSAT(500), AMOUNT_MSAT(0), AMOUNT_MSAT(500), blockheight, 'A', 0, '*')); - maybe_update_onchain_fees(ctx, db, &txid); + maybe_update_onchain_fees(ctx, cmd, bkpr, &txid); - log_chain_event(db, acct2, + log_chain_event(bkpr, acct2, make_chain_event(ctx, "deposit", AMOUNT_MSAT(1000), AMOUNT_MSAT(0), AMOUNT_MSAT(1000), blockheight, 'A', 1, '*')); - maybe_update_onchain_fees(ctx, db, &txid); + maybe_update_onchain_fees(ctx, cmd, bkpr, &txid); - log_chain_event(db, wal_acct, + log_chain_event(bkpr, wal_acct, make_chain_event(ctx, "deposit", AMOUNT_MSAT(2200), AMOUNT_MSAT(0), AMOUNT_MSAT(2200), blockheight, 'A', 2, '*')); - maybe_update_onchain_fees(ctx, db, &txid); + maybe_update_onchain_fees(ctx, cmd, bkpr, &txid); - maybe_update_onchain_fees(ctx, db, &txid); + maybe_update_onchain_fees(ctx, cmd, bkpr, &txid); db_commit_transaction(db); /* Expect: 5 onchain fee records, totaling to 151/150msat ea, * none for wallet */ - db_begin_transaction(db); - ofs = list_chain_fees(ctx, db); - db_commit_transaction(db); + ofs = list_chain_fees(ctx, bkpr); CHECK(tal_count(ofs) == 5); @@ -868,24 +1177,29 @@ static bool test_onchain_fee_chan_open(const tal_t *ctx, struct plugin *p) return true; } -static bool test_channel_rebalances(const tal_t *ctx, struct plugin *p) +static bool test_channel_rebalances(const tal_t *ctx) { - struct db *db = db_setup(ctx, p, tmp_dsn(ctx)); + struct bkpr *bkpr = bkpr_setup(ctx); + sqlite3 *db = bkpr_db; struct channel_event *ev1, *ev2, *ev3, **chan_evs; - struct rebalance **rebals; + struct rebalance_pair *rebals; + struct rebalance_htable_iter it; struct account *acct1, *acct2, *acct3; struct node_id peer_id; memset(&peer_id, 3, sizeof(struct node_id)); - acct1 = new_account(ctx, tal_fmt(ctx, "one"), &peer_id); - acct2 = new_account(ctx, tal_fmt(ctx, "two"), &peer_id); - acct3 = new_account(ctx, tal_fmt(ctx, "three"), &peer_id); + acct1 = new_account(bkpr->accounts, tal_fmt(ctx, "one")); + acct1->peer_id = &peer_id; + acct2 = new_account(bkpr->accounts, tal_fmt(ctx, "two")); + acct2->peer_id = &peer_id; + acct3 = new_account(bkpr->accounts, tal_fmt(ctx, "three")); + acct3->peer_id = &peer_id; db_begin_transaction(db); - account_add(db, acct1); - account_add(db, acct2); - account_add(db, acct3); + account_datastore_set(cmd, acct1, "must-create"); + account_datastore_set(cmd, acct2, "must-create"); + account_datastore_set(cmd, acct3, "must-create"); /* Simulate a rebalance of 100msats, w/ a 12msat fee */ ev1 = make_channel_event(ctx, "invoice", @@ -911,60 +1225,63 @@ static bool test_channel_rebalances(const tal_t *ctx, struct plugin *p) db_commit_transaction(db); db_begin_transaction(db); - chan_evs = account_get_channel_events(ctx, db, acct1); - CHECK(tal_count(chan_evs) == 1 && !chan_evs[0]->rebalance_id); + chan_evs = account_get_channel_events(ctx, bkpr, cmd, acct1); + CHECK(tal_count(chan_evs) == 1 && !find_rebalance(bkpr, chan_evs[0]->db_id)); - chan_evs = account_get_channel_events(ctx, db, acct2); - CHECK(tal_count(chan_evs) == 1 && !chan_evs[0]->rebalance_id); + chan_evs = account_get_channel_events(ctx, bkpr, cmd, acct2); + CHECK(tal_count(chan_evs) == 1 && !find_rebalance(bkpr, chan_evs[0]->db_id)); - chan_evs = account_get_channel_events(ctx, db, acct3); - CHECK(tal_count(chan_evs) == 1 && !chan_evs[0]->rebalance_id); + chan_evs = account_get_channel_events(ctx, bkpr, cmd, acct3); + CHECK(tal_count(chan_evs) == 1 && !find_rebalance(bkpr, chan_evs[0]->db_id)); - maybe_record_rebalance(db, ev2); - CHECK(ev2->rebalance_id != NULL); + maybe_record_rebalance(cmd, bkpr, ev2); + CHECK(find_rebalance(bkpr, ev2->db_id) != NULL); /* Both events should be marked as rebalance */ - chan_evs = account_get_channel_events(ctx, db, acct1); - CHECK(tal_count(chan_evs) == 1 && chan_evs[0]->rebalance_id); - - chan_evs = account_get_channel_events(ctx, db, acct2); - CHECK(tal_count(chan_evs) == 1 && chan_evs[0]->rebalance_id); + chan_evs = account_get_channel_events(ctx, bkpr, cmd, acct1); + CHECK(tal_count(chan_evs) == 1 && find_rebalance(bkpr, chan_evs[0]->db_id)); + ev1 = chan_evs[0]; + chan_evs = account_get_channel_events(ctx, bkpr, cmd, acct2); + CHECK(tal_count(chan_evs) == 1 && find_rebalance(bkpr, chan_evs[0]->db_id)); + CHECK(*find_rebalance(bkpr, chan_evs[0]->db_id) == ev1->db_id); + CHECK(*find_rebalance(bkpr, ev1->db_id) == chan_evs[0]->db_id); /* Third event is not a rebalance though */ - chan_evs = account_get_channel_events(ctx, db, acct3); - CHECK(tal_count(chan_evs) == 1 && !chan_evs[0]->rebalance_id); + chan_evs = account_get_channel_events(ctx, bkpr, cmd, acct3); + CHECK(tal_count(chan_evs) == 1 && !find_rebalance(bkpr, chan_evs[0]->db_id)); /* Did we get an accurate rebalances entry? */ - rebals = list_rebalances(ctx, db); - - CHECK(tal_count(rebals) == 1); - - CHECK(rebals[0]->in_ev_id == ev1->db_id); - CHECK(rebals[0]->out_ev_id == ev2->db_id); - CHECK(streq(rebals[0]->in_acct_name, "one")); - CHECK(streq(rebals[0]->out_acct_name, "two")); - CHECK(amount_msat_eq(rebals[0]->rebal_msat, AMOUNT_MSAT(100))); - CHECK(amount_msat_eq(rebals[0]->fee_msat, AMOUNT_MSAT(12))); - + assert(rebalance_htable_count(bkpr->rebalances->pairs) == 2); + rebals = rebalance_htable_first(bkpr->rebalances->pairs, &it); + + if (rebals->pair[0] == ev1->db_id) { + CHECK(rebals->pair[1] == ev2->db_id); + } else { + CHECK(rebals->pair[1] == ev1->db_id); + CHECK(rebals->pair[0] == ev2->db_id); + } db_commit_transaction(db); return true; } -static bool test_channel_event_crud(const tal_t *ctx, struct plugin *p) +static bool test_channel_event_crud(const tal_t *ctx) { - struct db *db = db_setup(ctx, p, tmp_dsn(ctx)); + struct bkpr *bkpr = bkpr_setup(ctx); + sqlite3 *db = bkpr_db; struct node_id peer_id; struct account *acct, *acct2; struct channel_event *ev1, *ev2, *ev3, **chan_evs; memset(&peer_id, 3, sizeof(struct node_id)); - acct = new_account(ctx, tal_fmt(ctx, "example"), &peer_id); - acct2 = new_account(ctx, tal_fmt(ctx, ACCOUNT_NAME_WALLET), &peer_id); + acct = new_account(bkpr->accounts, tal_fmt(ctx, "example")); + acct->peer_id = &peer_id; + acct2 = new_account(bkpr->accounts, tal_fmt(ctx, ACCOUNT_NAME_WALLET)); + acct2->peer_id = &peer_id; db_begin_transaction(db); - account_add(db, acct); - account_add(db, acct2); + account_datastore_set(cmd, acct, "must-create"); + account_datastore_set(cmd, acct2, "must-create"); db_commit_transaction(db); ev1 = tal(ctx, struct channel_event); @@ -975,12 +1292,9 @@ static bool test_channel_event_crud(const tal_t *ctx, struct plugin *p) ev1->fees = AMOUNT_MSAT(104); ev1->timestamp = 11111; ev1->part_id = 19; - ev1->desc = tal_strdup(ev1, "hello desc1"); - ev1->rebalance_id = NULL; /* Passing unknown tags in should be ok */ ev1->tag = "hello"; - ev1->desc = tal_fmt(ev1, "desc"); ev2 = tal(ctx, struct channel_event); ev2->payment_id = tal(ev2, struct sha256); @@ -991,9 +1305,6 @@ static bool test_channel_event_crud(const tal_t *ctx, struct plugin *p) ev2->timestamp = 22222; ev2->part_id = 0; ev2->tag = tal_fmt(ev2, "deposit"); - ev2->desc = NULL; - ev2->rebalance_id = tal(ev2, u64); - *ev2->rebalance_id = 1; ev3 = tal(ctx, struct channel_event); ev3->payment_id = tal(ev3, struct sha256); @@ -1004,8 +1315,6 @@ static bool test_channel_event_crud(const tal_t *ctx, struct plugin *p) ev3->timestamp = 33333; ev3->part_id = 5; ev3->tag = tal_fmt(ev3, "routed"); - ev3->desc = NULL; - ev3->rebalance_id = NULL; db_begin_transaction(db); log_channel_event(db, acct, ev1); @@ -1021,7 +1330,7 @@ static bool test_channel_event_crud(const tal_t *ctx, struct plugin *p) db_commit_transaction(db); db_begin_transaction(db); - chan_evs = account_get_channel_events(ctx, db, acct); + chan_evs = account_get_channel_events(ctx, bkpr, cmd, acct); db_commit_transaction(db); CHECK(streq(acct->name, chan_evs[0]->acct_name)); @@ -1034,9 +1343,9 @@ static bool test_channel_event_crud(const tal_t *ctx, struct plugin *p) return true; } -static bool test_chain_event_crud(const tal_t *ctx, struct plugin *p) +static bool test_chain_event_crud(const tal_t *ctx) { - struct db *db = db_setup(ctx, p, tmp_dsn(ctx)); + struct bkpr *bkpr = bkpr_setup(ctx); struct node_id peer_id; struct account *acct, *acct2; struct chain_event *ev1, *ev2, *ev3, **chain_evs; @@ -1045,11 +1354,13 @@ static bool test_chain_event_crud(const tal_t *ctx, struct plugin *p) ev2 = tal(ctx, struct chain_event); memset(&peer_id, 3, sizeof(struct node_id)); - acct = new_account(ctx, name, &peer_id); - acct2 = new_account(ctx, tal_fmt(ctx, ACCOUNT_NAME_WALLET), &peer_id); + acct = new_account(bkpr->accounts, name); + acct->peer_id = &peer_id; + acct2 = new_account(bkpr->accounts, tal_fmt(ctx, ACCOUNT_NAME_WALLET)); + acct2->peer_id = &peer_id; db_begin_transaction(db); - account_add(db, acct); - account_add(db, acct2); + account_datastore_set(cmd, acct, "must-create"); + account_datastore_set(cmd, acct2, "must-create"); db_commit_transaction(db); /* This event spends the second inserted event */ @@ -1068,10 +1379,9 @@ static bool test_chain_event_crud(const tal_t *ctx, struct plugin *p) ev1->spending_txid = tal(ctx, struct bitcoin_txid); memset(ev1->spending_txid, 'C', sizeof(struct bitcoin_txid)); ev1->payment_id = NULL; - ev1->desc = tal_fmt(ev1, "description"); db_begin_transaction(db); - log_chain_event(db, acct, ev1); + log_chain_event(bkpr, acct, ev1); db_commit_transaction(db); ev2->tag = tal_fmt(ctx, "deposit"); @@ -1087,7 +1397,6 @@ static bool test_chain_event_crud(const tal_t *ctx, struct plugin *p) ev2->outpoint.n = 1; ev2->spending_txid = NULL; ev2->payment_id = tal(ctx, struct sha256); - ev2->desc = NULL; memset(ev2->payment_id, 'B', sizeof(struct sha256)); /* Dummy event, logged to separate account */ @@ -1106,23 +1415,22 @@ static bool test_chain_event_crud(const tal_t *ctx, struct plugin *p) ev3->spending_txid = tal(ctx, struct bitcoin_txid); memset(ev3->spending_txid, 'D', sizeof(struct bitcoin_txid)); ev3->payment_id = NULL; - ev3->desc = NULL; db_begin_transaction(db); - log_chain_event(db, acct, ev2); + log_chain_event(bkpr, acct, ev2); /* log new event to a different account.. */ - log_chain_event(db, acct2, ev3); + log_chain_event(bkpr, acct2, ev3); db_commit_transaction(db); /* Try to add an already exiting event */ db_begin_transaction(db); - log_chain_event(db, acct, ev2); + log_chain_event(bkpr, acct, ev2); db_commit_transaction(db); /* Ok now we ge the list, there should only be two */ db_begin_transaction(db); - chain_evs = account_get_chain_events(ctx, db, acct); + chain_evs = account_get_chain_events(ctx, bkpr, NULL, acct); db_commit_transaction(db); CHECK(tal_count(chain_evs) == 2); @@ -1145,9 +1453,9 @@ static bool test_chain_event_crud(const tal_t *ctx, struct plugin *p) db_begin_transaction(db); - log_chain_event(db, acct, ev1); - log_chain_event(db, acct, ev2); - chain_evs = account_get_chain_events(ctx, db, acct); + log_chain_event(bkpr, acct, ev1); + log_chain_event(bkpr, acct, ev2); + chain_evs = account_get_chain_events(ctx, bkpr, NULL, acct); db_commit_transaction(db); /* There should be four now */ @@ -1165,20 +1473,20 @@ struct acct_balance { struct amount_msat balance; }; -static bool account_get_balance(struct plugin *plugin, - struct db *db, +static bool account_get_balance(struct bkpr *bkpr, const char *acct_name, struct acct_balance *bal) { - account_get_credit_debit(plugin, db, acct_name, + account_get_credit_debit(bkpr, NULL, acct_name, &bal->credit, &bal->debit); return amount_msat_sub(&bal->balance, bal->credit, bal->debit); } -static bool test_account_balances(const tal_t *ctx, struct plugin *p) +static bool test_account_balances(const tal_t *ctx) { - struct db *db = db_setup(ctx, p, tmp_dsn(ctx)); + struct bkpr *bkpr = bkpr_setup(ctx); + sqlite3 *db = bkpr_db; struct node_id peer_id; struct account *acct, *acct2; struct chain_event *ev1; @@ -1187,19 +1495,21 @@ static bool test_account_balances(const tal_t *ctx, struct plugin *p) memset(&peer_id, 3, sizeof(struct node_id)); - acct = new_account(ctx, tal_fmt(ctx, "example"), &peer_id); - acct2 = new_account(ctx, tal_fmt(ctx, ACCOUNT_NAME_WALLET), &peer_id); + acct = new_account(bkpr->accounts, tal_fmt(ctx, "example")); + acct->peer_id = &peer_id; + acct2 = new_account(bkpr->accounts, tal_fmt(ctx, ACCOUNT_NAME_WALLET)); + acct2->peer_id = &peer_id; db_begin_transaction(db); /* Check that account does not exist yet */ - ok = account_get_balance(NULL, db, acct->name, &balance); + ok = account_get_balance(bkpr, acct->name, &balance); CHECK(ok); - account_add(db, acct); - account_add(db, acct2); + account_datastore_set(cmd, acct, "must-create"); + account_datastore_set(cmd, acct2, "must-create"); /* +1000btc */ - log_chain_event(db, acct, + log_chain_event(bkpr, acct, make_chain_event(ctx, "one", AMOUNT_MSAT(1000), AMOUNT_MSAT(0), @@ -1212,7 +1522,7 @@ static bool test_account_balances(const tal_t *ctx, struct plugin *p) 1020, 'A', 2, '*'); /* -999btc */ - log_chain_event(db, acct, ev1); + log_chain_event(bkpr, acct, ev1); /* -440btc */ log_channel_event(db, acct, @@ -1228,7 +1538,7 @@ static bool test_account_balances(const tal_t *ctx, struct plugin *p) AMOUNT_MSAT(0), 'D')); - ok = account_get_balance(NULL, db, acct->name, &balance); + ok = account_get_balance(bkpr, acct->name, &balance); CHECK(ok); db_commit_transaction(db); @@ -1241,18 +1551,18 @@ static bool test_account_balances(const tal_t *ctx, struct plugin *p) AMOUNT_MSAT(0), AMOUNT_MSAT(5001), AMOUNT_MSAT(5001), 2020, 'A', 4, '*'); - log_chain_event(db, acct, ev1); + log_chain_event(bkpr, acct, ev1); - ok = account_get_balance(NULL, db, acct->name, &balance); + ok = account_get_balance(bkpr, acct->name, &balance); CHECK(!ok); db_commit_transaction(db); return true; } -static bool test_account_crud(const tal_t *ctx, struct plugin *p) +static bool test_account_crud(const tal_t *ctx) { - struct db *db = db_setup(ctx, p, tmp_dsn(ctx)); + struct bkpr *bkpr = bkpr_setup(ctx); struct node_id *peer_id; struct account *acct, *acct2, **acct_list; struct chain_event *ev1; @@ -1262,35 +1572,29 @@ static bool test_account_crud(const tal_t *ctx, struct plugin *p) peer_id = tal(ctx, struct node_id); memset(peer_id, 3, sizeof(struct node_id)); - acct = new_account(ctx, name, NULL); + acct = new_account(bkpr->accounts, name); CHECK(!acct->is_wallet); db_begin_transaction(db); - account_add(db, acct); + account_datastore_set(cmd, acct, "must-create"); db_commit_transaction(db); - db_begin_transaction(db); - acct_list = list_accounts(ctx, db); - db_commit_transaction(db); + acct_list = list_accounts(ctx, bkpr); CHECK(tal_count(acct_list) == 1); accountseq(acct_list[0], acct); - acct = new_account(ctx, tal_fmt(ctx, ACCOUNT_NAME_WALLET), NULL); + acct = new_account(bkpr->accounts, tal_fmt(ctx, ACCOUNT_NAME_WALLET)); CHECK(acct->is_wallet); db_begin_transaction(db); - account_add(db, acct); + account_datastore_set(cmd, acct, "must-create"); db_commit_transaction(db); - db_begin_transaction(db); - acct_list = list_accounts(ctx, db); - db_commit_transaction(db); + acct_list = list_accounts(ctx, bkpr); CHECK(tal_count(acct_list) == 2); /* Can we find an account ok? */ - db_begin_transaction(db); - acct2 = find_account(ctx, db, ACCOUNT_NAME_WALLET); - db_commit_transaction(db); + acct2 = find_account(bkpr, ACCOUNT_NAME_WALLET); accountseq(acct, acct2); /* Will we update an account's properties @@ -1310,18 +1614,17 @@ static bool test_account_crud(const tal_t *ctx, struct plugin *p) ev1->spending_txid = tal(ctx, struct bitcoin_txid); memset(ev1->spending_txid, 'C', sizeof(struct bitcoin_txid)); ev1->payment_id = NULL; - ev1->desc = tal_fmt(ev1, "oh hello"); db_begin_transaction(db); - log_chain_event(db, acct, ev1); + log_chain_event(bkpr, acct, ev1); tags = tal_arr(ctx, enum mvt_tag, 2); /* should not update the account info */ tags[0] = MVT_PUSHED; tags[1] = MVT_PENALTY; - maybe_update_account(db, acct, ev1, tags, 0, peer_id); - acct2 = find_account(ctx, db, ACCOUNT_NAME_WALLET); + maybe_update_account(cmd, acct, ev1, tags, 0, peer_id); + acct2 = find_account(bkpr, ACCOUNT_NAME_WALLET); accountseq(acct, acct2); /* channel_open -> open event db updated */ @@ -1329,8 +1632,8 @@ static bool test_account_crud(const tal_t *ctx, struct plugin *p) CHECK(acct->open_event_db_id == NULL); tags[0] = MVT_CHANNEL_OPEN; tags[1] = MVT_LEASED; - maybe_update_account(db, acct, ev1, tags, 2, peer_id); - acct2 = find_account(ctx, db, ACCOUNT_NAME_WALLET); + maybe_update_account(cmd, acct, ev1, tags, 2, peer_id); + acct2 = find_account(bkpr, ACCOUNT_NAME_WALLET); accountseq(acct, acct2); CHECK(acct->leased); CHECK(acct->open_event_db_id != NULL); @@ -1340,8 +1643,8 @@ static bool test_account_crud(const tal_t *ctx, struct plugin *p) tags[1] = MVT_OPENER; CHECK(acct->closed_event_db_id == NULL); CHECK(!acct->we_opened); - maybe_update_account(db, acct, ev1, tags, 0, NULL); - acct2 = find_account(ctx, db, ACCOUNT_NAME_WALLET); + maybe_update_account(cmd, acct, ev1, tags, 0, NULL); + acct2 = find_account(bkpr, ACCOUNT_NAME_WALLET); accountseq(acct, acct2); CHECK(acct->closed_event_db_id != NULL); CHECK(acct->we_opened); @@ -1354,25 +1657,23 @@ static bool test_account_crud(const tal_t *ctx, struct plugin *p) int main(int argc, char *argv[]) { bool ok = true; - /* Dummy for migration hooks */ - struct plugin *plugin = tal(NULL, struct plugin); - list_head_init(&plugin->js_list); - plugin->developer = true; common_setup(argv[0]); if (HAVE_SQLITE3) { - ok &= test_account_crud(tmpctx, plugin); - ok &= test_channel_event_crud(tmpctx, plugin); - ok &= test_chain_event_crud(tmpctx, plugin); - ok &= test_account_balances(tmpctx, plugin); - ok &= test_onchain_fee_chan_close(tmpctx, plugin); - ok &= test_onchain_fee_chan_open(tmpctx, plugin); - ok &= test_channel_rebalances(tmpctx, plugin); - ok &= test_onchain_fee_wallet_spend(tmpctx, plugin); + /* UBSan insists cmd isn't NULL */ + cmd = tal(tmpctx, struct command); + ok &= test_account_crud(tmpctx); + ok &= test_channel_event_crud(tmpctx); + ok &= test_chain_event_crud(tmpctx); + ok &= test_account_balances(tmpctx); + ok &= test_onchain_fee_chan_close(tmpctx); + ok &= test_onchain_fee_chan_open(tmpctx); + ok &= test_channel_rebalances(tmpctx); + ok &= test_onchain_fee_wallet_spend(tmpctx); + sqlite3_close(bkpr_db); } - tal_free(plugin); common_shutdown(); trace_cleanup(); return !ok; diff --git a/plugins/bkpr/test/run-sql.c b/plugins/bkpr/test/run-sql.c new file mode 100644 index 000000000000..8be5859adad7 --- /dev/null +++ b/plugins/bkpr/test/run-sql.c @@ -0,0 +1,130 @@ +#include "config.h" + +#include "plugins/bkpr/sql.c" +#include "plugins/libplugin.c" + +#include +#include +#include +#include +#include +#include +#include + +/* AUTOGENERATED MOCKS START */ +/* Generated stub for daemon_developer_mode */ +bool daemon_developer_mode(char *argv[]) +{ fprintf(stderr, "daemon_developer_mode called!\n"); abort(); } +/* Generated stub for daemon_setup */ +void daemon_setup(const char *argv0 UNNEEDED, + void (*backtrace_print)(const char *fmt UNNEEDED, ...) UNNEEDED, + void (*backtrace_exit)(void)) +{ fprintf(stderr, "daemon_setup called!\n"); abort(); } +/* Generated stub for deprecated_ok_ */ +bool deprecated_ok_(bool deprecated_apis UNNEEDED, + const char *feature UNNEEDED, + const char *start UNNEEDED, + const char *end UNNEEDED, + const char **begs UNNEEDED, + void (*complain)(const char *feat UNNEEDED, bool allowing UNNEEDED, void *) UNNEEDED, + void *cbarg UNNEEDED) +{ fprintf(stderr, "deprecated_ok_ called!\n"); abort(); } +/* Generated stub for find_blockheight */ +u32 find_blockheight(const struct bkpr *bkpr UNNEEDED, const struct bitcoin_txid *txid UNNEEDED) +{ fprintf(stderr, "find_blockheight called!\n"); abort(); } +/* Generated stub for first_fee_state */ +enum htlc_state first_fee_state(enum side opener UNNEEDED) +{ fprintf(stderr, "first_fee_state called!\n"); abort(); } +/* Generated stub for fmt_channel_id */ +char *fmt_channel_id(const tal_t *ctx UNNEEDED, const struct channel_id *channel_id UNNEEDED) +{ fprintf(stderr, "fmt_channel_id called!\n"); abort(); } +/* Generated stub for fmt_wireaddr_without_port */ +char *fmt_wireaddr_without_port(const tal_t *ctx UNNEEDED, const struct wireaddr *a UNNEEDED) +{ fprintf(stderr, "fmt_wireaddr_without_port called!\n"); abort(); } +/* Generated stub for fromwire_wireaddr */ +bool fromwire_wireaddr(const u8 **cursor UNNEEDED, size_t *max UNNEEDED, struct wireaddr *addr UNNEEDED) +{ fprintf(stderr, "fromwire_wireaddr called!\n"); abort(); } +/* Generated stub for htlc_state_flags */ +int htlc_state_flags(enum htlc_state state UNNEEDED) +{ fprintf(stderr, "htlc_state_flags called!\n"); abort(); } +/* Generated stub for htlc_state_name */ +const char *htlc_state_name(enum htlc_state s UNNEEDED) +{ fprintf(stderr, "htlc_state_name called!\n"); abort(); } +/* Generated stub for is_asterix_notification */ +bool is_asterix_notification(const char *notification_name UNNEEDED, + const char *subscriptions UNNEEDED) +{ fprintf(stderr, "is_asterix_notification called!\n"); abort(); } +/* Generated stub for json_filter_down */ +bool json_filter_down(struct json_filter **filter UNNEEDED, const char *member UNNEEDED) +{ fprintf(stderr, "json_filter_down called!\n"); abort(); } +/* Generated stub for json_filter_finished */ +bool json_filter_finished(const struct json_filter *filter UNNEEDED) +{ fprintf(stderr, "json_filter_finished called!\n"); abort(); } +/* Generated stub for json_filter_misused */ +const char *json_filter_misused(const tal_t *ctx UNNEEDED, const struct json_filter *f UNNEEDED) +{ fprintf(stderr, "json_filter_misused called!\n"); abort(); } +/* Generated stub for json_filter_ok */ +bool json_filter_ok(const struct json_filter *filter UNNEEDED, const char *member UNNEEDED) +{ fprintf(stderr, "json_filter_ok called!\n"); abort(); } +/* Generated stub for json_filter_up */ +bool json_filter_up(struct json_filter **filter UNNEEDED) +{ fprintf(stderr, "json_filter_up called!\n"); abort(); } +/* Generated stub for last_fee_state */ +enum htlc_state last_fee_state(enum side opener UNNEEDED) +{ fprintf(stderr, "last_fee_state called!\n"); abort(); } +/* Generated stub for log_level_name */ +const char *log_level_name(enum log_level level UNNEEDED) +{ fprintf(stderr, "log_level_name called!\n"); abort(); } +/* Generated stub for new_channel_event */ +struct channel_event *new_channel_event(const tal_t *ctx UNNEEDED, + const char *tag UNNEEDED, + struct amount_msat credit UNNEEDED, + struct amount_msat debit UNNEEDED, + struct amount_msat fees UNNEEDED, + const struct sha256 *payment_id TAKES UNNEEDED, + u32 part_id UNNEEDED, + u64 timestamp UNNEEDED) +{ fprintf(stderr, "new_channel_event called!\n"); abort(); } +/* Generated stub for param_check */ +bool param_check(struct command *cmd UNNEEDED, + const char *buffer UNNEEDED, + const jsmntok_t tokens[] UNNEEDED, ...) +{ fprintf(stderr, "param_check called!\n"); abort(); } +/* Generated stub for parse_filter */ +struct command_result *parse_filter(struct command *cmd UNNEEDED, + const char *name UNNEEDED, + const char *buffer UNNEEDED, + const jsmntok_t *tok UNNEEDED) +{ fprintf(stderr, "parse_filter called!\n"); abort(); } +/* AUTOGENERATED MOCKS END */ + +int main(int argc, char *argv[]) +{ + common_setup(argv[0]); + /* No quote: should return same pointer */ + const char *s1 = "simple"; + const char *r1 = sql_string(tmpctx, s1); + assert(r1 == s1); + + /* One quote: should return new string with doubled quote */ + const char *s2 = "O'Reilly"; + const char *r2 = sql_string(tmpctx, s2); + assert(strcmp(r2, "O''Reilly") == 0); + assert(r2 != s2); // New allocation + + /* Multiple quotes */ + const char *s3 = "'a'b'c'"; + const char *r3 = sql_string(tmpctx, s3); + assert(strcmp(r3, "''a''b''c''") == 0); + + /* All quotes */ + const char *s4 = "''''"; + const char *r4 = sql_string(tmpctx, s4); + assert(strcmp(r4, "''''''''") == 0); + + /* Empty string: should return same pointer */ + const char *s5 = ""; + const char *r5 = sql_string(tmpctx, s5); + assert(r5 == s5); + common_shutdown(); +} diff --git a/plugins/libplugin.c b/plugins/libplugin.c index dbe58d2cafb6..36a74d9f8134 100644 --- a/plugins/libplugin.c +++ b/plugins/libplugin.c @@ -187,6 +187,27 @@ static struct command *new_command(const tal_t *ctx, return cmd; } +static int rpc_open(struct plugin *plugin) +{ + struct sockaddr_un addr; + int fd = socket(AF_UNIX, SOCK_STREAM, 0); + + if (strlen(plugin->rpc_location) + 1 > sizeof(addr.sun_path)) + plugin_err(plugin, "rpc filename '%s' too long", + plugin->rpc_location); + strcpy(addr.sun_path, plugin->rpc_location); + addr.sun_family = AF_UNIX; + + if (connect(fd, (struct sockaddr *)&addr, sizeof(addr)) != 0) { + plugin_log(plugin, LOG_UNUSUAL, + "Could not connect to '%s': %s", + plugin->rpc_location, strerror(errno)); + close(fd); + fd = -1; + } + return fd; +} + static void complain_deprecated(const char *feature, bool allowing, struct command *cmd) @@ -532,6 +553,8 @@ struct json_out *json_out_obj(const tal_t *ctx, json_out_start(jout, NULL, '{'); if (str) json_out_addstr(jout, fieldname, str); + if (taken(str)) + tal_free(str); json_out_end(jout, '}'); json_out_finished(jout); @@ -734,14 +757,14 @@ void command_set_usage(struct command *cmd, const char *usage TAKES) } /* Reads rpc reply and returns tokens, setting contents to 'error' or - * 'result' (depending on *error). */ -static const jsmntok_t *read_rpc_reply(const tal_t *ctx, - struct plugin *plugin, - const jsmntok_t **contents, - bool *error, - int *reqlen) +- * 'result' (depending on *error). */ +static jsmntok_t *read_rpc_reply(const tal_t *ctx, + struct plugin *plugin, + const jsmntok_t **contents, + bool *error, + int *reqlen) { - const jsmntok_t *toks; + jsmntok_t *toks; do { *reqlen = read_json_from_rpc(plugin); @@ -778,38 +801,56 @@ static const jsmntok_t *sync_req(const tal_t *ctx, const char **resp) { bool error; + jsmntok_t *toks; const jsmntok_t *contents; int reqlen; struct json_out *jout = json_out_new(tmpctx); const char *id = json_id(tmpctx, plugin, "init/", method); + size_t num_toks; json_out_start(jout, NULL, '{'); json_out_addstr(jout, "jsonrpc", "2.0"); /* Copy in id *literally* */ memcpy(json_out_member_direct(jout, "id", strlen(id)), id, strlen(id)); json_out_addstr(jout, "method", method); - json_out_add_splice(jout, "params", params); + if (params) + json_out_add_splice(jout, "params", params); + else { + json_out_start(jout, "params", '{'); + json_out_end(jout, '}'); + } if (taken(params)) tal_free(params); + + /* If we're past init, we may need a new fd (the old one + * is being used for async comms). */ + if (plugin->rpc_conn->fd == -1) + plugin->rpc_conn->fd = rpc_open(plugin); + finish_and_send_json(plugin->rpc_conn->fd, jout); - read_rpc_reply(ctx, plugin, &contents, &error, &reqlen); + toks = read_rpc_reply(ctx, plugin, &contents, &error, &reqlen); if (error) plugin_err(plugin, "Got error reply to %s: '%.*s'", method, reqlen, membuf_elems(&plugin->rpc_conn->mb)); *resp = membuf_consume(&plugin->rpc_conn->mb, reqlen); - return contents; + + /* Make the returned pointer the valid tal object of minimal length */ + num_toks = json_next(contents) - contents; + memmove(toks, contents, num_toks * sizeof(*toks)); + tal_resize(&toks, num_toks); + return toks; } const jsmntok_t *jsonrpc_request_sync(const tal_t *ctx, - struct command *init_cmd, + struct command *cmd, const char *method, const struct json_out *params TAKES, const char **resp) { - assert(streq(init_cmd->methodname, "init")); - return sync_req(ctx, init_cmd->plugin, method, params, resp); + + return sync_req(ctx, cmd->plugin, method, params, resp); } /* Returns contents of scanning guide on 'result' */ @@ -828,7 +869,7 @@ static const char *rpc_scan_core(const tal_t *ctx, } /* Synchronous routine to send command and extract fields from response */ -void rpc_scan(struct command *init_cmd, +void rpc_scan(struct command *cmd, const char *method, const struct json_out *params TAKES, const char *guide, @@ -837,13 +878,12 @@ void rpc_scan(struct command *init_cmd, const char *err; va_list ap; - assert(streq(init_cmd->methodname, "init")); va_start(ap, guide); - err = rpc_scan_core(tmpctx, init_cmd->plugin, method, params, guide, ap); + err = rpc_scan_core(tmpctx, cmd->plugin, method, params, guide, ap); va_end(ap); if (err) - plugin_err(init_cmd->plugin, "Could not parse %s in reply to %s: %s", + plugin_err(cmd->plugin, "Could not parse %s in reply to %s: %s", guide, method, err); } @@ -858,7 +898,7 @@ static void json_add_keypath(struct json_out *jout, const char *fieldname, const } static const char *rpc_scan_datastore(const tal_t *ctx, - struct command *init_cmd, + struct command *cmd, const char *path, const char *hex_or_string, va_list ap) @@ -866,7 +906,6 @@ static const char *rpc_scan_datastore(const tal_t *ctx, const char *guide; struct json_out *params; - assert(streq(init_cmd->methodname, "init")); params = json_out_new(NULL); json_out_start(params, NULL, '{'); json_add_keypath(params, "key", path); @@ -874,12 +913,12 @@ static const char *rpc_scan_datastore(const tal_t *ctx, json_out_finished(params); guide = tal_fmt(tmpctx, "{datastore:[0:{%s:%%}]}", hex_or_string); - return rpc_scan_core(ctx, init_cmd->plugin, "listdatastore", take(params), + return rpc_scan_core(ctx, cmd->plugin, "listdatastore", take(params), guide, ap); } const char *rpc_scan_datastore_str(const tal_t *ctx, - struct command *init_cmd, + struct command *cmd, const char *path, ...) { @@ -887,14 +926,14 @@ const char *rpc_scan_datastore_str(const tal_t *ctx, va_list ap; va_start(ap, path); - ret = rpc_scan_datastore(ctx, init_cmd, path, "string", ap); + ret = rpc_scan_datastore(ctx, cmd, path, "string", ap); va_end(ap); return ret; } /* This variant scans the hex encoding, not the string */ const char *rpc_scan_datastore_hex(const tal_t *ctx, - struct command *init_cmd, + struct command *cmd, const char *path, ...) { @@ -902,7 +941,7 @@ const char *rpc_scan_datastore_hex(const tal_t *ctx, va_list ap; va_start(ap, path); - ret = rpc_scan_datastore(ctx, init_cmd, path, "hex", ap); + ret = rpc_scan_datastore(ctx, cmd, path, "hex", ap); va_end(ap); return ret; } @@ -1038,13 +1077,12 @@ static void destroy_cmd_mark_freed(struct command *cmd, bool *cmd_freed) *cmd_freed = true; } -static void handle_rpc_reply(struct plugin *plugin, const jsmntok_t *toks) +static void handle_rpc_reply(struct plugin *plugin, const char *buf, const jsmntok_t *toks) { const jsmntok_t *idtok, *contenttok; struct out_req *out; struct command_result *res; bool cmd_freed; - const char *buf = plugin->rpc_buffer + plugin->rpc_read_offset; idtok = json_get_member(buf, toks, "id"); if (!idtok) @@ -1397,7 +1435,7 @@ static bool rpc_read_response_one(struct plugin *plugin) plugin->rpc_buffer + plugin->rpc_read_offset); } - handle_rpc_reply(plugin, plugin->rpc_toks); + handle_rpc_reply(plugin, plugin->rpc_buffer + plugin->rpc_read_offset, plugin->rpc_toks); /* Move this object out of the buffer */ plugin->rpc_read_offset += plugin->rpc_toks[0].end; @@ -1547,11 +1585,10 @@ static struct command_result *handle_init(struct command *cmd, const jsmntok_t *params) { const jsmntok_t *configtok, *opttok, *t; - struct sockaddr_un addr; size_t i; char *dir, *network; struct plugin *p = cmd->plugin; - bool with_rpc = p->rpc_conn != NULL; + bool with_rpc; const char *err; configtok = json_get_member(buf, params, "configuration"); @@ -1578,28 +1615,17 @@ static struct command_result *handle_init(struct command *cmd, /* Only attempt to connect if the plugin has configured the rpc_conn * already, if that's not the case we were told to run without an RPC * connection, so don't even log an error. */ - /* FIXME: Move this to its own function so we can initialize at a - * later point in time. */ if (p->rpc_conn != NULL) { - p->rpc_conn->fd = socket(AF_UNIX, SOCK_STREAM, 0); - if (strlen(p->rpc_location) + 1 > sizeof(addr.sun_path)) - plugin_err(p, "rpc filename '%s' too long", - p->rpc_location); - strcpy(addr.sun_path, p->rpc_location); - addr.sun_family = AF_UNIX; - - if (connect(p->rpc_conn->fd, (struct sockaddr *)&addr, - sizeof(addr)) != 0) { + p->rpc_conn->fd = rpc_open(p); + if (p->rpc_conn->fd == -1) with_rpc = false; - plugin_log(p, LOG_UNUSUAL, - "Could not connect to '%s': %s", - p->rpc_location, strerror(errno)); - } + else + with_rpc = true; membuf_init(&p->rpc_conn->mb, tal_arr(p, char, READ_CHUNKSIZE), READ_CHUNKSIZE, membuf_tal_realloc); - - } + } else + with_rpc = false; opttok = json_get_member(buf, params, "options"); json_for_each_obj(i, t, opttok) { @@ -1633,6 +1659,9 @@ static struct command_result *handle_init(struct command *cmd, get_beglist, plugin_broken_cb, NULL); json_add_string(req->js, "config", "i-promise-to-fix-broken-api-user"); send_outreq(req); + + /* We will open a new one if we want to be sync. */ + p->rpc_conn->fd = -1; } return command_success(cmd, json_out_obj(cmd, NULL, NULL)); diff --git a/plugins/libplugin.h b/plugins/libplugin.h index 80261ab2ab85..550429950995 100644 --- a/plugins/libplugin.h +++ b/plugins/libplugin.h @@ -369,7 +369,7 @@ struct command_result *command_still_pending(struct command *cmd) * object is empty. */ struct json_out *json_out_obj(const tal_t *ctx, const char *fieldname, - const char *str); + const char *str TAKES); /* Return this iff the param() call failed in your handler. */ struct command_result *command_param_failed(void); @@ -445,24 +445,24 @@ bool command_deprecated_ok_flag(const struct command *cmd) #define notification_handler_pending(cmd) command_still_pending(cmd) /* Synchronous helper to send command and extract fields from - * response; can only be used in init callback. */ -void rpc_scan(struct command *init_cmd, + * response. */ +void rpc_scan(struct command *cmd, const char *method, const struct json_out *params TAKES, const char *guide, ...); -/* Helper to scan datastore: can only be used in init callback. Returns error - * msg (usually meaning field does not exist), or NULL on success. path is - * /-separated. Final arg is JSON_SCAN or JSON_SCAN_TAL. +/* Helper to scan datastore. Returns error msg (usually meaning field + * does not exist), or NULL on success. path is /-separated. Final + * arg is JSON_SCAN or JSON_SCAN_TAL. */ const char *rpc_scan_datastore_str(const tal_t *ctx, - struct command *init_cmd, + struct command *cmd, const char *path, ...); /* This variant scans the hex encoding, not the string */ const char *rpc_scan_datastore_hex(const tal_t *ctx, - struct command *init_cmd, + struct command *cmd, const char *path, ...); @@ -706,9 +706,9 @@ void plugin_set_memleak_handler(struct plugin *plugin, struct htable *memtable)); /* Synchronously call a JSON-RPC method and return its contents and - * the parser token. */ + * the parser token. params may be NULL for an empty object. */ const jsmntok_t *jsonrpc_request_sync(const tal_t *ctx, - struct command *init_cmd, + struct command *cmd, const char *method, const struct json_out *params TAKES, const char **resp); diff --git a/plugins/sql.c b/plugins/sql.c index d61452f4b91c..7b7e21bf4793 100644 --- a/plugins/sql.c +++ b/plugins/sql.c @@ -27,11 +27,10 @@ static const char schemas[] = /* TODO: * 2. Refresh time in API. - * 6. test on mainnet. - * 7. Some cool query for documentation. * 8. time_msec fields. - * 10. Pagination API - */ + * 10. General pagination API (not just chainmoves and channelmoves) + * 11. Normalize account_id fields into another table, as they are highly duplicate, and use views to maintain the current API. +*/ enum fieldtype { /* Hex variants */ FIELD_HEX, @@ -51,6 +50,7 @@ enum fieldtype { FIELD_NUMBER, FIELD_STRING, FIELD_SCID, + FIELD_OUTPOINT, }; struct fieldtypemap { @@ -74,6 +74,7 @@ static const struct fieldtypemap fieldtypemap[] = { { "number", "REAL" }, /* FIELD_NUMBER */ { "string", "TEXT" }, /* FIELD_STRING */ { "short_channel_id", "TEXT" }, /* FIELD_SCID */ + { "outpoint", "TEXT" }, /* FIELD_OUTPOINT */ }; struct column { @@ -94,6 +95,15 @@ struct db_query { struct table_desc **tables; const char *authfail; bool has_wildcard; + /* Update *last_created_index */ + u64 *last_created_index; +}; + +/* Waiting for another command to refresh table */ +struct refresh_waiter { + struct list_node list; + struct command *cmd; + struct db_query *dbq; }; struct table_desc { @@ -110,10 +120,18 @@ struct table_desc { struct table_desc *parent; /* Is this a sub object (otherwise, subarray if parent is true) */ bool is_subobject; + /* Do we use created_index as primary key? Otherwise we create rowid. */ + bool has_created_index; /* function to refresh it. */ struct command_result *(*refresh)(struct command *cmd, const struct table_desc *td, struct db_query *dbq); + /* some refresh functions maintain changed and created indexes */ + u64 last_created_index; + /* Are we refreshing now? */ + bool refreshing; + /* Any other commands waiting for the refresh completion */ + struct list_head refresh_waiters; }; static STRMAP(struct table_desc *) tablemap; static size_t max_dbmem = 500000000; @@ -171,6 +189,14 @@ static const struct index indices[] = { "transactions", { "hash", NULL }, }, + { + "chainmoves", + { "account_id", NULL }, + }, + { + "channelmoves", + { "account_id", NULL }, + }, }; static enum fieldtype find_fieldtype(const jsmntok_t *name) @@ -467,6 +493,29 @@ static struct command_result *refresh_tables(struct command *cmd, static struct command_result *one_refresh_done(struct command *cmd, struct db_query *dbq) { + struct table_desc *td = dbq->tables[0]; + struct list_head waiters; + struct refresh_waiter *rw; + + /* We are no longer refreshing */ + assert(td->refreshing); + td->refreshing = false; + + /* Transfer refresh waiters onto local list */ + list_head_init(&waiters); + list_append_list(&waiters, &td->refresh_waiters); + + while ((rw = list_pop(&waiters, struct refresh_waiter, list)) != NULL) { + struct command *rwcmd = rw->cmd; + struct db_query *rwdbq = rw->dbq; + tal_free(rw); + + /* Remove that one, and refresh the rest */ + assert(rwdbq->tables[0] == td); + tal_arr_remove(&rwdbq->tables, 0); + refresh_tables(rwcmd, rwdbq); + } + /* Remove that, iterate */ tal_arr_remove(&dbq->tables, 0); return refresh_tables(cmd, dbq); @@ -477,14 +526,16 @@ static struct command_result *process_json_list(struct command *cmd, const char *buf, const jsmntok_t *arr, const u64 *rowid, - const struct table_desc *td); + const struct table_desc *td, + u64 *last_created_index); /* Process all subobject columns */ static struct command_result *process_json_subobjs(struct command *cmd, const char *buf, const jsmntok_t *t, const struct table_desc *td, - u64 this_rowid) + u64 this_rowid, + u64 *last_created_index) { for (size_t i = 0; i < tal_count(td->columns); i++) { const struct column *col = td->columns[i]; @@ -501,10 +552,10 @@ static struct command_result *process_json_subobjs(struct command *cmd, /* If it's an array, use process_json_list */ if (!col->sub->is_subobject) { ret = process_json_list(cmd, buf, coltok, &this_rowid, - col->sub); + col->sub, last_created_index); } else { ret = process_json_subobjs(cmd, buf, coltok, col->sub, - this_rowid); + this_rowid, last_created_index); } if (ret) return ret; @@ -521,7 +572,8 @@ static struct command_result *process_json_obj(struct command *cmd, u64 this_rowid, const u64 *parent_rowid, size_t *sqloff, - sqlite3_stmt *stmt) + sqlite3_stmt *stmt, + u64 *last_created_index) { int err; @@ -548,7 +600,7 @@ static struct command_result *process_json_obj(struct command *cmd, else coltok = json_get_member(buf, t, col->jsonname); ret = process_json_obj(cmd, buf, coltok, col->sub, row, this_rowid, - NULL, sqloff, stmt); + NULL, sqloff, stmt, last_created_index); if (ret) return ret; continue; @@ -593,6 +645,11 @@ static struct command_result *process_json_obj(struct command *cmd, json_tok_full(buf, coltok)); } sqlite3_bind_int64(stmt, (*sqloff)++, val64); + /* created_index -> last_created_index */ + if (streq(col->dbname, "created_index") + && val64 > *last_created_index) { + *last_created_index = val64; + } break; case FIELD_BOOL: if (!json_to_bool(buf, coltok, &valbool)) { @@ -626,6 +683,7 @@ static struct command_result *process_json_obj(struct command *cmd, break; case FIELD_SCID: case FIELD_STRING: + case FIELD_OUTPOINT: sqlite3_bind_text(stmt, (*sqloff)++, buf + coltok->start, coltok->end - coltok->start, SQLITE_STATIC); @@ -663,7 +721,7 @@ static struct command_result *process_json_obj(struct command *cmd, sqlite3_errmsg(db)); } - return process_json_subobjs(cmd, buf, t, td, this_rowid); + return process_json_subobjs(cmd, buf, t, td, this_rowid, last_created_index); } /* A list, such as in the top-level reply, or for a sub-table */ @@ -671,7 +729,8 @@ static struct command_result *process_json_list(struct command *cmd, const char *buf, const jsmntok_t *arr, const u64 *parent_rowid, - const struct table_desc *td) + const struct table_desc *td, + u64 *last_created_index) { size_t i; const jsmntok_t *t; @@ -689,11 +748,22 @@ static struct command_result *process_json_list(struct command *cmd, json_for_each_arr(i, t, arr) { /* sqlite3 columns are 1-based! */ size_t off = 1; - u64 this_rowid = next_rowid++; + u64 this_rowid; - /* First entry is always the rowid */ - sqlite3_bind_int64(stmt, off++, this_rowid); - ret = process_json_obj(cmd, buf, t, td, i, this_rowid, parent_rowid, &off, stmt); + if (!td->has_created_index) { + this_rowid = next_rowid++; + /* First entry is always the rowid */ + sqlite3_bind_int64(stmt, off++, this_rowid); + } else { + if (!json_to_u64(buf, + json_get_member(buf, t, "created_index"), + &this_rowid)) + return command_fail(cmd, LIGHTNINGD, "No created_index in %s? '%.*s'", + td->cmdname, + json_tok_full_len(t), + json_tok_full(buf, t)); + } + ret = process_json_obj(cmd, buf, t, td, i, this_rowid, parent_rowid, &off, stmt, last_created_index); if (ret) break; sqlite3_reset(stmt); @@ -706,11 +776,12 @@ static struct command_result *process_json_list(struct command *cmd, static struct command_result *process_json_result(struct command *cmd, const char *buf, const jsmntok_t *result, - const struct table_desc *td) + const struct table_desc *td, + u64 *last_created_index) { return process_json_list(cmd, buf, json_get_member(buf, result, td->arrname), - NULL, td); + NULL, td, last_created_index); } static struct command_result *default_list_done(struct command *cmd, @@ -732,7 +803,7 @@ static struct command_result *default_list_done(struct command *cmd, td->name, errmsg); } - ret = process_json_result(cmd, buf, result, td); + ret = process_json_result(cmd, buf, result, td, dbq->last_created_index); if (ret) return ret; @@ -814,7 +885,7 @@ static struct command_result *listchannels_one_done(struct command *cmd, const struct table_desc *td = dbq->tables[0]; struct command_result *ret; - ret = process_json_result(cmd, buf, result, td); + ret = process_json_result(cmd, buf, result, td, dbq->last_created_index); if (ret) return ret; @@ -914,7 +985,7 @@ static struct command_result *listnodes_one_done(struct command *cmd, const struct table_desc *td = dbq->tables[0]; struct command_result *ret; - ret = process_json_result(cmd, buf, result, td); + ret = process_json_result(cmd, buf, result, td, dbq->last_created_index); if (ret) return ret; @@ -1032,14 +1103,28 @@ static struct command_result *nodes_refresh(struct command *cmd, } static struct command_result *refresh_tables(struct command *cmd, - struct db_query *dbq) + struct db_query *dbq) { - const struct table_desc *td; + struct table_desc *td; if (tal_count(dbq->tables) == 0) return refresh_complete(cmd, dbq); + /* td is const, but last_created_index needs updating, so we hand + * pointer in dbq. */ td = dbq->tables[0]; + + /* If it's currently being refreshed, wait */ + if (td->refreshing) { + struct refresh_waiter *rw = tal(cmd, struct refresh_waiter); + rw->cmd = cmd; + rw->dbq = dbq; + list_add(&td->refresh_waiters, &rw->list); + return command_still_pending(cmd); + } + + dbq->last_created_index = &dbq->tables[0]->last_created_index; + td->refreshing = true; return td->refresh(cmd, dbq->tables[0], dbq); } @@ -1142,7 +1227,8 @@ static void json_add_schema(struct json_stream *js, /* This needs to be an array, not a dictionary, since dicts * are often treated as unordered, and order is critical! */ json_array_start(js, "columns"); - json_add_column(js, "rowid", "INTEGER"); + if (!td->has_created_index) + json_add_column(js, "rowid", "INTEGER"); if (td->parent) { json_add_column(js, "row", "INTEGER"); json_add_column(js, "arrindex", "INTEGER"); @@ -1226,6 +1312,17 @@ static void add_sub_object(char **update_stmt, char **create_stmt, } } +/* We use created_index as INTEGER PRIMARY KEY, if it exists. + * Otherwise, we make an explicit rowid (implicit rowids cannot be + * used as a foreign key). */ +static const char *primary_key_name(const struct table_desc *td) +{ + if (td->has_created_index) + return "created_index"; + + return "rowid"; +} + /* Creates sql statements, initializes table */ static void finish_td(struct plugin *plugin, struct table_desc *td) { @@ -1239,11 +1336,13 @@ static void finish_td(struct plugin *plugin, struct table_desc *td) /* But it might have sub-sub objects! */ goto do_subtables; - /* We make an explicit rowid in each table, for subtables to access. This is - * becuase the implicit rowid can't be used as a foreign key! */ - create_stmt = tal_fmt(tmpctx, "CREATE TABLE %s (rowid INTEGER PRIMARY KEY, ", - td->name); - td->update_stmt = tal_fmt(td, "INSERT INTO %s VALUES (?, ", td->name); + create_stmt = tal_fmt(tmpctx, "CREATE TABLE %s (", td->name); + td->update_stmt = tal_fmt(td, "INSERT INTO %s VALUES (", td->name); + /* If no created_index, create explicit rowid */ + if (!td->has_created_index) { + tal_append_fmt(&create_stmt, "rowid INTEGER PRIMARY KEY, "); + tal_append_fmt(&td->update_stmt, "?, "); + } /* If we're a child array, we reference the parent column */ if (td->parent) { @@ -1252,9 +1351,9 @@ static void finish_td(struct plugin *plugin, struct table_desc *td) while (parent->is_subobject) parent = parent->parent; tal_append_fmt(&create_stmt, - "row INTEGER REFERENCES %s(rowid) ON DELETE CASCADE," + "row INTEGER REFERENCES %s(%s) ON DELETE CASCADE," " arrindex INTEGER", - parent->name); + parent->name, primary_key_name(parent)); tal_append_fmt(&td->update_stmt, "?,?"); sep = ","; } @@ -1272,6 +1371,9 @@ static void finish_td(struct plugin *plugin, struct table_desc *td) sep, col->dbname, fieldtypemap[col->ftype].sqltype); + /* created_index serves as primary key if it exists */ + if (streq(col->dbname, "created_index")) + tal_append_fmt(&create_stmt, " INTEGER PRIMARY KEY"); sep = ","; } tal_append_fmt(&create_stmt, ");"); @@ -1329,6 +1431,74 @@ static const char *db_table_name(const tal_t *ctx, const char *cmdname) return ret; } +static struct command_result *limited_list_done(struct command *cmd, + const char *method, + const char *buf, + const jsmntok_t *result, + struct db_query *dbq) +{ + struct table_desc *td = dbq->tables[0]; + struct command_result *ret; + + ret = process_json_result(cmd, buf, result, td, dbq->last_created_index); + if (ret) + return ret; + + return one_refresh_done(cmd, dbq); +} + +/* The simplest case: append-only lists */ +static struct command_result *refresh_by_created_index(struct command *cmd, + const struct table_desc *td, + struct db_query *dbq) +{ + struct out_req *req; + req = jsonrpc_request_start(cmd, td->cmdname, + limited_list_done, forward_error, + dbq); + json_add_string(req->js, "index", "created"); + json_add_u64(req->js, "start", *dbq->last_created_index + 1); + return send_outreq(req); +} + +struct refresh_funcs { + const char *cmdname; + struct command_result *(*refresh)(struct command *cmd, + const struct table_desc *td, + struct db_query *dbq); +}; + +static const struct refresh_funcs refresh_funcs[] = { + /* These are special, using gossmap */ + { "listchannels", channels_refresh }, + { "listnodes", nodes_refresh }, + /* FIXME: These support wait and full pagination */ + { "listhtlcs", default_refresh }, + { "listforwards", default_refresh }, + { "listinvoices", default_refresh }, + { "listsendpays", default_refresh }, + /* These are never changed or deleted */ + { "listchainmoves", refresh_by_created_index }, + { "listchannelmoves", refresh_by_created_index }, + /* No pagination support */ + { "listoffers", default_refresh }, + { "listpeers", default_refresh }, + { "listpeerchannels", default_refresh }, + { "listclosedchannels", default_refresh }, + { "listtransactions", default_refresh }, + { "bkpr-listaccountevents", default_refresh }, + { "bkpr-listincome", default_refresh } +}; + +static const struct refresh_funcs *find_command_refresh(const char *cmdname) +{ + for (size_t i = 0; i < ARRAY_SIZE(refresh_funcs); i++) { + if (streq(refresh_funcs[i].cmdname, cmdname)) + return &refresh_funcs[i]; + } + abort(); +} + static struct table_desc *new_table_desc(const tal_t *ctx, struct table_desc *parent, const jsmntok_t *cmd, @@ -1337,6 +1507,7 @@ static struct table_desc *new_table_desc(const tal_t *ctx, { struct table_desc *td; const char *name; + const struct refresh_funcs *refresh_func; td = tal(ctx, struct table_desc); td->cmdname = json_strdup(td, schemas, cmd); @@ -1349,12 +1520,16 @@ static struct table_desc *new_table_desc(const tal_t *ctx, td->is_subobject = is_subobject; td->arrname = json_strdup(td, schemas, arrname); td->columns = tal_arr(td, struct column *, 0); - if (streq(td->name, "channels")) - td->refresh = channels_refresh; - else if (streq(td->name, "nodes")) - td->refresh = nodes_refresh; - else - td->refresh = default_refresh; + td->last_created_index = 0; + td->has_created_index = false; + td->refreshing = false; + list_head_init(&td->refresh_waiters); + + /* Only top-levels have refresh functions */ + if (!parent) { + refresh_func = find_command_refresh(td->cmdname); + td->refresh = refresh_func->refresh; + } /* sub-objects are a JSON thing, not a real table! */ if (!td->is_subobject) @@ -1521,6 +1696,7 @@ static void init_tablemap(struct plugin *plugin) td = new_table_desc(ctx, NULL, t, cmd, false); add_table_object(td, items); + td->has_created_index = find_column(td, "created_index"); if (plugin) finish_td(plugin, td); @@ -1619,9 +1795,10 @@ static void print_columns(const struct table_desc *td, const char *indent, subindent = tal_fmt(tmpctx, "%s ", indent); printf("%s- related table `%s`%s\n", indent, subtd->name, objsrc); - printf("%s- `row` (reference to `%s.rowid`, sqltype `INTEGER`)\n" + printf("%s- `row` (reference to `%s.%s`, sqltype `INTEGER`)\n" "%s- `arrindex` (index within array, sqltype `INTEGER`)\n", - subindent, td->name, subindent); + subindent, td->name, primary_key_name(td), + subindent); print_columns(subtd, subindent, ""); } else { const char *subobjsrc; @@ -1643,10 +1820,12 @@ static void print_columns(const struct table_desc *td, const char *indent, td->columns[i]->jsonname); } else origin = ""; - printf("%s- `%s` (type `%s`, sqltype `%s`%s%s)\n", + printf("%s- `%s` (type `%s`, sqltype `%s%s`%s%s)\n", indent, td->columns[i]->dbname, fieldtypemap[td->columns[i]->ftype].name, fieldtypemap[td->columns[i]->ftype].sqltype, + streq(td->columns[i]->dbname, "created_index") + ? " PRIMARY KEY" : "", origin, objsrc); } } diff --git a/tests/plugins/test_libplugin.c b/tests/plugins/test_libplugin.c index d6f269610229..f73378e625a8 100644 --- a/tests/plugins/test_libplugin.c +++ b/tests/plugins/test_libplugin.c @@ -45,6 +45,8 @@ static struct command_result *json_helloworld(struct command *cmd, const jsmntok_t *params) { const char *name; + const char *response_buf; + const jsmntok_t *response; if (!param(cmd, buf, params, p_opt("name", param_string, &name), @@ -53,6 +55,12 @@ static struct command_result *json_helloworld(struct command *cmd, plugin_notify_message(cmd, LOG_INFORM, "Notification from %s", "json_helloworld"); + response = jsonrpc_request_sync(cmd, cmd, "listpeers", NULL, &response_buf); + plugin_log(cmd->plugin, LOG_INFORM, "listpeers gave %zu tokens: %.*s", + tal_count(response), + json_tok_full_len(response), + json_tok_full(response_buf, response)); + if (!name) return jsonrpc_get_datastore_binary(cmd, "test_libplugin/name", diff --git a/tests/test_bookkeeper.py b/tests/test_bookkeeper.py index 18243df195b4..c80c03fb4e39 100644 --- a/tests/test_bookkeeper.py +++ b/tests/test_bookkeeper.py @@ -1,7 +1,6 @@ from fixtures import * # noqa: F401,F403 from decimal import Decimal from pyln.client import Millisatoshi, RpcError -from pyln.testing.db import Sqlite3Db from fixtures import TEST_NETWORK from utils import ( sync_blockheight, wait_for, only_one, first_channel_id, TIMEOUT @@ -10,6 +9,7 @@ from pathlib import Path import os import pytest +import time import unittest @@ -23,6 +23,12 @@ def find_first_tag(evs, tag): return ev[0] +def check_events(node, channel_id, exp_events): + chan_events = [ev for ev in node.rpc.bkpr_listaccountevents()['events'] if ev['account'] == channel_id] + stripped = [{k: d[k] for k in ('tag', 'credit_msat', 'debit_msat') if k in d} for d in chan_events] + assert stripped == exp_events + + @unittest.skipIf(TEST_NETWORK != 'regtest', "fixme: broadcast fails, dusty") def test_bookkeeping_closing_trimmed_htlcs(node_factory, bitcoind, executor): l1, l2 = node_factory.line_graph(2) @@ -197,73 +203,6 @@ def test_bookkeeping_external_withdraws(node_factory, bitcoind): assert btc_balance['balance_msat'] == amount_msat * 2 - withdraw_amt - fees -@unittest.skipIf(TEST_NETWORK != 'regtest', "External wallet support doesn't work with elements yet.") -@unittest.skipIf(os.getenv('TEST_DB_PROVIDER', 'sqlite3') != 'sqlite3', "Depends on sqlite3 database location") -def test_bookkeeping_external_withdraw_missing(node_factory, bitcoind): - """ Withdrawals to an external address turn up as - extremely large onchain_fees when they happen before - our accounting plugin is attached""" - l1 = node_factory.get_node() - - basedir = l1.daemon.opts.get("lightning-dir") - addr = l1.rpc.newaddr()['bech32'] - - amount = 1111111 - amount_msat = Millisatoshi(amount * 1000) - bitcoind.rpc.sendtoaddress(addr, amount / 10**8) - bitcoind.rpc.sendtoaddress(addr, amount / 10**8) - - bitcoind.generate_block(1) - wait_for(lambda: len(l1.rpc.listfunds()['outputs']) == 2) - - waddr = l1.bitcoin.rpc.getnewaddress() - - # Ok, now we send some funds to an external address - l1.rpc.withdraw(waddr, amount // 2) - - # Only two income events: deposits - assert len(l1.rpc.bkpr_listincome()['income_events']) == 2 - # 4 account events: 2 wallet deposits, 1 external deposit - assert len(l1.rpc.bkpr_listaccountevents()['events']) == 3 - - # Stop node and remove the accounts data - l1.stop() - os.remove(os.path.join(basedir, TEST_NETWORK, 'accounts.sqlite3')) - l1.start() - - # Number of income events should be unchanged - assert len(l1.rpc.bkpr_listincome()['income_events']) == 2 - # we're now missing the external deposit - events = l1.rpc.bkpr_listaccountevents()['events'] - assert len(events) == 2 - assert len([e for e in events if e['account'] == 'external']) == 0 - assert len(find_tags(events, 'journal_entry')) == 0 - - # the wallet balance should be unchanged - btc_balance = only_one(only_one(l1.rpc.bkpr_listbalances()['accounts'])['balances']) - assert btc_balance['balance_msat'] == amount_msat * 2 - - # ok now we mine a block - bitcoind.generate_block(1) - sync_blockheight(bitcoind, [l1]) - - # expect the withdrawal to appear in the incomes - # and there should be an onchain fee - incomes = l1.rpc.bkpr_listincome()['income_events'] - # 2 wallet deposits, 1 onchain_fee - assert len(incomes) == 3 - assert len(find_tags(incomes, 'withdrawal')) == 0 - - fee_events = find_tags(incomes, 'onchain_fee') - assert len(fee_events) == 1 - fees = fee_events[0]['debit_msat'] - assert fees > Millisatoshi(amount // 2 * 1000) - - # wallet balance is decremented now - bal = only_one(only_one(l1.rpc.bkpr_listbalances()['accounts'])['balances']) - assert bal['balance_msat'] == amount_msat * 2 - fees - - @unittest.skipIf(TEST_NETWORK != 'regtest', "External wallet support doesn't work with elements yet.") def test_bookkeeping_rbf_withdraw(node_factory, bitcoind): """ If a withdraw to an external gets RBF'd, @@ -390,30 +329,18 @@ def test_bookkeeping_missed_chans_leases(node_factory, bitcoind): l1.start() l2.start() - # Wait for the balance snapshot to fire/finish - l1.daemon.wait_for_log('Snapshot balances updated') - l2.daemon.wait_for_log('Snapshot balances updated') + # l1 events: nothing missed! + exp_events = [{'tag': 'channel_open', 'credit_msat': open_amt * 1000 + lease_fee, 'debit_msat': 0}, + {'tag': 'lease_fee', 'credit_msat': 0, 'debit_msat': lease_fee}, + {'tag': 'onchain_fee', 'credit_msat': 1314000, 'debit_msat': 0}, + {'tag': 'invoice', 'credit_msat': 0, 'debit_msat': invoice_msat}] + check_events(l1, channel_id, exp_events) - def _check_events(node, channel_id, exp_events): - chan_events = [ev for ev in node.rpc.bkpr_listaccountevents()['events'] if ev['account'] == channel_id] - assert len(chan_events) == len(exp_events) - for ev, exp in zip(chan_events, exp_events): - assert ev['tag'] == exp[0] - assert ev['credit_msat'] == Millisatoshi(exp[1]) - assert ev['debit_msat'] == Millisatoshi(exp[2]) - - # l1 events - exp_events = [('channel_open', open_amt * 1000 + lease_fee, 0), - ('onchain_fee', 1314000, 0), - ('lease_fee', 0, lease_fee), - ('journal_entry', 0, invoice_msat)] - _check_events(l1, channel_id, exp_events) - - exp_events = [('channel_open', open_amt * 1000, 0), - ('onchain_fee', 894000, 0), - ('lease_fee', lease_fee, 0), - ('journal_entry', invoice_msat, 0)] - _check_events(l2, channel_id, exp_events) + exp_events = [{'tag': 'channel_open', 'credit_msat': open_amt * 1000, 'debit_msat': 0}, + {'tag': 'lease_fee', 'credit_msat': lease_fee, 'debit_msat': 0}, + {'tag': 'onchain_fee', 'credit_msat': 894000, 'debit_msat': 0}, + {'tag': 'invoice', 'credit_msat': invoice_msat, 'debit_msat': 0}] + check_events(l2, channel_id, exp_events) @unittest.skipIf(os.getenv('TEST_DB_PROVIDER', 'sqlite3') != 'sqlite3', "turns off bookkeeper at start") @@ -444,9 +371,14 @@ def test_bookkeeping_missed_chans_pushed(node_factory, bitcoind): l1.wait_local_channel_active(scid) channel_id = first_channel_id(l1, l2) + # Sigh. bookkeeper sorts events by timestamp. If the invoice event happens + # too close, it can change the order, so sleep here. + time.sleep(1) + # Send l2 funds via the channel l1.pay(l2, invoice_msat) - l1.daemon.wait_for_log(r'coin movement:.*\'invoice\'') + # Make sure they're completely settled, so accounting correct. + wait_for(lambda: only_one(l1.rpc.listpeerchannels()['channels'])['htlcs'] == []) # Now turn the bookkeeper on and restart l1.stop() @@ -456,30 +388,18 @@ def test_bookkeeping_missed_chans_pushed(node_factory, bitcoind): l1.start() l2.start() - # Wait for the balance snapshot to fire/finish - l1.daemon.wait_for_log('Snapshot balances updated') - l2.daemon.wait_for_log('Snapshot balances updated') - - def _check_events(node, channel_id, exp_events): - chan_events = [ev for ev in node.rpc.bkpr_listaccountevents()['events'] if ev['account'] == channel_id] - assert len(chan_events) == len(exp_events) - for ev, exp in zip(chan_events, exp_events): - assert ev['tag'] == exp[0] - assert ev['credit_msat'] == Millisatoshi(exp[1]) - assert ev['debit_msat'] == Millisatoshi(exp[2]) - # l1 events - exp_events = [('channel_open', open_amt * 1000, 0), - ('onchain_fee', 4927000, 0), - ('pushed', 0, push_amt), - ('journal_entry', 0, invoice_msat)] - _check_events(l1, channel_id, exp_events) + exp_events = [{'tag': 'channel_open', 'credit_msat': open_amt * 1000, 'debit_msat': 0}, + {'tag': 'pushed', 'credit_msat': 0, 'debit_msat': push_amt}, + {'tag': 'onchain_fee', 'credit_msat': 4927000, 'debit_msat': 0}, + {'tag': 'invoice', 'credit_msat': 0, 'debit_msat': invoice_msat}] + check_events(l1, channel_id, exp_events) # l2 events - exp_events = [('channel_open', 0, 0), - ('pushed', push_amt, 0), - ('journal_entry', invoice_msat, 0)] - _check_events(l2, channel_id, exp_events) + exp_events = [{'tag': 'channel_open', 'credit_msat': 0, 'debit_msat': 0}, + {'tag': 'pushed', 'credit_msat': push_amt, 'debit_msat': 0}, + {'tag': 'invoice', 'credit_msat': invoice_msat, 'debit_msat': 0}] + check_events(l2, channel_id, exp_events) @unittest.skipIf(TEST_NETWORK != 'regtest', "network fees hardcoded") @@ -648,10 +568,6 @@ def test_bookkeeping_missed_chans_pay_after(node_factory, bitcoind): l1.start() l2.start() - # Wait for the balance snapshot to fire/finish - l1.daemon.wait_for_log('Snapshot balances updated') - l2.daemon.wait_for_log('Snapshot balances updated') - # Should have channel in both, with balances for n in [l1, l2]: accts = [ba['account'] for ba in n.rpc.bkpr_listbalances()['accounts']] @@ -663,24 +579,16 @@ def test_bookkeeping_missed_chans_pay_after(node_factory, bitcoind): l1.pay(l2, invoice_msat) l1.daemon.wait_for_log(r'coin movement:.*\'invoice\'') - def _check_events(node, channel_id, exp_events): - chan_events = [ev for ev in node.rpc.bkpr_listaccountevents()['events'] if ev['account'] == channel_id] - assert len(chan_events) == len(exp_events) - for ev, exp in zip(chan_events, exp_events): - assert ev['tag'] == exp[0] - assert ev['credit_msat'] == Millisatoshi(exp[1]) - assert ev['debit_msat'] == Millisatoshi(exp[2]) - # l1 events - exp_events = [('channel_open', open_amt * 1000, 0), - ('onchain_fee', 4927000, 0), - ('invoice', 0, invoice_msat)] - _check_events(l1, channel_id, exp_events) + exp_events = [{'tag': 'channel_open', 'credit_msat': open_amt * 1000, 'debit_msat': 0}, + {'tag': 'onchain_fee', 'credit_msat': 4927000, 'debit_msat': 0}, + {'tag': 'invoice', 'credit_msat': 0, 'debit_msat': invoice_msat}] + check_events(l1, channel_id, exp_events) # l2 events - exp_events = [('channel_open', 0, 0), - ('invoice', invoice_msat, 0)] - _check_events(l2, channel_id, exp_events) + exp_events = [{'tag': 'channel_open', 'credit_msat': 0, 'debit_msat': 0}, + {'tag': 'invoice', 'credit_msat': invoice_msat, 'debit_msat': 0}] + check_events(l2, channel_id, exp_events) @unittest.skipIf(os.getenv('TEST_DB_PROVIDER', 'sqlite3') != 'sqlite3', "turns off bookkeeper at start") @@ -719,19 +627,11 @@ def test_bookkeeping_onchaind_txs(node_factory, bitcoind): l1.daemon.opts['rescan'] = 102 l1.start() - # Wait for the balance snapshot to fire/finish - l1.daemon.wait_for_log('Snapshot balances updated') - - # We should have the deposit + # We should have everything. events = l1.rpc.bkpr_listaccountevents()['events'] - assert len(events) == 2 - assert events[0]['account'] == 'wallet' - assert events[0]['tag'] == 'deposit' - assert events[1]['account'] == 'wallet' - assert events[1]['tag'] == 'journal_entry' - - wallet_bal = only_one(l1.rpc.bkpr_listbalances()['accounts']) - assert wallet_bal['account'] == 'wallet' + assert len(events) == 12 + + wallet_bal = only_one([a for a in l1.rpc.bkpr_listbalances()['accounts'] if a['account'] == 'wallet']) funds = l1.rpc.listfunds() assert len(funds['channels']) == 0 outs = sum([out['amount_msat'] for out in funds['outputs']]) @@ -748,11 +648,15 @@ def test_bookkeeping_descriptions(node_factory, bitcoind, chainparams): # Send l2 funds via the channel bolt11_desc = 'test "bolt11" description, 🥰🪢' l1.pay(l2, 11000000, label=bolt11_desc) + wait_for(lambda: only_one(l1.rpc.listpeerchannels()['channels'])['htlcs'] == []) + + # Need to call bookkeeper to trigger analysis! + l1_inc_ev = l1.rpc.bkpr_listincome()['income_events'] l1.daemon.wait_for_log('coin_move .* [(]invoice[)] 0msat -11000000msat') + l2.rpc.bkpr_listincome() l2.daemon.wait_for_log('coin_move .* [(]invoice[)] 11000000msat') # Test paying an bolt11 invoice (rcvr) - l1_inc_ev = l1.rpc.bkpr_listincome()['income_events'] inv = only_one([ev for ev in l1_inc_ev if ev['tag'] == 'invoice']) assert inv['description'] == bolt11_desc @@ -766,11 +670,14 @@ def test_bookkeeping_descriptions(node_factory, bitcoind, chainparams): offer = l1.rpc.call('offer', [100, bolt12_desc]) invoice = l2.rpc.call('fetchinvoice', {'offer': offer['bolt12']}) paid = l2.rpc.pay(invoice['invoice']) + wait_for(lambda: only_one(l1.rpc.listpeerchannels()['channels'])['htlcs'] == []) + wait_for(lambda: only_one(l2.rpc.listpeerchannels()['channels'])['htlcs'] == []) + l1_inc_ev = l1.rpc.bkpr_listincome()['income_events'] l1.daemon.wait_for_log('coin_move .* [(]invoice[)] 100msat') + l2.rpc.bkpr_listincome() l2.daemon.wait_for_log('coin_move .* [(]invoice[)] 0msat -100msat') # Test paying an offer (bolt12) (rcvr) - l1_inc_ev = l1.rpc.bkpr_listincome()['income_events'] inv = only_one([ev for ev in l1_inc_ev if 'payment_id' in ev and ev['payment_id'] == paid['payment_hash']]) assert inv['description'] == bolt12_desc @@ -820,6 +727,11 @@ def test_bookkeeping_descriptions(node_factory, bitcoind, chainparams): 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]) + # Test persistence! + l1.restart() + assert l1.rpc.bkpr_listaccountevents()['events'] == acct_evs + assert l1.rpc.bkpr_listincome()['income_events'] == income_evs + def test_empty_node(node_factory, bitcoind): """ @@ -905,21 +817,7 @@ def test_rebalance_tracking(node_factory, bitcoind): assert outbound_ev['payment_id'] == pay_hash -@unittest.skipIf(os.getenv('TEST_DB_PROVIDER', 'sqlite3') != 'sqlite3', "This test is based on a sqlite3 snapshot") -def test_bookkeeper_lease_fee_dupe_migration(node_factory): - """ Check that if there's duplicate lease_fees, we remove them""" - - l1 = node_factory.get_node(bkpr_dbfile='dupe_lease_fee.sqlite3.xz') - - wait_for(lambda: l1.daemon.is_in_log('Duplicate \'lease_fee\' found for account')) - - accts_db_path = os.path.join(l1.lightning_dir, TEST_NETWORK, 'accounts.sqlite3') - accts_db = Sqlite3Db(accts_db_path) - - assert accts_db.query('SELECT tag from channel_events where tag = \'lease_fee\';') == [{'tag': 'lease_fee'}] - - -def test_bookkeeper_custom_notifs(node_factory): +def test_bookkeeper_custom_notifs(node_factory, chainparams): # FIXME: what happens if we send internal funds to 'external' wallet? plugin = os.path.join( os.path.dirname(__file__), "plugins", "bookkeeper_custom_coins.py" @@ -937,9 +835,9 @@ def test_bookkeeper_custom_notifs(node_factory): acct = "nifty's secret stash" l1.rpc.senddeposit(acct, False, outpoint_in, amount) + l1.daemon.wait_for_log(r"Foreign chain event: deposit \(nifty's secret stash\) 180000000msat -0msat 1679955976 111 aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa:0") l1.rpc.sendspend(acct, outpoint_in, spend_txid, amount) - l1.daemon.wait_for_log(r"utxo_deposit \(deposit|nifty's secret stash\) .* -0msat 1679955976 111 aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa:0") - l1.daemon.wait_for_log(r"utxo_spend \(withdrawal|nifty's secret stash\) 0msat -12345678000msat 1679955976 111 aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa:0 bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb") + l1.daemon.wait_for_log(r"Foreign chain event: withdrawal \(nifty's secret stash\) 0msat -180000000msat 1679955976 111 aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa:0 bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb") # balance should be zero bals = l1.rpc.bkpr_listbalances()['accounts'] @@ -949,7 +847,7 @@ def test_bookkeeper_custom_notifs(node_factory): assert only_one(bal['balances'])['balance_msat'] == Millisatoshi(0) l1.rpc.senddeposit(acct, False, change_deposit, amount - withdraw_amt - fee) - l1.daemon.wait_for_log(r"utxo_deposit \(deposit|nifty's secret stash\) .* -0msat 1679955976 111 bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb:0") + l1.daemon.wait_for_log(r"Foreign chain event: deposit \(nifty's secret stash\) .* -0msat 1679955976 111 bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb:0") # balance should be equal to amount events = l1.rpc.bkpr_listaccountevents(acct)['events'] @@ -961,12 +859,56 @@ def test_bookkeeper_custom_notifs(node_factory): assert onchain_fee_one == fee + withdraw_amt l1.rpc.senddeposit(acct, True, external_deposit, withdraw_amt) - l1.daemon.wait_for_log(r"utxo_deposit \(deposit|external\) .* -0msat 1679955976 111 bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb:1") + l1.daemon.wait_for_log(r"Foreign chain event: deposit \(external\) .* -0msat 1679955976 111 bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb:1") events = l1.rpc.bkpr_listaccountevents(acct)['events'] onchain_fees = [x for x in events if x['type'] == 'onchain_fee'] assert len(onchain_fees) == 2 assert onchain_fees[0]['credit_msat'] == onchain_fee_one assert onchain_fees[1]['debit_msat'] == withdraw_amt + assert events == [{'account': "nifty's secret stash", + 'blockheight': 111, + 'credit_msat': 180000000, + 'currency': chainparams['bip173_prefix'], + 'debit_msat': 0, + 'outpoint': 'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa:0', + 'tag': 'deposit', + 'timestamp': 1679955976, + 'type': 'chain'}, + {'account': "nifty's secret stash", + 'blockheight': 111, + 'credit_msat': 0, + 'currency': chainparams['bip173_prefix'], + 'debit_msat': 180000000, + 'outpoint': 'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa:0', + 'tag': 'withdrawal', + 'timestamp': 1679955976, + 'txid': 'bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb', + 'type': 'chain'}, + {'account': "nifty's secret stash", + 'blockheight': 111, + 'credit_msat': 124443000, + 'currency': chainparams['bip173_prefix'], + 'debit_msat': 0, + 'outpoint': 'bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb:0', + 'tag': 'deposit', + 'timestamp': 1679955976, + 'type': 'chain'}, + {'account': "nifty's secret stash", + 'credit_msat': 55557000, + 'currency': chainparams['bip173_prefix'], + 'debit_msat': 0, + 'tag': 'onchain_fee', + 'timestamp': 1679955976, + 'txid': 'bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb', + 'type': 'onchain_fee'}, + {'account': "nifty's secret stash", + 'credit_msat': 0, + 'currency': chainparams['bip173_prefix'], + 'debit_msat': 55555000, + 'tag': 'onchain_fee', + 'timestamp': 1679955976, + 'txid': 'bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb', + 'type': 'onchain_fee'}] # This should not blow up incomes = l1.rpc.bkpr_listincome()['income_events'] @@ -974,19 +916,6 @@ def test_bookkeeper_custom_notifs(node_factory): assert acct_fee == Millisatoshi(fee) -@unittest.skipIf(os.getenv('TEST_DB_PROVIDER', 'sqlite3') != 'sqlite3', "This test is based on a sqlite3 snapshot") -def test_bookkeeper_bad_migration(node_factory): - l1 = node_factory.get_node(bkpr_dbfile='bookkeeper-accounts-pre-v24.08-migration.sqlite3.xz') - l2 = node_factory.get_node() - - # Make sure l1 does fixup, use query to force an access (which would fail if column not present) - assert l1.daemon.is_in_log("plugin-bookkeeper: Database fixup: adding spliced column to chain_events table") - l1.rpc.bkpr_listaccountevents('wallet') - - # l2 will *not* do this - assert not l2.daemon.is_in_log("adding spliced column") - - @unittest.skipIf(TEST_NETWORK != 'regtest', "Snapshots are bitcoin regtest.") @unittest.skipIf(os.getenv('TEST_DB_PROVIDER', 'sqlite3') != 'sqlite3', "uses snapshots") def test_migration(node_factory, bitcoind): @@ -1058,3 +987,160 @@ def test_migration(node_factory, bitcoind): # When generating, we want to stop so you can grab databases. assert generate is False + + l1_events = l1.rpc.bkpr_listaccountevents()['events'] + for e in l1_events: + del e['timestamp'] + + l2_events = l2.rpc.bkpr_listaccountevents()['events'] + for e in l2_events: + del e['timestamp'] + + # These were snapshotted before the bkpr migration, so should + # be the same! + assert l1_events == [{'account': 'wallet', + 'blockheight': 102, + 'credit_msat': 2000000000, + 'currency': 'bcrt', + 'debit_msat': 0, + 'outpoint': '63c59b312976320528552c258ae51563498dfd042b95bb0c842696614d59bb89:1', + 'tag': 'deposit', + 'type': 'chain'}, + {'account': 'wallet', + 'blockheight': 103, + 'credit_msat': 0, + 'currency': 'bcrt', + 'debit_msat': 2000000000, + 'outpoint': '63c59b312976320528552c258ae51563498dfd042b95bb0c842696614d59bb89:1', + 'tag': 'withdrawal', + 'txid': '675ab2a8c43afcf98b82a1120d1a4d36768c898792fe1282c5be4ac055377fbe', + 'type': 'chain'}, + {'account': 'wallet', + 'blockheight': 103, + 'credit_msat': 995073000, + 'currency': 'bcrt', + 'debit_msat': 0, + 'description': "Rusty's change", + 'outpoint': '675ab2a8c43afcf98b82a1120d1a4d36768c898792fe1282c5be4ac055377fbe:1', + 'tag': 'deposit', + 'type': 'chain'}, + {'account': 'be7f3755c04abec58212fe9287898c76364d1a0d12a1828bf9fc3ac4a8b25a67', + 'blockheight': 103, + 'credit_msat': 1000000000, + 'currency': 'bcrt', + 'debit_msat': 0, + 'description': "Rusty's channel", + 'outpoint': '675ab2a8c43afcf98b82a1120d1a4d36768c898792fe1282c5be4ac055377fbe:0', + 'tag': 'channel_open', + 'type': 'chain'}, + {'account': 'be7f3755c04abec58212fe9287898c76364d1a0d12a1828bf9fc3ac4a8b25a67', + 'credit_msat': 0, + 'currency': 'bcrt', + 'debit_msat': 12345678, + 'description': "Rusty's payment", + 'is_rebalance': False, + 'part_id': 0, + 'payment_id': '7ccef7e9fabbf4a841af44b1fc7319bc70ce98697b77ce6dacffa84bebcd4350', + 'tag': 'invoice', + 'type': 'channel'}, + {'account': 'be7f3755c04abec58212fe9287898c76364d1a0d12a1828bf9fc3ac4a8b25a67', + 'credit_msat': 4927000, + 'currency': 'bcrt', + 'debit_msat': 0, + 'tag': 'onchain_fee', + 'txid': '675ab2a8c43afcf98b82a1120d1a4d36768c898792fe1282c5be4ac055377fbe', + 'type': 'onchain_fee'}, + {'account': 'wallet', + 'credit_msat': 1004927000, + 'currency': 'bcrt', + 'debit_msat': 0, + 'tag': 'onchain_fee', + 'txid': '675ab2a8c43afcf98b82a1120d1a4d36768c898792fe1282c5be4ac055377fbe', + 'type': 'onchain_fee'}, + {'account': 'wallet', + 'credit_msat': 0, + 'currency': 'bcrt', + 'debit_msat': 1004927000, + 'tag': 'onchain_fee', + 'txid': '675ab2a8c43afcf98b82a1120d1a4d36768c898792fe1282c5be4ac055377fbe', + 'type': 'onchain_fee'}] + + assert l2_events == [{'account': 'be7f3755c04abec58212fe9287898c76364d1a0d12a1828bf9fc3ac4a8b25a67', + 'blockheight': 103, + 'credit_msat': 0, + 'currency': 'bcrt', + 'debit_msat': 0, + 'outpoint': '675ab2a8c43afcf98b82a1120d1a4d36768c898792fe1282c5be4ac055377fbe:0', + 'tag': 'channel_open', + 'type': 'chain'}, + {'account': 'be7f3755c04abec58212fe9287898c76364d1a0d12a1828bf9fc3ac4a8b25a67', + 'credit_msat': 12345678, + 'currency': 'bcrt', + 'debit_msat': 0, + 'description': "Rusty's payment", + 'is_rebalance': False, + 'part_id': 0, + 'payment_id': '7ccef7e9fabbf4a841af44b1fc7319bc70ce98697b77ce6dacffa84bebcd4350', + 'tag': 'invoice', + 'type': 'channel'}] + + +@unittest.skipIf(TEST_NETWORK != 'regtest', "Snapshots are bitcoin regtest.") +@unittest.skipIf(os.getenv('TEST_DB_PROVIDER', 'sqlite3') != 'sqlite3', "uses snapshots") +def test_migration_no_bkpr(node_factory, bitcoind): + """These nodes need to invent coinmoves to make the balances work""" + bitcoind.generate_block(1) + l1 = node_factory.get_node(dbfile="l1-before-moves-in-db.sqlite3.xz", + options={'database-upgrade': True}) + l2 = node_factory.get_node(dbfile="l2-before-moves-in-db.sqlite3.xz", + options={'database-upgrade': True}) + + chan = only_one(l1.rpc.listpeerchannels()['channels']) + + l1_events = l1.rpc.bkpr_listaccountevents()['events'] + for e in l1_events: + del e['timestamp'] + + l2_events = l2.rpc.bkpr_listaccountevents()['events'] + for e in l2_events: + del e['timestamp'] + + assert l1_events == [{'account': chan['channel_id'], + 'blockheight': 103, + 'credit_msat': 1000000000, + 'currency': 'bcrt', + 'debit_msat': 0, + 'outpoint': f"{chan['funding_txid']}:{chan['funding_outnum']}", + 'tag': 'channel_open', + 'type': 'chain'}, + {'account': 'wallet', + 'blockheight': 103, + 'credit_msat': 995073000, + 'currency': 'bcrt', + 'debit_msat': 0, + 'outpoint': f"{chan['funding_txid']}:{chan['funding_outnum'] ^ 1}", + 'tag': 'deposit', + 'type': 'chain'}, + {'account': chan['channel_id'], + 'credit_msat': 0, + 'currency': 'bcrt', + 'debit_msat': 12345678, + 'is_rebalance': False, + 'tag': 'journal', + 'type': 'channel'}] + + assert l2_events == [{'account': chan['channel_id'], + 'blockheight': 103, + 'credit_msat': 0, + 'currency': 'bcrt', + 'debit_msat': 0, + 'outpoint': f"{chan['funding_txid']}:{chan['funding_outnum']}", + 'tag': 'channel_open', + 'type': 'chain'}, + {'account': chan['channel_id'], + 'credit_msat': 12345678, + 'currency': 'bcrt', + 'debit_msat': 0, + 'is_rebalance': False, + 'tag': 'journal', + 'type': 'channel'}] diff --git a/tests/test_coinmoves.py b/tests/test_coinmoves.py new file mode 100644 index 000000000000..acf1c0d4d2e2 --- /dev/null +++ b/tests/test_coinmoves.py @@ -0,0 +1,2066 @@ +from fixtures import * # noqa: F401,F403 +from fixtures import TEST_NETWORK +from utils import ( + sync_blockheight, wait_for, only_one, TIMEOUT +) + +import os +import unittest +import pytest +import re +import time +from pyln.testing.utils import EXPERIMENTAL_DUAL_FUND + + +# While we're doing this, check sql plugin's representation too. +def check_sql(node, kind, expected): + columns = only_one(node.rpc.listsqlschemas(kind)['schemas'])['columns'] + ret = node.rpc.sql(f"SELECT * FROM {kind}") + assert len(ret['rows']) == len(expected) + for row, e in zip(ret['rows'], expected): + assert len(row) == len(columns) + for val, col in zip(row, columns): + if col['name'] in e: + assert val == e[col['name']], f"{col['name']} is {val} not {e[col['name']]}: ({row} vs {columns})" + elif col['name'] not in ('rowid', 'timestamp'): + assert val is None, f"{col['name']} is not None ({row} vs {columns})" + + +def check_moves(moves, expected): + # Can't predict timestamp + for m in moves: + del m['timestamp'] + # But we can absolutely predict created_index. + for i, m in enumerate(expected, start=1): + m['created_index'] = i + assert moves == expected + + +def check_channel_moves(node, expected): + wait_for(lambda: len(node.rpc.listchannelmoves()['channelmoves']) == len(expected)) + check_moves(node.rpc.listchannelmoves()['channelmoves'], expected) + check_sql(node, "channelmoves", expected) + + +def check_chain_moves(node, expected): + wait_for(lambda: len(node.rpc.listchainmoves()['chainmoves']) == len(expected)) + check_moves(node.rpc.listchainmoves()['chainmoves'], expected) + check_sql(node, "chainmoves", expected) + # Check extra_tags. + for e in expected: + rows = node.rpc.sql(f"SELECT cet.extra_tags FROM chainmoves_extra_tags cet LEFT JOIN chainmoves cm ON cet.row = cm.rowid WHERE cm.created_index={e['created_index']} ORDER BY cm.created_index, cet.arrindex;")['rows'] + extra_tags = [only_one(row) for row in rows] + assert extra_tags == e['extra_tags'] + + +def account_balances(accounts): + """Gather all the credits / debits for all accounts""" + balances = {} + for a in accounts: + if a['account_id'] not in balances: + balances[a['account_id']] = [] + balances[a['account_id']].append(a['credit_msat']) + balances[a['account_id']].append(-a['debit_msat']) + return balances + + +def check_balances(l1, l2, channel_id, msats_sent_to_2): + channel1 = account_balances(l1.rpc.listchannelmoves()['channelmoves']) + channel2 = account_balances(l2.rpc.listchannelmoves()['channelmoves']) + chain1 = account_balances(l1.rpc.listchainmoves()['chainmoves']) + chain2 = account_balances(l2.rpc.listchainmoves()['chainmoves']) + + # Our initial setup_channel sends 50000000000 msat + msats_sent_to_2 += 50000000000 + # Channel balances should reflect sats transferred + assert sum(channel1[channel_id]) == -msats_sent_to_2 + assert sum(channel2[channel_id]) == msats_sent_to_2 + + # Chain balances for the channels should be opposite the channel balances. + assert sum(chain1[channel_id]) == -sum(channel1[channel_id]) + assert sum(chain2[channel_id]) == -sum(channel2[channel_id]) + + # Wallet balances should reflect reality + l1_wallet = sum([o['amount_msat'] for o in l1.rpc.listfunds()['outputs']]) + l2_wallet = sum([o['amount_msat'] for o in l2.rpc.listfunds()['outputs']]) + + if sum(chain1['wallet']) != l1_wallet: + print(f"sum({chain1['wallet']}) != {l1_wallet}") + assert False + if sum(chain2['wallet']) != l2_wallet: + print(f"sum({chain2['wallet']}) != {l2_wallet}") + assert False + + +@pytest.mark.openchannel('v1') +@pytest.mark.openchannel('v2') +@unittest.skipIf(TEST_NETWORK != 'regtest', "Amounts are for regtest.") +def test_coinmoves(node_factory, bitcoind): + l1, l2, l3 = node_factory.get_nodes(3) + + # Empty + expected_channel1 = [] + expected_channel2 = [] + expected_chain1 = [] + expected_chain2 = [] + check_channel_moves(l1, expected_channel1) + check_channel_moves(l2, expected_channel2) + check_chain_moves(l1, expected_chain1) + check_chain_moves(l2, expected_chain2) + + # MVT_DEPOSIT + addr = l1.rpc.newaddr()['bech32'] + txid_deposit = bitcoind.rpc.sendtoaddress(addr, 200000000 / 10**8) + bitcoind.generate_block(1, wait_for_mempool=1) + sync_blockheight(bitcoind, [l1]) + vout_deposit = only_one([out['n'] for out in bitcoind.rpc.gettransaction(txid_deposit, False, True)['decoded']['vout'] if out['scriptPubKey']['address'] == addr]) + + expected_chain1 += [{'account_id': 'wallet', + 'blockheight': 102, + 'credit_msat': 200000000000, + 'debit_msat': 0, + 'output_msat': 200000000000, + 'primary_tag': 'deposit', + 'extra_tags': [], + 'utxo': f"{txid_deposit}:{vout_deposit}"}] + check_channel_moves(l1, expected_channel1) + check_channel_moves(l2, expected_channel2) + check_chain_moves(l1, expected_chain1) + check_chain_moves(l2, expected_chain2) + + # Since sql uses pagination, rowids no longer change on each access! + first_rowid = only_one(only_one(l1.rpc.sql("SELECT rowid FROM chainmoves;")['rows'])) + + # MVT_WITHDRAWAL + addr = l3.rpc.newaddr()['bech32'] + withdraw = l1.rpc.withdraw(addr, 100000000) + bitcoind.generate_block(1, wait_for_mempool=1) + sync_blockheight(bitcoind, [l1]) + vout_withdrawal = only_one([out['n'] for out in bitcoind.rpc.decoderawtransaction(withdraw['tx'])['vout'] if out['scriptPubKey']['address'] == addr]) + + expected_chain1 += [{'account_id': 'external', + 'blockheight': 0, + 'credit_msat': 100000000000, + 'debit_msat': 0, + 'output_msat': 100000000000, + 'originating_account': 'wallet', + 'primary_tag': 'deposit', + 'extra_tags': [], + 'utxo': f"{withdraw['txid']}:{vout_withdrawal}"}, + # Spend + {'account_id': 'wallet', + 'blockheight': 103, + 'credit_msat': 0, + 'debit_msat': 200000000000, + 'primary_tag': 'withdrawal', + 'output_msat': 200000000000, + 'extra_tags': [], + 'spending_txid': withdraw['txid'], + 'utxo': f"{txid_deposit}:{vout_deposit}"}, + # Change + {'account_id': 'wallet', + 'blockheight': 103, + 'credit_msat': 99995433000, + 'debit_msat': 0, + 'primary_tag': 'deposit', + 'extra_tags': [], + 'output_msat': 99995433000, + 'utxo': f"{withdraw['txid']}:{vout_withdrawal ^ 1}"}] + check_channel_moves(l1, expected_channel1) + check_channel_moves(l2, expected_channel2) + check_chain_moves(l1, expected_chain1) + check_chain_moves(l2, expected_chain2) + + # MVT_CHANNEL_OPEN + l1.rpc.connect(l2.info['id'], 'localhost', l2.port) + fundchannel = l1.rpc.fundchannel(l2.info['id'], 'all') + bitcoind.generate_block(1, wait_for_mempool=fundchannel['txid']) + wait_for(lambda: all([c['state'] == 'CHANNELD_NORMAL' for c in l1.rpc.listpeerchannels(l2.info['id'])['channels']])) + expected_chain1 += [{'account_id': 'wallet', + 'blockheight': 104, + 'credit_msat': 0, + 'debit_msat': 99995433000, + 'output_msat': 99995433000, + 'primary_tag': 'withdrawal', + 'extra_tags': [], + 'spending_txid': fundchannel['txid'], + 'utxo': f"{withdraw['txid']}:{vout_withdrawal ^ 1}"}, + {'account_id': 'wallet', + 'blockheight': 104, + 'credit_msat': 25000000, + 'debit_msat': 0, + 'output_msat': 25000000, + 'primary_tag': 'deposit', + 'extra_tags': [], + 'utxo': f"{fundchannel['txid']}:{fundchannel['outnum'] ^ 1}"}, + {'account_id': fundchannel['channel_id'], + 'blockheight': 104, + 'credit_msat': 99965813000, + 'debit_msat': 0, + 'output_msat': 99965813000, + 'peer_id': l2.info['id'], + 'primary_tag': 'channel_open', + 'extra_tags': ['opener'], + 'utxo': f"{fundchannel['txid']}:{fundchannel['outnum']}"}] + expected_chain2 += [{'account_id': fundchannel['channel_id'], + 'blockheight': 104, + 'credit_msat': 0, + 'debit_msat': 0, + 'output_msat': 99965813000, + 'peer_id': l1.info['id'], + 'primary_tag': 'channel_open', + 'extra_tags': [], + 'utxo': f"{fundchannel['txid']}:{fundchannel['outnum']}"}] + check_channel_moves(l1, expected_channel1) + check_channel_moves(l2, expected_channel2) + check_chain_moves(l1, expected_chain1) + check_chain_moves(l2, expected_chain2) + + # MVT_INVOICE + inv = l2.rpc.invoice('any', 'test_coinmoves', 'test_coinmoves') + l1.rpc.xpay(inv['bolt11'], '1000sat') + # Make sure it's fully settled. + wait_for(lambda: only_one(l1.rpc.listpeerchannels(l2.info['id'])['channels'])['htlcs'] == []) + # We cheat and extract group id. + group_id = l1.rpc.listchannelmoves()['channelmoves'][-1]['group_id'] + expected_channel1 += [{'account_id': fundchannel['channel_id'], + 'credit_msat': 0, + 'debit_msat': 1000000, + 'primary_tag': 'invoice', + 'fees_msat': 0, + 'payment_hash': inv['payment_hash'], + 'group_id': group_id, + 'part_id': 1}] + expected_channel2 += [{'account_id': fundchannel['channel_id'], + 'credit_msat': 1000000, + 'debit_msat': 0, + 'primary_tag': 'invoice', + 'fees_msat': 0, + 'payment_hash': inv['payment_hash']}] + check_channel_moves(l1, expected_channel1) + check_channel_moves(l2, expected_channel2) + check_chain_moves(l1, expected_chain1) + check_chain_moves(l2, expected_chain2) + + # MVT_PUSHED + l3.rpc.connect(l1.info['id'], 'localhost', l1.port) + if not EXPERIMENTAL_DUAL_FUND: + l3fundchannel = l3.rpc.fundchannel(l1.info['id'], 40000000, push_msat=100000) + else: + l3fundchannel = l3.rpc.fundchannel(l1.info['id'], 40000000) + bitcoind.generate_block(1, wait_for_mempool=1) + wait_for(lambda: all([c['state'] == 'CHANNELD_NORMAL' for c in l1.rpc.listpeerchannels(l3.info['id'])['channels']])) + expected_chain1 += [{'account_id': l3fundchannel['channel_id'], + 'blockheight': 105, + 'credit_msat': 0, + 'debit_msat': 0, + 'output_msat': 40000000000, + 'peer_id': l3.info['id'], + 'primary_tag': 'channel_open', + 'extra_tags': [], + 'utxo': f"{l3fundchannel['txid']}:{l3fundchannel['outnum']}"}] + if not EXPERIMENTAL_DUAL_FUND: + expected_channel1 += [{'account_id': l3fundchannel['channel_id'], + 'credit_msat': 100000, + 'debit_msat': 0, + 'fees_msat': 0, + 'primary_tag': 'pushed'}] + check_channel_moves(l1, expected_channel1) + check_channel_moves(l2, expected_channel2) + check_chain_moves(l1, expected_chain1) + check_chain_moves(l2, expected_chain2) + + # MVT_ROUTED + # Make sure l3 sees l2. + bitcoind.generate_block(5) + wait_for(lambda: len(l3.rpc.listchannels()['channels']) == 4) + inv = l2.rpc.invoice('any', 'test_coinmoves2', 'test_coinmoves2') + l3.rpc.xpay(inv['bolt11'], '10000000sat') + # Make sure it's fully settled. + wait_for(lambda: only_one(l3.rpc.listpeerchannels(l1.info['id'])['channels'])['htlcs'] == []) + expected_channel1 += [{'account_id': fundchannel['channel_id'], + 'credit_msat': 0, + 'debit_msat': 10000000000, + 'fees_msat': 100001, + 'payment_hash': inv['payment_hash'], + 'primary_tag': 'routed'}, + {'account_id': l3fundchannel['channel_id'], + 'credit_msat': 10000100001, + 'debit_msat': 0, + 'fees_msat': 100001, + 'payment_hash': inv['payment_hash'], + 'primary_tag': 'routed'}] + expected_channel2 += [{'account_id': fundchannel['channel_id'], + 'credit_msat': 10000000000, + 'debit_msat': 0, + 'fees_msat': 0, + 'payment_hash': inv['payment_hash'], + 'primary_tag': 'invoice'}] + check_channel_moves(l1, expected_channel1) + check_channel_moves(l2, expected_channel2) + check_chain_moves(l1, expected_chain1) + check_chain_moves(l2, expected_chain2) + + # MVT_CHANNEL_CLOSE + close = l1.rpc.close(fundchannel['channel_id']) + bitcoind.generate_block(1, wait_for_mempool=1) + sync_blockheight(bitcoind, [l1, l2]) + # Determining our own output is harder than you might think! + l1_addrs = [a['p2tr'] for a in l1.rpc.listaddresses()['addresses'] if 'p2tr' in a] + l1_vout_close = only_one([out['n'] for out in bitcoind.rpc.decoderawtransaction(only_one(close['txs']))['vout'] if out['scriptPubKey']['address'] in l1_addrs]) + l2_addrs = [a['p2tr'] for a in l2.rpc.listaddresses()['addresses'] if 'p2tr' in a] + l2_vout_close = only_one([out['n'] for out in bitcoind.rpc.decoderawtransaction(only_one(close['txs']))['vout'] if out['scriptPubKey']['address'] in l2_addrs]) + expected_chain1 += [{'account_id': 'wallet', + 'blockheight': 111, + 'credit_msat': 89961918000, + 'debit_msat': 0, + 'extra_tags': [], + 'output_msat': 89961918000, + 'primary_tag': 'deposit', + 'utxo': f"{only_one(close['txids'])}:{l1_vout_close}"}, + {'account_id': fundchannel['channel_id'], + 'blockheight': 111, + 'credit_msat': 0, + 'debit_msat': 89964813000, + 'extra_tags': [], + 'output_count': 2, + 'output_msat': 99965813000, + 'primary_tag': 'channel_close', + 'spending_txid': only_one(close['txids']), + 'utxo': f"{fundchannel['txid']}:{fundchannel['outnum']}"}, + {'account_id': 'external', + 'blockheight': 111, + 'credit_msat': 10001000000, + 'debit_msat': 0, + 'extra_tags': [], + 'originating_account': fundchannel['channel_id'], + 'output_msat': 10001000000, + 'primary_tag': 'to_them', + 'utxo': f"{only_one(close['txids'])}:{l1_vout_close ^ 1}"}] + expected_chain2 += [{'account_id': 'wallet', + 'blockheight': 111, + 'credit_msat': 10001000000, + 'debit_msat': 0, + 'extra_tags': [], + 'output_msat': 10001000000, + 'primary_tag': 'deposit', + 'utxo': f"{only_one(close['txids'])}:{l2_vout_close}"}, + {'account_id': fundchannel['channel_id'], + 'blockheight': 111, + 'credit_msat': 0, + 'debit_msat': 10001000000, + 'extra_tags': [], + 'output_count': 2, + 'output_msat': 99965813000, + 'primary_tag': 'channel_close', + 'spending_txid': only_one(close['txids']), + 'utxo': f"{fundchannel['txid']}:{fundchannel['outnum']}"}, + {'account_id': 'external', + 'blockheight': 111, + 'credit_msat': 89961918000, + 'debit_msat': 0, + 'extra_tags': [], + 'originating_account': fundchannel['channel_id'], + 'output_msat': 89961918000, + 'primary_tag': 'to_them', + 'utxo': f"{only_one(close['txids'])}:{l2_vout_close ^ 1}"}] + check_channel_moves(l1, expected_channel1) + check_channel_moves(l2, expected_channel2) + check_chain_moves(l1, expected_chain1) + check_chain_moves(l2, expected_chain2) + + final_first_rowid = only_one(only_one(l1.rpc.sql("SELECT rowid FROM chainmoves ORDER BY rowid LIMIT 1;")['rows'])) + assert final_first_rowid == first_rowid + + +def setup_channel(bitcoind, l1, l2): + """Set up a balanced l1->l2 channel, return: + + l1's expected channel moves + l2's expected channel moves + l1's expected chain moves + l2's expected chain moves + The fundchannel return + """ + expected_channel1 = [] + expected_channel2 = [] + expected_chain1 = [] + expected_chain2 = [] + + addr = l1.rpc.newaddr()['bech32'] + txid_deposit = bitcoind.rpc.sendtoaddress(addr, 100000000 / 10**8) + bitcoind.generate_block(1, wait_for_mempool=1) + sync_blockheight(bitcoind, [l1]) + vout_deposit = only_one([out['n'] for out in bitcoind.rpc.gettransaction(txid_deposit, False, True)['decoded']['vout'] if out['scriptPubKey']['address'] == addr]) + + expected_chain1 += [{'account_id': 'wallet', + 'blockheight': 102, + 'credit_msat': 100000000000, + 'debit_msat': 0, + 'output_msat': 100000000000, + 'primary_tag': 'deposit', + 'extra_tags': [], + 'utxo': f"{txid_deposit}:{vout_deposit}"}] + l1.rpc.connect(l2.info['id'], 'localhost', l2.port) + fundchannel = l1.rpc.fundchannel(l2.info['id'], 'all') + bitcoind.generate_block(1, wait_for_mempool=fundchannel['txid']) + wait_for(lambda: all([c['state'] == 'CHANNELD_NORMAL' for c in l1.rpc.listpeerchannels(l2.info['id'])['channels']])) + expected_chain1 += [{'account_id': 'wallet', # Spent UTXO + 'blockheight': 103, + 'credit_msat': 0, + 'debit_msat': 100000000000, + 'output_msat': 100000000000, + 'primary_tag': 'withdrawal', + 'extra_tags': [], + 'spending_txid': fundchannel['txid'], + 'utxo': f"{txid_deposit}:{vout_deposit}"}, + {'account_id': 'wallet', # Change + 'blockheight': 103, + 'credit_msat': 25000000, + 'debit_msat': 0, + 'output_msat': 25000000, + 'primary_tag': 'deposit', + 'extra_tags': [], + 'utxo': f"{fundchannel['txid']}:{fundchannel['outnum'] ^ 1}"}, + {'account_id': fundchannel['channel_id'], # Channel open + 'blockheight': 103, + 'credit_msat': 99970073000, + 'debit_msat': 0, + 'output_msat': 99970073000, + 'peer_id': l2.info['id'], + 'primary_tag': 'channel_open', + 'extra_tags': ['opener'], + 'utxo': f"{fundchannel['txid']}:{fundchannel['outnum']}"}] + expected_chain2 += [{'account_id': fundchannel['channel_id'], # Channel open + 'blockheight': 103, + 'credit_msat': 0, + 'debit_msat': 0, + 'output_msat': 99970073000, + 'peer_id': l1.info['id'], + 'primary_tag': 'channel_open', + 'extra_tags': [], + 'utxo': f"{fundchannel['txid']}:{fundchannel['outnum']}"}] + check_channel_moves(l1, expected_channel1) + check_channel_moves(l2, expected_channel2) + check_chain_moves(l1, expected_chain1) + check_chain_moves(l2, expected_chain2) + + inv = l2.rpc.invoice('any', 'setup_channel', 'setup_channel') + routestep = { + 'amount_msat': 50000000000, + 'id': l2.info['id'], + 'delay': 5, + 'channel': l1.get_channel_scid(l2), + } + l1.rpc.sendpay([routestep], inv['payment_hash'], payment_secret=inv['payment_secret'], bolt11=inv['bolt11']) + wait_for(lambda: only_one(l1.rpc.listpeerchannels(l2.info['id'])['channels'])['htlcs'] == []) + expected_channel1 += [{'account_id': fundchannel['channel_id'], + 'credit_msat': 0, + 'debit_msat': 50000000000, + 'primary_tag': 'invoice', + 'fees_msat': 0, + 'payment_hash': inv['payment_hash'], + 'group_id': 1, + 'part_id': 0}] + expected_channel2 += [{'account_id': fundchannel['channel_id'], + 'credit_msat': 50000000000, + 'debit_msat': 0, + 'primary_tag': 'invoice', + 'fees_msat': 0, + 'payment_hash': inv['payment_hash']}] + check_channel_moves(l1, expected_channel1) + check_channel_moves(l2, expected_channel2) + check_chain_moves(l1, expected_chain1) + check_chain_moves(l2, expected_chain2) + + return (expected_channel1, expected_channel2, expected_chain1, expected_chain2, fundchannel) + + +# There are many unilateral close variants to test: +# - HTLC not yet included in tx. +# - HTLC included in tx, times out. +# - HTLC included in tx, we fulfill. +# - HTLC not included in tx, because one side considers it fulfilled. +# - HTLC is too small to appear in tx, lost to fees. +@pytest.mark.openchannel('v1') +@pytest.mark.openchannel('v2') +@unittest.skipIf(TEST_NETWORK != 'regtest', "Amounts are for regtest.") +def test_coinmoves_unilateral_htlc_before_included(node_factory, bitcoind): + # l2 includes it, but l1 doesn't get commitment, so it drops to chain without it. + if EXPERIMENTAL_DUAL_FUND: + disc = ['-WIRE_COMMITMENT_SIGNED*4'] + else: + disc = ['-WIRE_COMMITMENT_SIGNED*3'] + l1, l2 = node_factory.get_nodes(2, opts=[{}, {'disconnect': disc}]) + + expected_channel1, expected_channel2, expected_chain1, expected_chain2, fundchannel = setup_channel(bitcoind, l1, l2) + + # This HTLC doesn't make it to full confirmation. + inv = l2.rpc.invoice('any', 'test_coinmoves_unilateral_htlc_in_before_included', 'test_coinmoves_unilateral_htlc_in_before_included') + routestep = { + # Too small to make it worth spending anchor + 'amount_msat': 1000000, + 'id': l2.info['id'], + 'delay': 5, + 'channel': l1.get_channel_scid(l2), + } + l1.rpc.sendpay([routestep], inv['payment_hash'], payment_secret=inv['payment_secret'], bolt11=inv['bolt11']) + wait_for(lambda: only_one(l1.rpc.listpeers(l2.info['id'])['peers'])['connected'] is False) + + check_channel_moves(l1, expected_channel1) + check_channel_moves(l2, expected_channel2) + check_chain_moves(l1, expected_chain1) + check_chain_moves(l2, expected_chain2) + + close_info = l1.rpc.close(l2.info['id'], unilateraltimeout=1) + bitcoind.generate_block(1, wait_for_mempool=1) + + # Make sure onchaind has digested it. + l1.daemon.wait_for_log('5 outputs unresolved: in 4 blocks will spend DELAYED_OUTPUT_TO_US') + l2.daemon.wait_for_log('All outputs resolved: waiting 100 more blocks before forgetting channel') + + # Which outputs are anchors, and which are to us and which to them? + # Use onchaind's logs, eg: + # Tracking output 0e1cfbc2be0aada02222a163a1a413fd0b06bae8017c3626cbf8816499dadc09:0: OUR_UNILATERAL/ANCHOR_TO_THEM + line = l1.daemon.is_in_log('Tracking output.*/ANCHOR_TO_THEM') + anch_to_l2 = int(re.search(r'output [0-9a-f]{64}:([0-9]):', line).group(1)) + line = l1.daemon.is_in_log('Tracking output.*/ANCHOR_TO_US') + anch_to_l1 = int(re.search(r'output [0-9a-f]{64}:([0-9]):', line).group(1)) + line = l1.daemon.is_in_log('Tracking output.*/DELAYED_OUTPUT_TO_US') + to_l1 = int(re.search(r'output [0-9a-f]{64}:([0-9]):', line).group(1)) + line = l1.daemon.is_in_log('Tracking output.*/OUTPUT_TO_THEM') + to_l2 = int(re.search(r'output [0-9a-f]{64}:([0-9]):', line).group(1)) + + expected_chain1 += [{'account_id': fundchannel['channel_id'], + 'blockheight': 104, + 'credit_msat': 0, + 'debit_msat': 99970073000 - 50000000000, + 'extra_tags': [], + 'output_count': 4, + 'output_msat': 99970073000, + 'primary_tag': 'channel_close', + 'spending_txid': only_one(close_info['txids']), + 'utxo': f"{fundchannel['txid']}:{fundchannel['outnum']}"}, + {'account_id': 'external', + 'blockheight': 104, + 'credit_msat': 330000, + 'debit_msat': 0, + 'extra_tags': [], + 'originating_account': fundchannel['channel_id'], + 'output_msat': 330000, + 'primary_tag': 'anchor', + 'utxo': f"{only_one(close_info['txids'])}:{anch_to_l2}"}, + {'account_id': 'external', + 'blockheight': 104, + 'credit_msat': 330000, + 'debit_msat': 0, + 'extra_tags': [], + 'originating_account': fundchannel['channel_id'], + 'output_msat': 330000, + 'primary_tag': 'anchor', + 'utxo': f"{only_one(close_info['txids'])}:{anch_to_l1}"}, + {'account_id': 'external', + 'blockheight': 104, + 'credit_msat': 50000000000, + 'debit_msat': 0, + 'extra_tags': [], + 'originating_account': fundchannel['channel_id'], + 'output_msat': 50000000000, + 'primary_tag': 'to_them', + 'utxo': f"{only_one(close_info['txids'])}:{to_l2}"}] + expected_chain2 += [{'account_id': fundchannel['channel_id'], + 'blockheight': 104, + 'credit_msat': 0, + 'debit_msat': 50000000000, + 'extra_tags': [], + 'output_count': 4, + 'output_msat': 99970073000, + 'primary_tag': 'channel_close', + 'spending_txid': only_one(close_info['txids']), + 'utxo': f"{fundchannel['txid']}:{fundchannel['outnum']}"}, + {'account_id': 'external', + 'blockheight': 104, + 'credit_msat': 330000, + 'debit_msat': 0, + 'extra_tags': [], + 'originating_account': fundchannel['channel_id'], + 'output_msat': 330000, + 'primary_tag': 'anchor', + 'utxo': f"{only_one(close_info['txids'])}:{anch_to_l2}"}, + {'account_id': 'external', + 'blockheight': 104, + 'credit_msat': 330000, + 'debit_msat': 0, + 'extra_tags': [], + 'originating_account': fundchannel['channel_id'], + 'output_msat': 330000, + 'primary_tag': 'anchor', + 'utxo': f"{only_one(close_info['txids'])}:{anch_to_l1}"}, + {'account_id': 'external', + 'blockheight': 104, + 'credit_msat': 49965193000, + 'debit_msat': 0, + 'extra_tags': [], + 'originating_account': fundchannel['channel_id'], + 'output_msat': 49965193000, + 'primary_tag': 'to_them', + 'utxo': f"{only_one(close_info['txids'])}:{to_l1}"}, + {'account_id': 'wallet', + 'blockheight': 104, + 'credit_msat': 50000000000, + 'debit_msat': 0, + 'extra_tags': [], + 'originating_account': fundchannel['channel_id'], + 'output_msat': 50000000000, + 'primary_tag': 'deposit', + 'utxo': f"{only_one(close_info['txids'])}:{to_l2}"}] + check_channel_moves(l1, expected_channel1) + check_chain_moves(l1, expected_chain1) + check_channel_moves(l2, expected_channel2) + check_chain_moves(l2, expected_chain2) + + bitcoind.generate_block(4) + l1.daemon.wait_for_log('waiting confirmation that we spent DELAYED_OUTPUT_TO_US') + check_channel_moves(l1, expected_channel1) + check_chain_moves(l1, expected_chain1) + bitcoind.generate_block(1, wait_for_mempool=1) + + line = l1.daemon.wait_for_log('Resolved OUR_UNILATERAL/DELAYED_OUTPUT_TO_US by our proposal OUR_DELAYED_RETURN_TO_WALLET') + to_us_txid = re.search(r'by our proposal OUR_DELAYED_RETURN_TO_WALLET \(([0-9a-f]{64})\)', line).group(1) + + expected_chain1 += [{'account_id': 'wallet', + 'blockheight': 109, + 'credit_msat': 49965059000, + 'debit_msat': 0, + 'extra_tags': [], + 'output_msat': 49965059000, + 'primary_tag': 'deposit', + 'utxo': f"{to_us_txid}:0"}, + {'account_id': fundchannel['channel_id'], + 'blockheight': 104, + 'credit_msat': 49965193000, + 'debit_msat': 0, + 'extra_tags': [], + 'output_msat': 49965193000, + 'primary_tag': 'delayed_to_us', + 'utxo': f"{only_one(close_info['txids'])}:{to_l1}"}, + {'account_id': fundchannel['channel_id'], + 'blockheight': 109, + 'credit_msat': 0, + 'debit_msat': 49965193000, + 'extra_tags': [], + 'output_msat': 49965193000, + 'primary_tag': 'to_wallet', + 'spending_txid': to_us_txid, + 'utxo': f"{only_one(close_info['txids'])}:{to_l1}"}] + check_channel_moves(l1, expected_channel1) + check_chain_moves(l1, expected_chain1) + check_channel_moves(l2, expected_channel2) + check_chain_moves(l2, expected_chain2) + + # Make sure it's stable! + bitcoind.generate_block(100) + sync_blockheight(bitcoind, [l1, l2]) + time.sleep(5) + check_channel_moves(l1, expected_channel1) + check_chain_moves(l1, expected_chain1) + check_channel_moves(l2, expected_channel2) + check_chain_moves(l2, expected_chain2) + l1.daemon.wait_for_log('onchaind complete, forgetting peer') + l2.daemon.wait_for_log('onchaind complete, forgetting peer') + + # We didn't send any HTLCs + check_balances(l1, l2, fundchannel['channel_id'], 0) + + +@pytest.mark.openchannel('v1') +@pytest.mark.openchannel('v2') +@unittest.skipIf(TEST_NETWORK != 'regtest', "Amounts are for regtest.") +def test_coinmoves_unilateral_htlc_timeout(node_factory, bitcoind): + """HTLC times out""" + l1, l2 = node_factory.get_nodes(2, opts=[{}, + {'disconnect': ['-WIRE_UPDATE_FAIL_HTLC']}]) + + expected_channel1, expected_channel2, expected_chain1, expected_chain2, fundchannel = setup_channel(bitcoind, l1, l2) + + inv = l2.rpc.invoice('any', 'test_coinmoves_unilateral_htlc_timeout', 'test_coinmoves_unilateral_htlc_timeout') + l2.rpc.delinvoice('test_coinmoves_unilateral_htlc_timeout', 'unpaid') + routestep = { + # We will spend anchor to make this confirm. + 'amount_msat': 100000000, + 'id': l2.info['id'], + 'delay': 10, + 'channel': l1.get_channel_scid(l2), + } + l1.rpc.sendpay([routestep], inv['payment_hash'], payment_secret=inv['payment_secret'], bolt11=inv['bolt11']) + wait_for(lambda: only_one(l1.rpc.listpeers(l2.info['id'])['peers'])['connected'] is False) + + check_channel_moves(l1, expected_channel1) + check_channel_moves(l2, expected_channel2) + check_chain_moves(l1, expected_chain1) + check_chain_moves(l2, expected_chain2) + + close_info = l1.rpc.close(l2.info['id'], unilateraltimeout=1) + + # We will spend anchor to confirm this. + line = l1.daemon.wait_for_log("Creating anchor spend for local commit tx ") + anchor_spend_txid = re.search(r'Creating anchor spend for local commit tx ([0-9a-f]{64})', line).group(1) + + bitcoind.generate_block(1, wait_for_mempool=1) + sync_blockheight(bitcoind, [l1, l2]) + + # Make sure onchaind has digested it. + l1.daemon.wait_for_log('6 outputs unresolved: in 4 blocks will spend DELAYED_OUTPUT_TO_US') + l2.daemon.wait_for_log('6 outputs unresolved') + + # Which outputs are anchors, and which are to us and which to them? + # Use onchaind's logs, eg: + # Tracking output 0e1cfbc2be0aada02222a163a1a413fd0b06bae8017c3626cbf8816499dadc09:0: OUR_UNILATERAL/ANCHOR_TO_THEM + line = l1.daemon.is_in_log('Tracking output.*/ANCHOR_TO_THEM') + anch_to_l2 = int(re.search(r'output [0-9a-f]{64}:([0-9]):', line).group(1)) + line = l1.daemon.is_in_log('Tracking output.*/ANCHOR_TO_US') + anch_to_l1 = int(re.search(r'output [0-9a-f]{64}:([0-9]):', line).group(1)) + line = l1.daemon.is_in_log('Tracking output.*/DELAYED_OUTPUT_TO_US') + to_l1 = int(re.search(r'output [0-9a-f]{64}:([0-9]):', line).group(1)) + line = l1.daemon.is_in_log('Tracking output.*/OUTPUT_TO_THEM') + to_l2 = int(re.search(r'output [0-9a-f]{64}:([0-9]):', line).group(1)) + line = l1.daemon.is_in_log('Tracking output.*/OUR_HTLC') + htlc = int(re.search(r'output [0-9a-f]{64}:([0-9]):', line).group(1)) + + expected_chain1 += [{'account_id': 'wallet', # Anchor spend from fundchannel change + 'blockheight': 104, + 'credit_msat': 0, + 'debit_msat': 25000000, + 'extra_tags': [], + 'output_msat': 25000000, + 'primary_tag': 'withdrawal', + 'spending_txid': anchor_spend_txid, + 'utxo': f"{fundchannel['txid']}:{fundchannel['outnum'] ^ 1}"}, + {'account_id': 'wallet', # change from anchor spend + 'blockheight': 104, + 'credit_msat': 15579000, + 'debit_msat': 0, + 'extra_tags': [], + 'output_msat': 15579000, + 'primary_tag': 'deposit', + 'utxo': f"{anchor_spend_txid}:0"}, + {'account_id': fundchannel['channel_id'], + 'blockheight': 104, + 'credit_msat': 0, + 'debit_msat': 99970073000 - 50000000000, + 'extra_tags': [], + 'output_count': 5, + 'output_msat': 99970073000, + 'primary_tag': 'channel_close', + 'spending_txid': only_one(close_info['txids']), + 'utxo': f"{fundchannel['txid']}:{fundchannel['outnum']}"}, + {'account_id': 'external', + 'blockheight': 104, + 'credit_msat': 330000, + 'debit_msat': 0, + 'extra_tags': [], + 'originating_account': fundchannel['channel_id'], + 'output_msat': 330000, + 'primary_tag': 'anchor', + 'utxo': f"{only_one(close_info['txids'])}:{anch_to_l2}"}, + {'account_id': 'external', + 'blockheight': 104, + 'credit_msat': 330000, + 'debit_msat': 0, + 'extra_tags': [], + 'originating_account': fundchannel['channel_id'], + 'output_msat': 330000, + 'primary_tag': 'anchor', + 'utxo': f"{only_one(close_info['txids'])}:{anch_to_l1}"}, + {'account_id': 'external', + 'blockheight': 104, + 'credit_msat': 50000000000, + 'debit_msat': 0, + 'extra_tags': [], + 'originating_account': fundchannel['channel_id'], + 'output_msat': 50000000000, + 'primary_tag': 'to_them', + 'utxo': f"{only_one(close_info['txids'])}:{to_l2}"}] + expected_chain2 += [{'account_id': fundchannel['channel_id'], + 'blockheight': 104, + 'credit_msat': 0, + 'debit_msat': 50000000000, + 'extra_tags': [], + 'output_count': 5, + 'output_msat': 99970073000, + 'primary_tag': 'channel_close', + 'spending_txid': only_one(close_info['txids']), + 'utxo': f"{fundchannel['txid']}:{fundchannel['outnum']}"}, + {'account_id': 'external', + 'blockheight': 104, + 'credit_msat': 330000, + 'debit_msat': 0, + 'extra_tags': [], + 'originating_account': fundchannel['channel_id'], + 'output_msat': 330000, + 'primary_tag': 'anchor', + 'utxo': f"{only_one(close_info['txids'])}:{anch_to_l2}"}, + {'account_id': 'external', + 'blockheight': 104, + 'credit_msat': 330000, + 'debit_msat': 0, + 'extra_tags': [], + 'originating_account': fundchannel['channel_id'], + 'output_msat': 330000, + 'primary_tag': 'anchor', + 'utxo': f"{only_one(close_info['txids'])}:{anch_to_l1}"}, + {'account_id': 'external', + 'blockheight': 104, + 'credit_msat': 49864547000, + 'debit_msat': 0, + 'extra_tags': [], + 'originating_account': fundchannel['channel_id'], + 'output_msat': 49864547000, + 'primary_tag': 'to_them', + 'utxo': f"{only_one(close_info['txids'])}:{to_l1}"}, + {'account_id': 'wallet', + 'blockheight': 104, + 'credit_msat': 50000000000, + 'debit_msat': 0, + 'extra_tags': [], + 'originating_account': fundchannel['channel_id'], + 'output_msat': 50000000000, + 'primary_tag': 'deposit', + 'utxo': f"{only_one(close_info['txids'])}:{to_l2}"}] + check_channel_moves(l1, expected_channel1) + check_chain_moves(l1, expected_chain1) + check_channel_moves(l2, expected_channel2) + check_chain_moves(l2, expected_chain2) + + bitcoind.generate_block(4) + l1.daemon.wait_for_log('waiting confirmation that we spent DELAYED_OUTPUT_TO_US') + check_channel_moves(l1, expected_channel1) + check_chain_moves(l1, expected_chain1) + bitcoind.generate_block(1, wait_for_mempool=1) + + line = l1.daemon.wait_for_log('Resolved OUR_UNILATERAL/DELAYED_OUTPUT_TO_US by our proposal OUR_DELAYED_RETURN_TO_WALLET') + to_l1_txid = re.search(r'by our proposal OUR_DELAYED_RETURN_TO_WALLET \(([0-9a-f]{64})\)', line).group(1) + + expected_chain1 += [{'account_id': 'wallet', + 'blockheight': 109, + 'credit_msat': 49864413000, + 'debit_msat': 0, + 'extra_tags': [], + 'output_msat': 49864413000, + 'primary_tag': 'deposit', + 'utxo': f"{to_l1_txid}:0"}, + {'account_id': fundchannel['channel_id'], + 'blockheight': 104, + 'credit_msat': 49864547000, + 'debit_msat': 0, + 'extra_tags': [], + 'output_msat': 49864547000, + 'primary_tag': 'delayed_to_us', + 'utxo': f"{only_one(close_info['txids'])}:{to_l1}"}, + {'account_id': fundchannel['channel_id'], + 'blockheight': 109, + 'credit_msat': 0, + 'debit_msat': 49864547000, + 'extra_tags': [], + 'output_msat': 49864547000, + 'primary_tag': 'to_wallet', + 'spending_txid': to_l1_txid, + 'utxo': f"{only_one(close_info['txids'])}:{to_l1}"}] + check_channel_moves(l1, expected_channel1) + check_chain_moves(l1, expected_chain1) + check_channel_moves(l2, expected_channel2) + check_chain_moves(l2, expected_chain2) + + # When l1 spends the htlc_tx, it will grab a UTXO. Remove existing ones + # so it's deterministic. + l1.rpc.fundpsbt('all', 0, 0, reserve=100) + + bitcoind.generate_block(5) + l1.daemon.wait_for_log('waiting confirmation that we spent OUR_HTLC') + bitcoind.generate_block(1, wait_for_mempool=1) + + line = l1.daemon.wait_for_log('Resolved OUR_UNILATERAL/OUR_HTLC by our proposal OUR_HTLC_TIMEOUT_TX') + htlc_timeout_txid = re.search(r'by our proposal OUR_HTLC_TIMEOUT_TX \(([0-9a-f]{64})\)', line).group(1) + expected_chain1 += [{'account_id': 'wallet', + 'blockheight': 115, + 'credit_msat': 0, + 'debit_msat': 15579000, + 'extra_tags': [], + 'output_msat': 15579000, + 'primary_tag': 'withdrawal', + 'spending_txid': htlc_timeout_txid, + 'utxo': f"{anchor_spend_txid}:0"}, + {'account_id': 'wallet', + 'blockheight': 115, + 'credit_msat': 6358000, + 'debit_msat': 0, + 'extra_tags': [], + 'output_msat': 6358000, + 'primary_tag': 'deposit', + 'utxo': f"{htlc_timeout_txid}:1"}, + {'account_id': fundchannel['channel_id'], + 'blockheight': 104, + 'credit_msat': 100000000, + 'debit_msat': 0, + 'extra_tags': [], + 'output_msat': 100000000, + 'primary_tag': 'htlc_timeout', + 'utxo': f"{only_one(close_info['txids'])}:{htlc}"}, + {'account_id': fundchannel['channel_id'], + 'blockheight': 115, + 'credit_msat': 0, + 'debit_msat': 100000000, + 'extra_tags': [], + 'output_msat': 100000000, + 'primary_tag': 'htlc_timeout', + 'spending_txid': htlc_timeout_txid, + 'utxo': f"{only_one(close_info['txids'])}:{htlc}"}] + expected_chain2 += [{'account_id': 'external', + 'blockheight': 104, + 'credit_msat': 100000000, + 'debit_msat': 0, + 'extra_tags': [], + 'originating_account': fundchannel['channel_id'], + 'output_msat': 100000000, + 'primary_tag': 'htlc_timeout', + 'utxo': f"{only_one(close_info['txids'])}:{htlc}"}] + check_channel_moves(l1, expected_channel1) + check_chain_moves(l1, expected_chain1) + check_channel_moves(l2, expected_channel2) + check_chain_moves(l2, expected_chain2) + + l1.daemon.wait_for_log("Telling lightningd about OUR_DELAYED_RETURN_TO_WALLET to resolve OUR_HTLC_TIMEOUT_TX/DELAYED_OUTPUT_TO_US after block 119") + bitcoind.generate_block(4) + l1.daemon.wait_for_log("waiting confirmation that we spent DELAYED_OUTPUT_TO_US .* using OUR_DELAYED_RETURN_TO_WALLET") + bitcoind.generate_block(1, wait_for_mempool=1) + line = l1.daemon.wait_for_log('Resolved OUR_HTLC_TIMEOUT_TX/DELAYED_OUTPUT_TO_US by our proposal OUR_DELAYED_RETURN_TO_WALLET') + htlc_to_l1_txid = re.search(r'by our proposal OUR_DELAYED_RETURN_TO_WALLET \(([0-9a-f]{64})\)', line).group(1) + + expected_chain1 += [{'account_id': 'wallet', + 'blockheight': 120, + 'credit_msat': 99866000, + 'debit_msat': 0, + 'extra_tags': [], + 'output_msat': 99866000, + 'primary_tag': 'deposit', + 'utxo': f"{htlc_to_l1_txid}:0"}, + {'account_id': fundchannel['channel_id'], + 'blockheight': 115, + 'credit_msat': 100000000, + 'debit_msat': 0, + 'extra_tags': [], + 'output_msat': 100000000, + 'primary_tag': 'htlc_tx', + 'utxo': f"{htlc_timeout_txid}:0"}, + {'account_id': fundchannel['channel_id'], + 'blockheight': 120, + 'credit_msat': 0, + 'debit_msat': 100000000, + 'extra_tags': [], + 'output_msat': 100000000, + 'primary_tag': 'to_wallet', + 'spending_txid': htlc_to_l1_txid, + 'utxo': f"{htlc_timeout_txid}:0"}] + check_channel_moves(l1, expected_channel1) + check_chain_moves(l1, expected_chain1) + check_channel_moves(l2, expected_channel2) + check_chain_moves(l2, expected_chain2) + + # Make sure it's stable! + bitcoind.generate_block(100) + sync_blockheight(bitcoind, [l1, l2]) + time.sleep(5) + check_channel_moves(l1, expected_channel1) + check_chain_moves(l1, expected_chain1) + check_channel_moves(l2, expected_channel2) + check_chain_moves(l2, expected_chain2) + l1.daemon.wait_for_log('onchaind complete, forgetting peer') + l2.daemon.wait_for_log('onchaind complete, forgetting peer') + + # We didn't send any HTLCs + check_balances(l1, l2, fundchannel['channel_id'], 0) + + +@pytest.mark.openchannel('v1') +@pytest.mark.openchannel('v2') +@unittest.skipIf(TEST_NETWORK != 'regtest', "Amounts are for regtest.") +def test_coinmoves_unilateral_htlc_dust(node_factory, bitcoind): + """HTLC too small to appear in tx, lost to fees""" + l1, l2 = node_factory.get_nodes(2, opts=[{}, + {'disconnect': ['-WIRE_UPDATE_FAIL_HTLC']}]) + + expected_channel1, expected_channel2, expected_chain1, expected_chain2, fundchannel = setup_channel(bitcoind, l1, l2) + + inv = l2.rpc.invoice('any', 'test_coinmoves_unilateral_htlc_dust', 'test_coinmoves_unilateral_htlc_dust') + l2.rpc.delinvoice('test_coinmoves_unilateral_htlc_dust', 'unpaid') + routestep = { + 'amount_msat': 10000, + 'id': l2.info['id'], + 'delay': 10, + 'channel': l1.get_channel_scid(l2), + } + l1.rpc.sendpay([routestep], inv['payment_hash'], payment_secret=inv['payment_secret'], bolt11=inv['bolt11']) + wait_for(lambda: only_one(l1.rpc.listpeers(l2.info['id'])['peers'])['connected'] is False) + + check_channel_moves(l1, expected_channel1) + check_channel_moves(l2, expected_channel2) + check_chain_moves(l1, expected_chain1) + check_chain_moves(l2, expected_chain2) + + close_info = l1.rpc.close(l2.info['id'], unilateraltimeout=1) + bitcoind.generate_block(1, wait_for_mempool=1) + sync_blockheight(bitcoind, [l1, l2]) + + # Make sure onchaind has digested it. + l1.daemon.wait_for_log('5 outputs unresolved: in 4 blocks will spend DELAYED_OUTPUT_TO_US') + l2.daemon.wait_for_log("All outputs resolved: waiting 100 more blocks before forgetting channel") + + # Which outputs are anchors, and which are to us and which to them? + # Use onchaind's logs, eg: + # Tracking output 0e1cfbc2be0aada02222a163a1a413fd0b06bae8017c3626cbf8816499dadc09:0: OUR_UNILATERAL/ANCHOR_TO_THEM + line = l1.daemon.is_in_log('Tracking output.*/ANCHOR_TO_THEM') + anch_to_l2 = int(re.search(r'output [0-9a-f]{64}:([0-9]):', line).group(1)) + line = l1.daemon.is_in_log('Tracking output.*/ANCHOR_TO_US') + anch_to_l1 = int(re.search(r'output [0-9a-f]{64}:([0-9]):', line).group(1)) + line = l1.daemon.is_in_log('Tracking output.*/DELAYED_OUTPUT_TO_US') + to_l1 = int(re.search(r'output [0-9a-f]{64}:([0-9]):', line).group(1)) + line = l1.daemon.is_in_log('Tracking output.*/OUTPUT_TO_THEM') + to_l2 = int(re.search(r'output [0-9a-f]{64}:([0-9]):', line).group(1)) + + expected_chain1 += [{'account_id': fundchannel['channel_id'], + 'blockheight': 104, + 'credit_msat': 0, + 'debit_msat': 99970073000 - 50000000000, + 'extra_tags': [], + 'output_count': 4, + 'output_msat': 99970073000, + 'primary_tag': 'channel_close', + 'spending_txid': only_one(close_info['txids']), + 'utxo': f"{fundchannel['txid']}:{fundchannel['outnum']}"}, + {'account_id': 'external', + 'blockheight': 104, + 'credit_msat': 330000, + 'debit_msat': 0, + 'extra_tags': [], + 'originating_account': fundchannel['channel_id'], + 'output_msat': 330000, + 'primary_tag': 'anchor', + 'utxo': f"{only_one(close_info['txids'])}:{anch_to_l2}"}, + {'account_id': 'external', + 'blockheight': 104, + 'credit_msat': 330000, + 'debit_msat': 0, + 'extra_tags': [], + 'originating_account': fundchannel['channel_id'], + 'output_msat': 330000, + 'primary_tag': 'anchor', + 'utxo': f"{only_one(close_info['txids'])}:{anch_to_l1}"}, + {'account_id': 'external', + 'blockheight': 104, + 'credit_msat': 50000000000, + 'debit_msat': 0, + 'extra_tags': [], + 'originating_account': fundchannel['channel_id'], + 'output_msat': 50000000000, + 'primary_tag': 'to_them', + 'utxo': f"{only_one(close_info['txids'])}:{to_l2}"}] + expected_chain2 += [{'account_id': fundchannel['channel_id'], + 'blockheight': 104, + 'credit_msat': 0, + 'debit_msat': 50000000000, + 'extra_tags': [], + 'output_count': 4, + 'output_msat': 99970073000, + 'primary_tag': 'channel_close', + 'spending_txid': only_one(close_info['txids']), + 'utxo': f"{fundchannel['txid']}:{fundchannel['outnum']}"}, + {'account_id': 'external', + 'blockheight': 104, + 'credit_msat': 330000, + 'debit_msat': 0, + 'extra_tags': [], + 'originating_account': fundchannel['channel_id'], + 'output_msat': 330000, + 'primary_tag': 'anchor', + 'utxo': f"{only_one(close_info['txids'])}:{anch_to_l2}"}, + {'account_id': 'external', + 'blockheight': 104, + 'credit_msat': 330000, + 'debit_msat': 0, + 'extra_tags': [], + 'originating_account': fundchannel['channel_id'], + 'output_msat': 330000, + 'primary_tag': 'anchor', + 'utxo': f"{only_one(close_info['txids'])}:{anch_to_l1}"}, + {'account_id': 'external', + 'blockheight': 104, + 'credit_msat': 49965183000, + 'debit_msat': 0, + 'extra_tags': [], + 'originating_account': fundchannel['channel_id'], + 'output_msat': 49965183000, + 'primary_tag': 'to_them', + 'utxo': f"{only_one(close_info['txids'])}:{to_l1}"}, + {'account_id': 'wallet', + 'blockheight': 104, + 'credit_msat': 50000000000, + 'debit_msat': 0, + 'extra_tags': [], + 'originating_account': fundchannel['channel_id'], + 'output_msat': 50000000000, + 'primary_tag': 'deposit', + 'utxo': f"{only_one(close_info['txids'])}:{to_l2}"}] + check_channel_moves(l1, expected_channel1) + check_chain_moves(l1, expected_chain1) + check_channel_moves(l2, expected_channel2) + check_chain_moves(l2, expected_chain2) + + bitcoind.generate_block(4) + l1.daemon.wait_for_log("waiting confirmation that we spent DELAYED_OUTPUT_TO_US .* using OUR_DELAYED_RETURN_TO_WALLET") + bitcoind.generate_block(1, wait_for_mempool=1) + + line = l1.daemon.wait_for_log('Resolved OUR_UNILATERAL/DELAYED_OUTPUT_TO_US by our proposal OUR_DELAYED_RETURN_TO_WALLET') + to_l1_txid = re.search(r'by our proposal OUR_DELAYED_RETURN_TO_WALLET \(([0-9a-f]{64})\)', line).group(1) + + expected_chain1 += [{'account_id': 'wallet', + 'blockheight': 109, + 'credit_msat': 49965049000, + 'debit_msat': 0, + 'extra_tags': [], + 'output_msat': 49965049000, + 'primary_tag': 'deposit', + 'utxo': f"{to_l1_txid}:0"}, + {'account_id': fundchannel['channel_id'], + 'blockheight': 104, + 'credit_msat': 49965183000, + 'debit_msat': 0, + 'extra_tags': [], + 'output_msat': 49965183000, + 'primary_tag': 'delayed_to_us', + 'utxo': f"{only_one(close_info['txids'])}:{to_l1}"}, + {'account_id': fundchannel['channel_id'], + 'blockheight': 109, + 'credit_msat': 0, + 'debit_msat': 49965183000, + 'extra_tags': [], + 'output_msat': 49965183000, + 'primary_tag': 'to_wallet', + 'spending_txid': to_l1_txid, + 'utxo': f"{only_one(close_info['txids'])}:{to_l1}"}] + check_channel_moves(l1, expected_channel1) + check_chain_moves(l1, expected_chain1) + check_channel_moves(l2, expected_channel2) + check_chain_moves(l2, expected_chain2) + + l1.daemon.wait_for_log('All outputs resolved: waiting 100 more blocks before forgetting channel') + + # Make sure it's stable! + bitcoind.generate_block(100) + sync_blockheight(bitcoind, [l1, l2]) + time.sleep(5) + check_channel_moves(l1, expected_channel1) + check_chain_moves(l1, expected_chain1) + check_channel_moves(l2, expected_channel2) + check_chain_moves(l2, expected_chain2) + l1.daemon.wait_for_log('onchaind complete, forgetting peer') + l2.daemon.wait_for_log('onchaind complete, forgetting peer') + + # We send a HTLC but it didn't finalize. + check_balances(l1, l2, fundchannel['channel_id'], 0) + + +@pytest.mark.openchannel('v1') +@pytest.mark.openchannel('v2') +@unittest.skipIf(TEST_NETWORK != 'regtest', "Amounts are for regtest.") +def test_coinmoves_unilateral_htlc_fulfill(node_factory, bitcoind): + """HTLC gets fulfilled (onchain)""" + l1, l2 = node_factory.get_nodes(2, opts=[{}, + {'disconnect': ['-WIRE_UPDATE_FULFILL_HTLC*2']}]) + + expected_channel1, expected_channel2, expected_chain1, expected_chain2, fundchannel = setup_channel(bitcoind, l1, l2) + + inv = l2.rpc.invoice('any', 'test_coinmoves_unilateral_htlc_fulfill', 'test_coinmoves_unilateral_htlc_fulfill') + routestep = { + # We will spend anchor to make this confirm. + 'amount_msat': 100000000, + 'id': l2.info['id'], + 'delay': 10, + 'channel': l1.get_channel_scid(l2), + } + l1.rpc.sendpay([routestep], inv['payment_hash'], payment_secret=inv['payment_secret'], bolt11=inv['bolt11']) + wait_for(lambda: only_one(l1.rpc.listpeers(l2.info['id'])['peers'])['connected'] is False) + + check_channel_moves(l1, expected_channel1) + check_channel_moves(l2, expected_channel2) + check_chain_moves(l1, expected_chain1) + check_chain_moves(l2, expected_chain2) + + close_info = l1.rpc.close(l2.info['id'], unilateraltimeout=1) + + # We will spend anchor to confirm this. + line = l1.daemon.wait_for_log("Creating anchor spend for local commit tx ") + anchor_spend_txid = re.search(r'Creating anchor spend for local commit tx ([0-9a-f]{64})', line).group(1) + + bitcoind.generate_block(1, wait_for_mempool=1) + sync_blockheight(bitcoind, [l1, l2]) + + # Make sure onchaind has digested it. + l1.daemon.wait_for_log('6 outputs unresolved: in 4 blocks will spend DELAYED_OUTPUT_TO_US') + + # Which outputs are anchors, and which are to us and which to them? + # Use onchaind's logs, eg: + # Tracking output 0e1cfbc2be0aada02222a163a1a413fd0b06bae8017c3626cbf8816499dadc09:0: OUR_UNILATERAL/ANCHOR_TO_THEM + line = l1.daemon.is_in_log('Tracking output.*/ANCHOR_TO_THEM') + anch_to_l2 = int(re.search(r'output [0-9a-f]{64}:([0-9]):', line).group(1)) + line = l1.daemon.is_in_log('Tracking output.*/ANCHOR_TO_US') + anch_to_l1 = int(re.search(r'output [0-9a-f]{64}:([0-9]):', line).group(1)) + line = l1.daemon.is_in_log('Tracking output.*/DELAYED_OUTPUT_TO_US') + to_l1 = int(re.search(r'output [0-9a-f]{64}:([0-9]):', line).group(1)) + line = l1.daemon.is_in_log('Tracking output.*/OUTPUT_TO_THEM') + to_l2 = int(re.search(r'output [0-9a-f]{64}:([0-9]):', line).group(1)) + line = l1.daemon.is_in_log('Tracking output.*/OUR_HTLC') + htlc = int(re.search(r'output [0-9a-f]{64}:([0-9]):', line).group(1)) + + expected_chain1 += [{'account_id': 'wallet', # Anchor spend from fundchannel change + 'blockheight': 104, + 'credit_msat': 0, + 'debit_msat': 25000000, + 'extra_tags': [], + 'output_msat': 25000000, + 'primary_tag': 'withdrawal', + 'spending_txid': anchor_spend_txid, + 'utxo': f"{fundchannel['txid']}:{fundchannel['outnum'] ^ 1}"}, + {'account_id': 'wallet', # Change from anchor spend + 'blockheight': 104, + 'credit_msat': 15579000, + 'debit_msat': 0, + 'extra_tags': [], + 'output_msat': 15579000, + 'primary_tag': 'deposit', + 'utxo': f"{anchor_spend_txid}:0"}, + {'account_id': fundchannel['channel_id'], + 'blockheight': 104, + 'credit_msat': 0, + 'debit_msat': 99970073000 - 50000000000, + 'extra_tags': [], + 'output_count': 5, + 'output_msat': 99970073000, + 'primary_tag': 'channel_close', + 'spending_txid': only_one(close_info['txids']), + 'utxo': f"{fundchannel['txid']}:{fundchannel['outnum']}"}, + {'account_id': 'external', + 'blockheight': 104, + 'credit_msat': 330000, + 'debit_msat': 0, + 'extra_tags': [], + 'originating_account': fundchannel['channel_id'], + 'output_msat': 330000, + 'primary_tag': 'anchor', + 'utxo': f"{only_one(close_info['txids'])}:{anch_to_l2}"}, + {'account_id': 'external', + 'blockheight': 104, + 'credit_msat': 330000, + 'debit_msat': 0, + 'extra_tags': [], + 'originating_account': fundchannel['channel_id'], + 'output_msat': 330000, + 'primary_tag': 'anchor', + 'utxo': f"{only_one(close_info['txids'])}:{anch_to_l1}"}, + {'account_id': 'external', + 'blockheight': 104, + 'credit_msat': 50000000000, + 'debit_msat': 0, + 'extra_tags': [], + 'originating_account': fundchannel['channel_id'], + 'output_msat': 50000000000, + 'primary_tag': 'to_them', + 'utxo': f"{only_one(close_info['txids'])}:{to_l2}"}] + expected_chain2 += [{'account_id': fundchannel['channel_id'], + 'blockheight': 104, + 'credit_msat': 0, + 'debit_msat': 50000000000, + 'extra_tags': [], + 'output_count': 5, + 'output_msat': 99970073000, + 'primary_tag': 'channel_close', + 'spending_txid': only_one(close_info['txids']), + 'utxo': f"{fundchannel['txid']}:{fundchannel['outnum']}"}, + {'account_id': 'external', + 'blockheight': 104, + 'credit_msat': 330000, + 'debit_msat': 0, + 'extra_tags': [], + 'originating_account': fundchannel['channel_id'], + 'output_msat': 330000, + 'primary_tag': 'anchor', + 'utxo': f"{only_one(close_info['txids'])}:{anch_to_l2}"}, + {'account_id': 'external', + 'blockheight': 104, + 'credit_msat': 330000, + 'debit_msat': 0, + 'extra_tags': [], + 'originating_account': fundchannel['channel_id'], + 'output_msat': 330000, + 'primary_tag': 'anchor', + 'utxo': f"{only_one(close_info['txids'])}:{anch_to_l1}"}, + {'account_id': 'external', + 'blockheight': 104, + 'credit_msat': 49864547000, + 'debit_msat': 0, + 'extra_tags': [], + 'originating_account': fundchannel['channel_id'], + 'output_msat': 49864547000, + 'primary_tag': 'to_them', + 'utxo': f"{only_one(close_info['txids'])}:{to_l1}"}, + {'account_id': 'wallet', + 'blockheight': 104, + 'credit_msat': 50000000000, + 'debit_msat': 0, + 'extra_tags': [], + 'originating_account': fundchannel['channel_id'], + 'output_msat': 50000000000, + 'primary_tag': 'deposit', + 'utxo': f"{only_one(close_info['txids'])}:{to_l2}"}] + check_channel_moves(l1, expected_channel1) + check_chain_moves(l1, expected_chain1) + check_channel_moves(l2, expected_channel2) + check_chain_moves(l2, expected_chain2) + + bitcoind.generate_block(1, wait_for_mempool=1) + line = l2.daemon.wait_for_log('Resolved THEIR_UNILATERAL/THEIR_HTLC by our proposal THEIR_HTLC_FULFILL_TO_US') + htlc_success_txid = re.search(r'by our proposal THEIR_HTLC_FULFILL_TO_US \(([0-9a-f]{64})\)', line).group(1) + + expected_chain1 += [{'account_id': 'external', + 'blockheight': 104, + 'credit_msat': 100000000, + 'debit_msat': 0, + 'extra_tags': [], + 'originating_account': fundchannel['channel_id'], + 'output_msat': 100000000, + 'payment_hash': inv['payment_hash'], + 'primary_tag': 'htlc_fulfill', + 'utxo': f"{only_one(close_info['txids'])}:{htlc}"}, + {'account_id': 'external', + 'blockheight': 105, + 'credit_msat': 0, + 'debit_msat': 0, + 'extra_tags': [], + 'originating_account': fundchannel['channel_id'], + 'output_msat': 100000000, + 'spending_txid': htlc_success_txid, + 'primary_tag': 'htlc_fulfill', + 'utxo': f"{only_one(close_info['txids'])}:{htlc}"}] + # Note: the invoice is fulfilled in the *chain* moves, not *channel*. + expected_chain2 += [{'account_id': 'wallet', + 'blockheight': 105, + 'credit_msat': 94534000, + 'debit_msat': 0, + 'extra_tags': [], + 'output_msat': 94534000, + 'primary_tag': 'deposit', + 'utxo': f"{htlc_success_txid}:0"}, + {'account_id': fundchannel['channel_id'], + 'blockheight': 104, + 'credit_msat': 100000000, + 'debit_msat': 0, + 'extra_tags': [], + 'output_msat': 100000000, + 'payment_hash': inv['payment_hash'], + 'primary_tag': 'htlc_fulfill', + 'utxo': f"{only_one(close_info['txids'])}:{htlc}"}, + {'account_id': fundchannel['channel_id'], + 'blockheight': 105, + 'credit_msat': 0, + 'debit_msat': 100000000, + 'extra_tags': [], + 'output_msat': 100000000, + 'primary_tag': 'to_wallet', + 'spending_txid': htlc_success_txid, + 'utxo': f"{only_one(close_info['txids'])}:{htlc}"}] + check_channel_moves(l1, expected_channel1) + check_chain_moves(l1, expected_chain1) + check_channel_moves(l2, expected_channel2) + check_chain_moves(l2, expected_chain2) + + bitcoind.generate_block(3) + l1.daemon.wait_for_log('waiting confirmation that we spent DELAYED_OUTPUT_TO_US') + bitcoind.generate_block(1, wait_for_mempool=1) + + line = l1.daemon.wait_for_log('Resolved OUR_UNILATERAL/DELAYED_OUTPUT_TO_US by our proposal OUR_DELAYED_RETURN_TO_WALLET') + to_l1_txid = re.search(r'by our proposal OUR_DELAYED_RETURN_TO_WALLET \(([0-9a-f]{64})\)', line).group(1) + + expected_chain1 += [{'account_id': 'wallet', + 'blockheight': 109, + 'credit_msat': 49864413000, + 'debit_msat': 0, + 'extra_tags': [], + 'output_msat': 49864413000, + 'primary_tag': 'deposit', + 'utxo': f"{to_l1_txid}:0"}, + {'account_id': fundchannel['channel_id'], + 'blockheight': 104, + 'credit_msat': 49864547000, + 'debit_msat': 0, + 'extra_tags': [], + 'output_msat': 49864547000, + 'primary_tag': 'delayed_to_us', + 'utxo': f"{only_one(close_info['txids'])}:{to_l1}"}, + {'account_id': fundchannel['channel_id'], + 'blockheight': 109, + 'credit_msat': 0, + 'debit_msat': 49864547000, + 'extra_tags': [], + 'output_msat': 49864547000, + 'primary_tag': 'to_wallet', + 'spending_txid': to_l1_txid, + 'utxo': f"{only_one(close_info['txids'])}:{to_l1}"}] + check_channel_moves(l1, expected_channel1) + check_chain_moves(l1, expected_chain1) + check_channel_moves(l2, expected_channel2) + check_chain_moves(l2, expected_chain2) + l1.daemon.wait_for_log('All outputs resolved: waiting 100 more blocks before forgetting channel') + l2.daemon.wait_for_log('All outputs resolved: waiting 100 more blocks before forgetting channel') + + # Make sure it's stable! + bitcoind.generate_block(100) + sync_blockheight(bitcoind, [l1, l2]) + time.sleep(5) + check_channel_moves(l1, expected_channel1) + check_chain_moves(l1, expected_chain1) + check_channel_moves(l2, expected_channel2) + check_chain_moves(l2, expected_chain2) + l1.daemon.wait_for_log('onchaind complete, forgetting peer') + l2.daemon.wait_for_log('onchaind complete, forgetting peer') + + # We send an HTLC, but it's not accounted in channel. + check_balances(l1, l2, fundchannel['channel_id'], 0) + + +@pytest.mark.openchannel('v1') +@pytest.mark.openchannel('v2') +@unittest.skipIf(TEST_NETWORK != 'regtest', "Amounts are for regtest.") +def test_coinmoves_unilateral_htlc_fulfilled_oneside(node_factory, bitcoind): + """l1 drops to chain with HTLC fulfilled (included in l2's output), l2 hasn't seen completion yet.""" + if EXPERIMENTAL_DUAL_FUND: + disc = ['-WIRE_COMMITMENT_SIGNED*5'] + else: + disc = ['-WIRE_COMMITMENT_SIGNED*4'] + l1, l2 = node_factory.get_nodes(2, opts=[{'disconnect': disc}, {}]) + + expected_channel1, expected_channel2, expected_chain1, expected_chain2, fundchannel = setup_channel(bitcoind, l1, l2) + + inv = l2.rpc.invoice('any', 'test_coinmoves_unilateral_htlc_fulfilled_oneside', 'test_coinmoves_unilateral_htlc_fulfilled_oneside') + routestep = { + # We will spend anchor to make this confirm. + 'amount_msat': 100000000, + 'id': l2.info['id'], + 'delay': 10, + 'channel': l1.get_channel_scid(l2), + } + l1.rpc.sendpay([routestep], inv['payment_hash'], payment_secret=inv['payment_secret'], bolt11=inv['bolt11']) + wait_for(lambda: only_one(l1.rpc.listpeers(l2.info['id'])['peers'])['connected'] is False) + + check_channel_moves(l1, expected_channel1) + check_channel_moves(l2, expected_channel2) + check_chain_moves(l1, expected_chain1) + check_chain_moves(l2, expected_chain2) + + close_info = l1.rpc.close(l2.info['id'], unilateraltimeout=1) + + # We will spend anchor to confirm this. + line = l1.daemon.wait_for_log("Creating anchor spend for local commit tx ") + anchor_spend_txid = re.search(r'Creating anchor spend for local commit tx ([0-9a-f]{64})', line).group(1) + + bitcoind.generate_block(1, wait_for_mempool=2) + sync_blockheight(bitcoind, [l1, l2]) + + # Make sure onchaind has digested it. + l2.daemon.wait_for_log('All outputs resolved: waiting 100 more blocks before forgetting channel') + l1.daemon.wait_for_log('5 outputs unresolved: in 5 blocks will spend DELAYED_OUTPUT_TO_US') + + # Which outputs are anchors, and which are to us and which to them? + # Use onchaind's logs, eg: + # Tracking output 0e1cfbc2be0aada02222a163a1a413fd0b06bae8017c3626cbf8816499dadc09:0: OUR_UNILATERAL/ANCHOR_TO_THEM + line = l1.daemon.is_in_log('Tracking output.*/ANCHOR_TO_THEM') + anch_to_l2 = int(re.search(r'output [0-9a-f]{64}:([0-9]):', line).group(1)) + line = l1.daemon.is_in_log('Tracking output.*/ANCHOR_TO_US') + anch_to_l1 = int(re.search(r'output [0-9a-f]{64}:([0-9]):', line).group(1)) + line = l1.daemon.is_in_log('Tracking output.*/DELAYED_OUTPUT_TO_US') + to_l1 = int(re.search(r'output [0-9a-f]{64}:([0-9]):', line).group(1)) + line = l1.daemon.is_in_log('Tracking output.*/OUTPUT_TO_THEM') + to_l2 = int(re.search(r'output [0-9a-f]{64}:([0-9]):', line).group(1)) + + expected_chain1 += [{'account_id': 'wallet', # Anchor spend from fundchannel change + 'blockheight': 104, + 'credit_msat': 0, + 'debit_msat': 25000000, + 'extra_tags': [], + 'output_msat': 25000000, + 'primary_tag': 'withdrawal', + 'spending_txid': anchor_spend_txid, + 'utxo': f"{fundchannel['txid']}:{fundchannel['outnum'] ^ 1}"}, + {'account_id': 'wallet', # Change from anchor spend + 'blockheight': 104, + 'credit_msat': 16186000, + 'debit_msat': 0, + 'extra_tags': [], + 'output_msat': 16186000, + 'primary_tag': 'deposit', + 'utxo': f"{anchor_spend_txid}:0"}, + {'account_id': fundchannel['channel_id'], + 'blockheight': 104, + 'credit_msat': 0, + 'debit_msat': 99970073000 - 50000000000, + 'extra_tags': [], + 'output_count': 4, + 'output_msat': 99970073000, + 'primary_tag': 'channel_close', + 'spending_txid': only_one(close_info['txids']), + 'utxo': f"{fundchannel['txid']}:{fundchannel['outnum']}"}, + {'account_id': 'external', + 'blockheight': 104, + 'credit_msat': 330000, + 'debit_msat': 0, + 'extra_tags': [], + 'originating_account': fundchannel['channel_id'], + 'output_msat': 330000, + 'primary_tag': 'anchor', + 'utxo': f"{only_one(close_info['txids'])}:{anch_to_l2}"}, + {'account_id': 'external', + 'blockheight': 104, + 'credit_msat': 330000, + 'debit_msat': 0, + 'extra_tags': [], + 'originating_account': fundchannel['channel_id'], + 'output_msat': 330000, + 'primary_tag': 'anchor', + 'utxo': f"{only_one(close_info['txids'])}:{anch_to_l1}"}, + {'account_id': 'external', + 'blockheight': 104, + 'credit_msat': 50100000000, + 'debit_msat': 0, + 'extra_tags': [], + 'originating_account': fundchannel['channel_id'], + 'output_msat': 50100000000, + 'primary_tag': 'to_them', + 'utxo': f"{only_one(close_info['txids'])}:{to_l2}"}] + expected_chain2 += [{'account_id': fundchannel['channel_id'], + 'blockheight': 104, + 'credit_msat': 0, + 'debit_msat': 50000000000, + 'extra_tags': [], + 'output_count': 4, + 'output_msat': 99970073000, + 'primary_tag': 'channel_close', + 'spending_txid': only_one(close_info['txids']), + 'utxo': f"{fundchannel['txid']}:{fundchannel['outnum']}"}, + {'account_id': 'external', + 'blockheight': 104, + 'credit_msat': 330000, + 'debit_msat': 0, + 'extra_tags': [], + 'originating_account': fundchannel['channel_id'], + 'output_msat': 330000, + 'primary_tag': 'anchor', + 'utxo': f"{only_one(close_info['txids'])}:{anch_to_l2}"}, + {'account_id': 'external', + 'blockheight': 104, + 'credit_msat': 330000, + 'debit_msat': 0, + 'extra_tags': [], + 'originating_account': fundchannel['channel_id'], + 'output_msat': 330000, + 'primary_tag': 'anchor', + 'utxo': f"{only_one(close_info['txids'])}:{anch_to_l1}"}, + {'account_id': 'external', + 'blockheight': 104, + 'credit_msat': 49865193000, + 'debit_msat': 0, + 'extra_tags': [], + 'originating_account': fundchannel['channel_id'], + 'output_msat': 49865193000, + 'primary_tag': 'to_them', + 'utxo': f"{only_one(close_info['txids'])}:{to_l1}"}, + {'account_id': 'wallet', + 'blockheight': 104, + 'credit_msat': 50100000000, + 'debit_msat': 0, + 'extra_tags': [], + 'originating_account': fundchannel['channel_id'], + 'output_msat': 50100000000, + 'primary_tag': 'deposit', + 'utxo': f"{only_one(close_info['txids'])}:{to_l2}"}] + check_channel_moves(l1, expected_channel1) + check_chain_moves(l1, expected_chain1) + check_channel_moves(l2, expected_channel2) + check_chain_moves(l2, expected_chain2) + + bitcoind.generate_block(4) + l1.daemon.wait_for_log('waiting confirmation that we spent DELAYED_OUTPUT_TO_US') + check_channel_moves(l1, expected_channel1) + check_chain_moves(l1, expected_chain1) + check_channel_moves(l2, expected_channel2) + check_chain_moves(l2, expected_chain2) + + bitcoind.generate_block(1, wait_for_mempool=1) + line = l1.daemon.wait_for_log('Resolved OUR_UNILATERAL/DELAYED_OUTPUT_TO_US by our proposal OUR_DELAYED_RETURN_TO_WALLET') + to_l1_txid = re.search(r'by our proposal OUR_DELAYED_RETURN_TO_WALLET \(([0-9a-f]{64})\)', line).group(1) + + expected_chain1 += [{'account_id': 'wallet', + 'blockheight': 109, + 'credit_msat': 49865059000, + 'debit_msat': 0, + 'extra_tags': [], + 'output_msat': 49865059000, + 'primary_tag': 'deposit', + 'utxo': f"{to_l1_txid}:0"}, + {'account_id': fundchannel['channel_id'], + 'blockheight': 104, + 'credit_msat': 49865193000, + 'debit_msat': 0, + 'extra_tags': [], + 'output_msat': 49865193000, + 'primary_tag': 'delayed_to_us', + 'utxo': f"{only_one(close_info['txids'])}:{to_l1}"}, + {'account_id': fundchannel['channel_id'], + 'blockheight': 109, + 'credit_msat': 0, + 'debit_msat': 49865193000, + 'extra_tags': [], + 'output_msat': 49865193000, + 'primary_tag': 'to_wallet', + 'spending_txid': to_l1_txid, + 'utxo': f"{only_one(close_info['txids'])}:{to_l1}"}] + check_channel_moves(l1, expected_channel1) + check_chain_moves(l1, expected_chain1) + check_channel_moves(l2, expected_channel2) + check_chain_moves(l2, expected_chain2) + l1.daemon.wait_for_log('All outputs resolved: waiting 100 more blocks before forgetting channel') + + # Make sure it's stable! + bitcoind.generate_block(100) + sync_blockheight(bitcoind, [l1, l2]) + time.sleep(5) + check_channel_moves(l1, expected_channel1) + check_chain_moves(l1, expected_chain1) + check_channel_moves(l2, expected_channel2) + check_chain_moves(l2, expected_chain2) + l1.daemon.wait_for_log('onchaind complete, forgetting peer') + l2.daemon.wait_for_log('onchaind complete, forgetting peer') + + # We send no in-channel HTLCs + check_balances(l1, l2, fundchannel['channel_id'], 0) + + +@pytest.mark.openchannel('v1') +@pytest.mark.openchannel('v2') +@unittest.skipIf(TEST_NETWORK != 'regtest', "Amounts are for regtest.") +def test_coinmoves_unilateral_htlc_penalty(node_factory, bitcoind): + """l2 drops to old commitment to chain with HTLC.""" + if EXPERIMENTAL_DUAL_FUND: + disc = ['-WIRE_COMMITMENT_SIGNED*5'] + else: + disc = ['-WIRE_COMMITMENT_SIGNED*4'] + l1, l2 = node_factory.get_nodes(2, opts=[{'may_reconnect': True, + 'dev-no-reconnect': None}, + {'disconnect': disc, + 'may_reconnect': True, + 'dev-no-reconnect': None}]) + + expected_channel1, expected_channel2, expected_chain1, expected_chain2, fundchannel = setup_channel(bitcoind, l1, l2) + + inv = l2.rpc.invoice('any', 'test_coinmoves_unilateral_htlc_fulfilled_oneside', 'test_coinmoves_unilateral_htlc_fulfilled_oneside') + routestep = { + # We will spend anchor to make this confirm. + 'amount_msat': 100000000, + 'id': l2.info['id'], + 'delay': 10, + 'channel': l1.get_channel_scid(l2), + } + l1.rpc.sendpay([routestep], inv['payment_hash'], payment_secret=inv['payment_secret'], bolt11=inv['bolt11']) + wait_for(lambda: only_one(l1.rpc.listpeers(l2.info['id'])['peers'])['connected'] is False) + + check_channel_moves(l1, expected_channel1) + check_channel_moves(l2, expected_channel2) + check_chain_moves(l1, expected_chain1) + check_chain_moves(l2, expected_chain2) + cheattx = l1.rpc.dev_sign_last_tx(l2.info['id'])['tx'] + cheattxid = bitcoind.rpc.decoderawtransaction(cheattx)['txid'] + + # Reconnect, HTLC will settle. + l2.rpc.connect(l1.info['id'], 'localhost', l1.port) + wait_for(lambda: only_one(l1.rpc.listpeerchannels()['channels'])['htlcs'] == []) + + expected_channel1 += [{'account_id': fundchannel['channel_id'], + 'credit_msat': 0, + 'debit_msat': 100000000, + 'fees_msat': 0, + 'group_id': 1, + 'part_id': 0, + 'payment_hash': inv['payment_hash'], + 'primary_tag': 'invoice'}] + expected_channel2 += [{'account_id': fundchannel['channel_id'], + 'credit_msat': 100000000, + 'debit_msat': 0, + 'fees_msat': 0, + 'payment_hash': inv['payment_hash'], + 'primary_tag': 'invoice'}] + check_channel_moves(l1, expected_channel1) + check_channel_moves(l2, expected_channel2) + check_chain_moves(l1, expected_chain1) + check_chain_moves(l2, expected_chain2) + + # Don't interfere, l1, we're going to *cheat* + l1.stop() + bitcoind.rpc.sendrawtransaction(cheattx) + bitcoind.generate_block(1) + + # We spend all outputs at once. + l2.daemon.wait_for_log("6 outputs unresolved: waiting confirmation") + + # Which outputs are anchors, and which are to us and which to them? + # Use onchaind's logs, eg: + # Tracking output 0e1cfbc2be0aada02222a163a1a413fd0b06bae8017c3626cbf8816499dadc09:0: OUR_UNILATERAL/ANCHOR_TO_THEM + line = l2.daemon.is_in_log('Tracking output.*/ANCHOR_TO_THEM') + anch_to_l2 = int(re.search(r'output [0-9a-f]{64}:([0-9]):', line).group(1)) + line = l2.daemon.is_in_log('Tracking output.*/ANCHOR_TO_US') + anch_to_l1 = int(re.search(r'output [0-9a-f]{64}:([0-9]):', line).group(1)) + line = l2.daemon.is_in_log('Tracking output.*/OUTPUT_TO_US') + to_l2 = int(re.search(r'output [0-9a-f]{64}:([0-9]):', line).group(1)) + line = l2.daemon.is_in_log('Tracking output.*/DELAYED_CHEAT_OUTPUT_TO_THEM') + to_l1 = int(re.search(r'output [0-9a-f]{64}:([0-9]):', line).group(1)) + line = l2.daemon.is_in_log('Tracking output.*/THEIR_HTLC') + htlc = int(re.search(r'output [0-9a-f]{64}:([0-9]):', line).group(1)) + + expected_chain2 += [{'account_id': fundchannel['channel_id'], + 'blockheight': 104, + 'credit_msat': 0, + 'debit_msat': 50100000000, + 'extra_tags': [], + 'output_count': 5, + 'output_msat': 99970073000, + 'primary_tag': 'channel_close', + 'spending_txid': cheattxid, + 'utxo': f"{fundchannel['txid']}:{fundchannel['outnum']}"}, + {'account_id': 'external', + 'blockheight': 104, + 'credit_msat': 330000, + 'debit_msat': 0, + 'extra_tags': [], + 'originating_account': fundchannel['channel_id'], + 'output_msat': 330000, + 'primary_tag': 'anchor', + 'utxo': f"{cheattxid}:{anch_to_l1}"}, + {'account_id': 'external', + 'blockheight': 104, + 'credit_msat': 330000, + 'debit_msat': 0, + 'extra_tags': [], + 'originating_account': fundchannel['channel_id'], + 'output_msat': 330000, + 'primary_tag': 'anchor', + 'utxo': f"{cheattxid}:{anch_to_l2}"}, + {'account_id': 'wallet', + 'blockheight': 104, + 'credit_msat': 50000000000, + 'debit_msat': 0, + 'extra_tags': [], + 'originating_account': fundchannel['channel_id'], + 'output_msat': 50000000000, + 'primary_tag': 'deposit', + 'utxo': f"{cheattxid}:{to_l2}"}] + check_channel_moves(l2, expected_channel2) + check_chain_moves(l2, expected_chain2) + + # Generates all the penalties + bitcoind.generate_block(1, wait_for_mempool=2) + line = l2.daemon.wait_for_log('Resolved THEIR_REVOKED_UNILATERAL/DELAYED_CHEAT_OUTPUT_TO_THEM by our proposal OUR_PENALTY_TX') + to_l1_penalty = re.search(r'by our proposal OUR_PENALTY_TX \(([0-9a-f]{64})\)', line).group(1) + line = l2.daemon.wait_for_log('Resolved THEIR_REVOKED_UNILATERAL/THEIR_HTLC by our proposal OUR_PENALTY_TX') + htlc_penalty = re.search(r'by our proposal OUR_PENALTY_TX \(([0-9a-f]{64})\)', line).group(1) + + expected_chain2 += [{'account_id': 'wallet', + 'blockheight': 105, + 'credit_msat': 49858187000, + 'debit_msat': 0, + 'extra_tags': [], + 'output_msat': 49858187000, + 'primary_tag': 'deposit', + 'utxo': f"{to_l1_penalty}:0"}, + {'account_id': 'wallet', + 'blockheight': 105, + 'credit_msat': 92908000, + 'debit_msat': 0, + 'extra_tags': [], + 'output_msat': 92908000, + 'primary_tag': 'deposit', + 'utxo': f"{htlc_penalty}:0"}, + {'account_id': fundchannel['channel_id'], + 'blockheight': 104, + 'credit_msat': 49864547000, + 'debit_msat': 0, + 'extra_tags': [], + 'output_msat': 49864547000, + 'primary_tag': 'penalty', + 'utxo': f"{cheattxid}:{to_l1}"}, + {'account_id': fundchannel['channel_id'], + 'blockheight': 105, + 'credit_msat': 0, + 'debit_msat': 49864547000, + 'extra_tags': [], + 'output_msat': 49864547000, + 'primary_tag': 'to_wallet', + 'spending_txid': to_l1_penalty, + 'utxo': f"{cheattxid}:{to_l1}"}, + {'account_id': fundchannel['channel_id'], + 'blockheight': 104, + 'credit_msat': 100000000, + 'debit_msat': 0, + 'extra_tags': [], + 'output_msat': 100000000, + 'primary_tag': 'penalty', + 'utxo': f"{cheattxid}:{htlc}"}, + {'account_id': fundchannel['channel_id'], + 'blockheight': 105, + 'credit_msat': 0, + 'debit_msat': 100000000, + 'extra_tags': [], + 'output_msat': 100000000, + 'primary_tag': 'to_wallet', + 'spending_txid': htlc_penalty, + 'utxo': f"{cheattxid}:{htlc}"}] + expected_channel2 += [{'account_id': fundchannel['channel_id'], + 'credit_msat': 49864547000, + 'debit_msat': 0, + 'fees_msat': 0, + 'primary_tag': 'penalty_adj'}, + {'account_id': fundchannel['channel_id'], + 'credit_msat': 0, + 'debit_msat': 49864547000, + 'fees_msat': 0, + 'primary_tag': 'penalty_adj'}, + {'account_id': fundchannel['channel_id'], + 'credit_msat': 100000000, + 'debit_msat': 0, + 'fees_msat': 0, + 'primary_tag': 'penalty_adj'}, + {'account_id': fundchannel['channel_id'], + 'credit_msat': 0, + 'debit_msat': 100000000, + 'fees_msat': 0, + 'primary_tag': 'penalty_adj'}] + check_channel_moves(l2, expected_channel2) + check_chain_moves(l2, expected_chain2) + + l2.daemon.wait_for_log('All outputs resolved: waiting 100 more blocks before forgetting channel') + + # Make sure it's stable! + bitcoind.generate_block(100) + sync_blockheight(bitcoind, [l2]) + time.sleep(5) + check_channel_moves(l2, expected_channel2) + check_chain_moves(l2, expected_chain2) + l2.daemon.wait_for_log('onchaind complete, forgetting peer') + + channel2 = account_balances(l2.rpc.listchannelmoves()['channelmoves']) + chain2 = account_balances(l2.rpc.listchainmoves()['chainmoves']) + + # Channel balances should reflect sats transferred + # FIXME: Should probably include penalty. + assert sum(channel2[fundchannel['channel_id']]) == 50000000000 + 100000000 + + # Wallet balances should reflect reality + l2_wallet = sum([o['amount_msat'] for o in l2.rpc.listfunds()['outputs']]) + if sum(chain2['wallet']) != l2_wallet: + print(f"sum({chain2['wallet']}) != {l2_wallet}") + assert False + + # FIXME: + # MVT_PENALIZED, + # MVT_STOLEN, + # MVT_TO_MINER, + # MVT_LEASE_FEE, + # MVT_CHANNEL_PROPOSED, + # Extra tags + # MVT_SPLICE, + # MVT_LEASED, + # MVT_STEALABLE, + + +@pytest.mark.openchannel('v1') +@pytest.mark.openchannel('v2') +def test_wait(node_factory, bitcoind, executor): + l1, l2 = node_factory.get_nodes(2) + + fut = executor.submit(l1.rpc.wait, subsystem='chainmoves', indexname='created', nextvalue=1) + + addr = l1.rpc.newaddr()['bech32'] + bitcoind.rpc.sendtoaddress(addr, 200000000 / 10**8) + bitcoind.generate_block(1, wait_for_mempool=1) + + out = fut.result(TIMEOUT) + assert out == {'subsystem': 'chainmoves', + 'created': 1, + 'chainmoves': {'account': 'wallet', + 'credit_msat': 200000000000, + 'debit_msat': 0}} + + l1.rpc.connect(l2.info['id'], 'localhost', l2.port) + fund = l1.rpc.fundchannel(l2.info['id'], 10000000) + bitcoind.generate_block(1, wait_for_mempool=fund['txid']) + wait_for(lambda: all([c['state'] == 'CHANNELD_NORMAL' for c in l1.rpc.listpeerchannels(l2.info['id'])['channels']])) + + fut = executor.submit(l1.rpc.wait, subsystem='channelmoves', indexname='created', nextvalue=1) + inv = l2.rpc.invoice('any', 'test_wait', 'test_wait') + l1.rpc.xpay(inv['bolt11'], '1000000sat') + + out = fut.result(TIMEOUT) + assert out == {'subsystem': 'channelmoves', + 'created': 1, + 'channelmoves': {'account': fund['channel_id'], + 'debit_msat': 1000000000, + 'credit_msat': 0}} + + +@unittest.skipIf(os.getenv('TEST_DB_PROVIDER', 'sqlite3') != 'sqlite3', "uses snapshots") +@unittest.skipIf(TEST_NETWORK != 'regtest', "Snapshots are bitcoin regtest.") +def test_migration(node_factory, bitcoind): + """These nodes import coinmoves from the old bookkeeper account.db""" + bitcoind.generate_block(1) + l1 = node_factory.get_node(dbfile="l1-before-moves-in-db.sqlite3.xz", + bkpr_dbfile="l1-bkpr-accounts.sqlite3.xz", + options={'database-upgrade': True}) + l2 = node_factory.get_node(dbfile="l2-before-moves-in-db.sqlite3.xz", + bkpr_dbfile="l2-bkpr-accounts.sqlite3.xz", + options={'database-upgrade': True}) + chan = only_one(l1.rpc.listpeerchannels()['channels']) + payment = only_one(l1.rpc.listsendpays()['payments']) + + expected_channel1 = [{'account_id': chan['channel_id'], + 'created_index': 1, + 'credit_msat': 0, + 'debit_msat': 12345678, + 'fees_msat': 0, + 'payment_hash': payment['payment_hash'], + 'primary_tag': 'invoice'}] + expected_channel2 = [{'account_id': chan['channel_id'], + 'created_index': 1, + 'credit_msat': 12345678, + 'debit_msat': 0, + 'fees_msat': 0, + 'payment_hash': payment['payment_hash'], + 'primary_tag': 'invoice'}] + expected_chain1 = [{'account_id': 'wallet', + 'blockheight': 102, + 'created_index': 1, + 'credit_msat': 2000000000, + 'debit_msat': 0, + 'extra_tags': [], + 'output_msat': 2000000000, + 'primary_tag': 'deposit', + 'utxo': '63c59b312976320528552c258ae51563498dfd042b95bb0c842696614d59bb89:1'}, + {'account_id': 'wallet', + 'blockheight': 103, + 'created_index': 2, + 'credit_msat': 0, + 'debit_msat': 2000000000, + 'extra_tags': [], + 'output_msat': 2000000000, + 'primary_tag': 'withdrawal', + 'spending_txid': chan['funding_txid'], + 'utxo': '63c59b312976320528552c258ae51563498dfd042b95bb0c842696614d59bb89:1'}, + {'account_id': 'wallet', + 'blockheight': 103, + 'created_index': 3, + 'credit_msat': 995073000, + 'debit_msat': 0, + 'extra_tags': [], + 'output_msat': 995073000, + 'primary_tag': 'deposit', + 'utxo': f"{chan['funding_txid']}:{chan['funding_outnum'] ^ 1}"}, + {'account_id': chan['channel_id'], + 'blockheight': 103, + 'created_index': 4, + 'credit_msat': 1000000000, + 'debit_msat': 0, + 'extra_tags': ['opener'], + 'output_msat': 1000000000, + 'peer_id': l2.info['id'], + 'primary_tag': 'channel_open', + 'utxo': f"{chan['funding_txid']}:{chan['funding_outnum']}"}] + expected_chain2 = [{'account_id': chan['channel_id'], + 'blockheight': 103, + 'created_index': 1, + 'credit_msat': 0, + 'debit_msat': 0, + 'extra_tags': [], + 'output_msat': 1000000000, + 'peer_id': l1.info['id'], + 'primary_tag': 'channel_open', + 'utxo': f"{chan['funding_txid']}:{chan['funding_outnum']}"}] + check_channel_moves(l1, expected_channel1) + check_channel_moves(l2, expected_channel2) + check_chain_moves(l1, expected_chain1) + check_chain_moves(l2, expected_chain2) + + +@unittest.skipIf(os.getenv('TEST_DB_PROVIDER', 'sqlite3') != 'sqlite3', "uses snapshots") +@unittest.skipIf(TEST_NETWORK != 'regtest', "Snapshots are for regtest.") +def test_migration_no_bkpr(node_factory, bitcoind): + """These nodes need to invent coinmoves to make the balances work""" + bitcoind.generate_block(1) + l1 = node_factory.get_node(dbfile="l1-before-moves-in-db.sqlite3.xz", + options={'database-upgrade': True}) + l2 = node_factory.get_node(dbfile="l2-before-moves-in-db.sqlite3.xz", + options={'database-upgrade': True}) + + chan = only_one(l1.rpc.listpeerchannels()['channels']) + + expected_channel1 = [{'account_id': chan['channel_id'], + 'created_index': 1, + 'credit_msat': 0, + 'debit_msat': 12345678, + 'fees_msat': 0, + 'primary_tag': 'journal', + }] + expected_channel2 = [{'account_id': chan['channel_id'], + 'created_index': 1, + 'credit_msat': 12345678, + 'debit_msat': 0, + 'fees_msat': 0, + 'primary_tag': 'journal', + }] + expected_chain1 = [{'account_id': 'wallet', + 'blockheight': 103, + 'created_index': 1, + 'credit_msat': 995073000, + 'debit_msat': 0, + 'extra_tags': [], + 'output_msat': 995073000, + 'primary_tag': 'deposit', + 'utxo': f"{chan['funding_txid']}:{chan['funding_outnum'] ^ 1}"}, + {'account_id': chan['channel_id'], + 'blockheight': 103, + 'created_index': 2, + 'credit_msat': 1000000000, + 'debit_msat': 0, + 'extra_tags': ['opener'], + 'output_msat': 1000000000, + 'peer_id': l2.info['id'], + 'primary_tag': 'channel_open', + 'utxo': f"{chan['funding_txid']}:{chan['funding_outnum']}"}] + expected_chain2 = [{'account_id': chan['channel_id'], + 'blockheight': 103, + 'created_index': 1, + 'credit_msat': 0, + 'debit_msat': 0, + 'extra_tags': [], + 'output_msat': 1000000000, + 'peer_id': l1.info['id'], + 'primary_tag': 'channel_open', + 'utxo': f"{chan['funding_txid']}:{chan['funding_outnum']}"}] + + check_channel_moves(l1, expected_channel1) + check_channel_moves(l2, expected_channel2) + check_chain_moves(l1, expected_chain1) + check_chain_moves(l2, expected_chain2) diff --git a/tests/test_misc.py b/tests/test_misc.py index 8a4509d2fc05..5832787aff20 100644 --- a/tests/test_misc.py +++ b/tests/test_misc.py @@ -3823,8 +3823,8 @@ def test_datastore_escapeing(node_factory): def test_datastore(node_factory): - # Suppress xpay, which makes a layer - l1 = node_factory.get_node(options={"disable-plugin": "cln-xpay"}) + # Suppress xpay and bookkeeper which use the datastore + l1 = node_factory.get_node(options={"disable-plugin": ["cln-xpay", "bookkeeper"]}) # Starts empty assert l1.rpc.listdatastore() == {'datastore': []} @@ -3938,8 +3938,8 @@ def test_datastore(node_factory): def test_datastore_keylist(node_factory): - # Suppress xpay, which makes a layer - l1 = node_factory.get_node(options={"disable-plugin": "cln-xpay"}) + # Suppress xpay and bookkeeper which use the datastore + l1 = node_factory.get_node(options={"disable-plugin": ["cln-xpay", "bookkeeper"]}) # Starts empty assert l1.rpc.listdatastore() == {'datastore': []} @@ -4001,7 +4001,8 @@ def test_datastore_keylist(node_factory): def test_datastoreusage(node_factory): - l1: LightningNode = node_factory.get_node(options={"disable-plugin": "cln-xpay"}) + # Suppress xpay and bookkeeper which use the datastore + l1: LightningNode = node_factory.get_node(options={"disable-plugin": ["cln-xpay", "bookkeeper"]}) assert l1.rpc.datastoreusage() == {'datastoreusage': {'key': '[]', 'total_bytes': 0}} data = 'somedatatostoreinthedatastore' # len 29 @@ -4951,6 +4952,8 @@ def test_tracing(node_factory): elif cmd == 'span_resume': assert spanid in traces suspended.remove(spanid) + elif cmd == 'destroying': + pass else: assert False, "Unknown trace line" diff --git a/tests/test_pay.py b/tests/test_pay.py index 5e26b7e4cc41..78ae2343dc59 100644 --- a/tests/test_pay.py +++ b/tests/test_pay.py @@ -1235,9 +1235,9 @@ def test_forward(node_factory, bitcoind): route = copy.deepcopy(baseroute) l1.rpc.sendpay(route, rhash, payment_secret=inv['payment_secret']) l1.rpc.waitsendpay(rhash) + wait_for(lambda: only_one(l1.rpc.listpeerchannels()['channels'])['htlcs'] == []) # Check that invoice payment and fee are tracked appropriately - l1.daemon.wait_for_log('coin_move .* [(]invoice[)]') l1.rpc.bkpr_dumpincomecsv('koinly', 'koinly.csv') koinly_path = os.path.join(l1.daemon.lightning_dir, TEST_NETWORK, 'koinly.csv') @@ -1441,9 +1441,8 @@ def _income_tagset(node, tagset): # the balance on l3 should equal the invoice accts = l3.rpc.bkpr_listbalances()['accounts'] assert len(accts) == 2 - wallet = accts[0] - chan_acct = accts[1] - assert wallet['account'] == 'wallet' + wallet = only_one([a for a in accts if a['account'] == 'wallet']) + chan_acct = only_one([a for a in accts if a['account'] != 'wallet']) # We no longer make a zero balance entry for the wallet at start assert wallet['balances'] == [] assert incomes[0]['tag'] == 'invoice' diff --git a/tests/test_plugin.py b/tests/test_plugin.py index 2ac9da76b144..b01005479322 100644 --- a/tests/test_plugin.py +++ b/tests/test_plugin.py @@ -1635,6 +1635,7 @@ def test_libplugin(node_factory): # Test commands assert l1.rpc.call("helloworld") == {"hello": "NOT FOUND"} + l1.daemon.wait_for_log(r'listpeers gave 3 tokens: {"peers":\[\]}') l1.daemon.wait_for_log("get_ds_bin_done: 00010203") l1.daemon.wait_for_log("BROKEN.* Datastore gave nonstring result.*00010203") assert l1.rpc.call("helloworld", {"name": "test"}) == {"hello": "test"} @@ -3756,6 +3757,65 @@ def test_sql(node_factory, bitcoind): 'type': 'msat'}, {'name': 'scriptPubKey', 'type': 'hex'}]}, + 'chainmoves': { + 'indices': [['account_id']], + 'columns': [{'name': 'created_index', + 'type': 'u64'}, + {'name': 'account_id', + 'type': 'string'}, + {'name': 'credit_msat', + 'type': 'msat'}, + {'name': 'debit_msat', + 'type': 'msat'}, + {'name': 'timestamp', + 'type': 'u64'}, + {'name': 'primary_tag', + 'type': 'string'}, + {'name': 'peer_id', + 'type': 'pubkey'}, + {'name': 'originating_account', + 'type': 'string'}, + {'name': 'spending_txid', + 'type': 'txid'}, + {'name': 'utxo', + 'type': 'outpoint'}, + {'name': 'payment_hash', + 'type': 'hash'}, + {'name': 'output_msat', + 'type': 'msat'}, + {'name': 'output_count', + 'type': 'u32'}, + {'name': 'blockheight', + 'type': 'u32'}]}, + 'chainmoves_extra_tags': { + 'columns': [{'name': 'row', + 'type': 'u64'}, + {'name': 'arrindex', + 'type': 'u64'}, + {'name': 'extra_tags', + 'type': 'string'}]}, + 'channelmoves': { + 'indices': [['account_id']], + 'columns': [{'name': 'created_index', + 'type': 'u64'}, + {'name': 'account_id', + 'type': 'string'}, + {'name': 'credit_msat', + 'type': 'msat'}, + {'name': 'debit_msat', + 'type': 'msat'}, + {'name': 'timestamp', + 'type': 'u64'}, + {'name': 'primary_tag', + 'type': 'string'}, + {'name': 'payment_hash', + 'type': 'hash'}, + {'name': 'part_id', + 'type': 'u64'}, + {'name': 'group_id', + 'type': 'u64'}, + {'name': 'fees_msat', + 'type': 'msat'}]}, 'bkpr_accountevents': { 'columns': [{'name': 'account', 'type': 'string'}, @@ -3822,18 +3882,23 @@ def test_sql(node_factory, bitcoind): 'hex': 'BLOB', 'hash': 'BLOB', 'txid': 'BLOB', + 'outpoint': 'TEXT', 'pubkey': 'BLOB', 'secret': 'BLOB', 'number': 'REAL', 'short_channel_id': 'TEXT'} - # Check schemas match (each one has rowid at start) - rowidcol = {'name': 'rowid', 'type': 'u64'} + # Check schemas match for table, schema in expected_schemas.items(): res = only_one(l2.rpc.listsqlschemas(table)['schemas']) assert res['tablename'] == table assert res.get('indices') == schema.get('indices') - sqlcolumns = [{'name': c['name'], 'type': sqltypemap[c['type']]} for c in [rowidcol] + schema['columns']] + # Those without a created_index get an *explicit* rowid; + if any([c['name'] == 'created_index' for c in schema['columns']]): + prefix = [] + else: + prefix = [{'name': 'rowid', 'type': 'u64'}] + sqlcolumns = [{'name': c['name'], 'type': sqltypemap[c['type']]} for c in prefix + schema['columns']] assert res['columns'] == sqlcolumns # Make sure we didn't miss any @@ -3871,11 +3936,17 @@ def test_sql(node_factory, bitcoind): for table, schema in expected_schemas.items(): ret = l2.rpc.sql("SELECT * FROM {};".format(table)) - assert len(ret['rows'][0]) == 1 + len(schema['columns']) + # If you have created_index, we don't create an explicit rowid. + has_rowid = not any([c['name'] == 'created_index' for c in schema['columns']]) - # First column is always rowid! - for row in ret['rows']: - assert row[0] > 0 + if has_rowid: + assert len(ret['rows'][0]) == 1 + len(schema['columns']) + + # First column is always rowid! + for row in ret['rows']: + assert row[0] > 0 + else: + assert len(ret['rows'][0]) == len(schema['columns']) for col in schema['columns']: # We will get a complaint for trying to access deprecated cols by name: @@ -3902,6 +3973,10 @@ def test_sql(node_factory, bitcoind): val += "" elif col['type'] == "short_channel_id": assert len(val.split('x')) == 3 + elif col['type'] == "outpoint": + txid, vout = val.split(':') + assert len(bytes.fromhex(txid)) == 32 + int(vout) else: assert False @@ -4184,6 +4259,19 @@ def test_sql_crash(node_factory, bitcoind): l1.rpc.sql(f"SELECT * FROM peerchannels;") +def test_sql_parallel(node_factory, executor): + """Parallel refreshes of tables causes SQL errors: + Error executing INSERT INTO chainmoves VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?,?); on row 0: UNIQUE constraint failed: chainmoves.created_index + """ + l1, l2 = node_factory.line_graph(2) + + futs = [] + for _ in range(5): + futs.append(executor.submit(l1.rpc.sql, "SELECT * FROM chainmoves")) + for f in futs: + f.result(TIMEOUT) + + def test_listchannels_broken_message(node_factory): """This gave a bogus BROKEN message with deprecated-apis enabled""" l1 = node_factory.get_node(options={'allow-deprecated-apis': True}) diff --git a/tests/utils.py b/tests/utils.py index ad4ff83324e5..fc3f18b18da1 100644 --- a/tests/utils.py +++ b/tests/utils.py @@ -140,6 +140,7 @@ def check_coin_moves(n, account_id, expected_moves, chainparams): # Stash moves for errors, if needed _acct_moves = acct_moves for mv in acct_moves: + # Generate tags as single array, which is how bookkeeper presents it mv['tags'] = [mv['primary_tag']] + mv['extra_tags'] print("{{'type': '{}', 'credit_msat': {}, 'debit_msat': {}, 'tags': '{}' , ['fees_msat'?: '{}']}}," .format(mv['type'], diff --git a/wallet/Makefile b/wallet/Makefile index c3707dde20c5..b79ce3c651be 100644 --- a/wallet/Makefile +++ b/wallet/Makefile @@ -1,6 +1,7 @@ #! /usr/bin/make WALLET_LIB_SRC := \ + wallet/account_migration.c \ wallet/db.c \ wallet/invoices.c \ wallet/psbt_fixup.c \ @@ -30,6 +31,7 @@ ALL_C_HEADERS += $(WALLET_HDRS) # The following files contain SQL-annotated statements that we need to extact WALLET_SQL_FILES := \ $(DB_SQL_FILES) \ + wallet/account_migration.c \ wallet/db.c \ wallet/invoices.c \ wallet/wallet.c \ diff --git a/wallet/account_migration.c b/wallet/account_migration.c new file mode 100644 index 000000000000..3e4807de3a54 --- /dev/null +++ b/wallet/account_migration.c @@ -0,0 +1,537 @@ +/* All this code is to read the old accounts.db file from bookkeeper + * and copy the moves table */ +#include "config.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* These functions and definitions copied almost exactly from old + * plugins/bkpr/{recorder.c,chain_event.h,channel_event.h} + */ +struct chain_event { + + /* Id of this chain event in the database */ + u64 db_id; + + /* db_id of account this event belongs to */ + u64 acct_db_id; + + /* Name of the account this belongs to */ + char *acct_name; + + /* Name of account this originated from */ + char *origin_acct; + + /* Tag describing the event */ + const char *tag; + + /* Is the node's wallet ignoring this? */ + bool ignored; + + /* Is this chain output stealable? If so + * we'll need to watch it for longer */ + bool stealable; + + /* Is this chain event because of a splice + * confirmation? */ + bool splice_close; + + /* Is this a rebalance event? */ + bool rebalance; + + /* Amount we received in this event */ + struct amount_msat credit; + + /* Amount we paid in this event */ + struct amount_msat debit; + + /* Total 'amount' of output on this chain event */ + struct amount_msat output_value; + + /* What token are the credit/debits? */ + const char *currency; + + /* What time did the event happen */ + u64 timestamp; + + /* What block did the event happen */ + u32 blockheight; + + /* What txo did this event concern */ + struct bitcoin_outpoint outpoint; + + /* What tx was the outpoint spent in (if spent) */ + struct bitcoin_txid *spending_txid; + + /* Sometimes chain events resolve payments */ + struct sha256 *payment_id; + + /* Desc of event (maybe useful for printing notes) */ + const char *desc; + + /* Added: close_count */ + u32 output_count; + + /* Added: peer_id */ + struct node_id *peer_id; + + /* Added: did we open this account? */ + bool we_opened; +}; + +static struct chain_event *stmt2chain_event(const tal_t *ctx, struct db_stmt *stmt) +{ + struct chain_event *e = tal(ctx, struct chain_event); + e->db_id = db_col_u64(stmt, "e.id"); + e->acct_db_id = db_col_u64(stmt, "e.account_id"); + e->acct_name = db_col_strdup(e, stmt, "a.name"); + + if (!db_col_is_null(stmt, "e.origin")) + e->origin_acct = db_col_strdup(e, stmt, "e.origin"); + else + e->origin_acct = NULL; + + e->tag = db_col_strdup(e, stmt, "e.tag"); + + e->credit = db_col_amount_msat(stmt, "e.credit"); + e->debit = db_col_amount_msat(stmt, "e.debit"); + e->output_value = db_col_amount_msat(stmt, "e.output_value"); + + e->currency = db_col_strdup(e, stmt, "e.currency"); + e->timestamp = db_col_u64(stmt, "e.timestamp"); + e->blockheight = db_col_int(stmt, "e.blockheight"); + + db_col_txid(stmt, "e.utxo_txid", &e->outpoint.txid); + e->outpoint.n = db_col_int(stmt, "e.outnum"); + + if (!db_col_is_null(stmt, "e.payment_id")) { + e->payment_id = tal(e, struct sha256); + db_col_sha256(stmt, "e.payment_id", e->payment_id); + } else + e->payment_id = NULL; + + if (!db_col_is_null(stmt, "e.spending_txid")) { + e->spending_txid = tal(e, struct bitcoin_txid); + db_col_txid(stmt, "e.spending_txid", e->spending_txid); + } else + e->spending_txid = NULL; + + e->ignored = db_col_int(stmt, "e.ignored") == 1; + e->stealable = db_col_int(stmt, "e.stealable") == 1; + + if (!db_col_is_null(stmt, "e.ev_desc")) + e->desc = db_col_strdup(e, stmt, "e.ev_desc"); + else + e->desc = NULL; + + e->splice_close = db_col_int(stmt, "e.spliced") == 1; + e->output_count = db_col_int(stmt, "a.closed_count"); + if (!db_col_is_null(stmt, "a.peer_id")) { + e->peer_id = tal(e, struct node_id); + db_col_node_id(stmt, "a.peer_id", e->peer_id); + } else + e->peer_id = NULL; + + e->we_opened = db_col_int(stmt, "a.we_opened"); + + /* Note that they would have never executed the final migration from + * "common: remove "ignored" tag", in this PR, so we do that now: + * {SQL("UPDATE chain_events" + * " SET account_id = (SELECT id FROM accounts WHERE name = 'external')" + * " WHERE account_id = (SELECT id FROM accounts WHERE name = 'wallet')" + * " AND ignored = 1"), NULL}, + */ + if (e->ignored && streq(e->acct_name, ACCOUNT_NAME_WALLET)) + e->acct_name = ACCOUNT_NAME_EXTERNAL; + + return e; +} + +static struct chain_event **find_chain_events(const tal_t *ctx, + struct db_stmt *stmt TAKES) +{ + struct chain_event **results; + + db_query_prepared(stmt); + if (stmt->error) + db_fatal(stmt->db, "find_chain_events err: %s", stmt->error); + results = tal_arr(ctx, struct chain_event *, 0); + while (db_step(stmt)) { + struct chain_event *e = stmt2chain_event(results, stmt); + tal_arr_expand(&results, e); + } + + if (taken(stmt)) + tal_free(stmt); + + return results; +} + +static struct chain_event **list_chain_events(const tal_t *ctx, struct db *db) +{ + 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" + ", a.closed_count" + ", a.peer_id" + ", a.we_opened" + " FROM chain_events e" + " LEFT OUTER JOIN accounts a" + " ON e.account_id = a.id" + " ORDER BY e.timestamp, e.id;")); + + return find_chain_events(ctx, take(stmt)); +} + +struct channel_event { + + /* Id of this chain event in the database */ + u64 db_id; + + /* db_id of account this event belongs to */ + u64 acct_db_id; + + /* Name of the account this belongs to */ + char *acct_name; + + /* Tag describing the event */ + const char *tag; + + /* Amount we received in this event */ + struct amount_msat credit; + + /* Amount we paid in this event */ + struct amount_msat debit; + + /* Total 'fees' related to this channel event */ + struct amount_msat fees; + + /* What token are the credit/debits? */ + const char *currency; + + /* Payment identifier (typically the preimage hash) */ + struct sha256 *payment_id; + + /* Some payments share a payment_id, and are differentiable via id */ + u32 part_id; + + /* What time did the event happen */ + u64 timestamp; + + /* Description, usually from invoice */ + const char *desc; + + /* ID of paired event, iff is a rebalance */ + u64 *rebalance_id; +}; + +static struct channel_event *stmt2channel_event(const tal_t *ctx, struct db_stmt *stmt) +{ + struct channel_event *e = tal(ctx, struct channel_event); + + e->db_id = db_col_u64(stmt, "e.id"); + e->acct_db_id = db_col_u64(stmt, "e.account_id"); + e->acct_name = db_col_strdup(e, stmt, "a.name"); + + e->tag = db_col_strdup(e, stmt, "e.tag"); + + e->credit = db_col_amount_msat(stmt, "e.credit"); + e->debit = db_col_amount_msat(stmt, "e.debit"); + e->fees = db_col_amount_msat(stmt, "e.fees"); + + e->currency = db_col_strdup(e, stmt, "e.currency"); + if (!db_col_is_null(stmt, "e.payment_id")) { + e->payment_id = tal(e, struct sha256); + db_col_sha256(stmt, "e.payment_id", e->payment_id); + } else + e->payment_id = NULL; + e->part_id = db_col_int(stmt, "e.part_id"); + e->timestamp = db_col_u64(stmt, "e.timestamp"); + + if (!db_col_is_null(stmt, "e.ev_desc")) + e->desc = db_col_strdup(e, stmt, "e.ev_desc"); + else + e->desc = NULL; + + if (!db_col_is_null(stmt, "e.rebalance_id")) { + e->rebalance_id = tal(e, u64); + *e->rebalance_id = db_col_u64(stmt, "e.rebalance_id"); + } else + e->rebalance_id = NULL; + + return e; +} + +static struct channel_event **list_channel_events(const tal_t *ctx, + struct db *db) + +{ + struct db_stmt *stmt; + struct channel_event **results; + + stmt = db_prepare_v2(db, SQL("SELECT" + " e.id" + ", e.account_id" + ", a.name" + ", e.tag" + ", e.credit" + ", e.debit" + ", e.fees" + ", e.currency" + ", e.payment_id" + ", e.part_id" + ", e.timestamp" + ", e.ev_desc" + ", e.rebalance_id" + " FROM channel_events e" + " LEFT OUTER JOIN accounts a" + " ON a.id = e.account_id" + " ORDER BY e.timestamp, e.id;")); + db_query_prepared(stmt); + + results = tal_arr(ctx, struct channel_event *, 0); + while (db_step(stmt)) { + struct channel_event *e = stmt2channel_event(results, stmt); + tal_arr_expand(&results, e); + } + tal_free(stmt); + + return results; +} +/* end stolen code */ + +static void acct_db_error(struct lightningd *ld, bool fatal, const char *fmt, va_list ap) +{ + va_list ap2; + + fmt = tal_fmt(tmpctx, "bookkeper migration: %s", fmt); + va_copy(ap2, ap); + logv(ld->log, LOG_BROKEN, NULL, true, fmt, ap); + + if (fatal) + fatal_vfmt(fmt, ap2); + va_end(ap2); +} + +void migrate_from_account_db(struct lightningd *ld, struct db *db) +{ + const char *olddir = NULL; + const char *db_dsn; + struct db *account_db; + struct chain_event **chain_events; + struct channel_event **channel_events; + size_t descriptions_migrated = 0; + struct db_stmt *stmt; + int version; + + /* Initialize wait indices: we're going to use it to generate ids. */ + load_indexes(db, ld->indexes); + + /* Switch to bookkeeper-dir, if specified */ + if (ld->old_bookkeeper_dir) { + olddir = path_cwd(NULL); + if (chdir(ld->old_bookkeeper_dir) != 0) + fatal("Unable to switch to 'bookkeeper-dir'=%s", + ld->old_bookkeeper_dir); + } + + /* No user suppled db_dsn, set one up here */ + db_dsn = ld->old_bookkeeper_db; + if (!db_dsn) + db_dsn = "sqlite3://accounts.sqlite3"; + + /* If we can't open it, we ignore it */ + account_db = db_open(NULL, db_dsn, ld->developer, false, acct_db_error, ld); + if (!account_db) { + migrate_setup_coinmoves(ld, db); + goto out; + } + + /* Load events */ + db_begin_transaction(account_db); + version = db_get_version(account_db); + /* -1 means empty database (Postgres usually). */ + if (version == -1) { + db_commit_transaction(account_db); + tal_free(account_db); + migrate_setup_coinmoves(ld, db); + goto out; + } + /* Last migration was 24.08. Migrate there first if this happens. */ + if (version != 17) + fatal("Cannot migrate account database version %i", version); + chain_events = list_chain_events(tmpctx, account_db); + channel_events = list_channel_events(tmpctx, account_db); + db_commit_transaction(account_db); + tal_free(account_db); + + for (size_t i = 0; i < tal_count(chain_events); i++) { + const struct chain_event *ev = chain_events[i]; + struct mvt_account_id *account = tal(ev, struct mvt_account_id); + struct mvt_tags tags; + enum mvt_tag tag; + struct amount_sat output_sat; + u64 id; + + stmt = db_prepare_v2(db, + SQL("INSERT INTO chain_moves (" + " id," + " account_channel_id," + " account_nonchannel_id," + " tag_bitmap," + " credit_or_debit," + " timestamp," + " utxo," + " spending_txid," + " peer_id," + " payment_hash," + " block_height," + " output_sat," + " originating_channel_id," + " originating_nonchannel_id," + " output_count) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)")); + set_mvt_account_id(account, NULL, ev->acct_name); + id = chain_mvt_index_created(ld, db, account, ev->credit, ev->debit); + db_bind_u64(stmt, id); + if (!mvt_tag_parse(ev->tag, strlen(ev->tag), &tag)) + abort(); + tags = tag_to_mvt_tags(tag); + if (tag == MVT_CHANNEL_OPEN && ev->we_opened) + mvt_tag_set(&tags, MVT_OPENER); + if (ev->splice_close) + mvt_tag_set(&tags, MVT_SPLICE); + if (ev->stealable) + mvt_tag_set(&tags, MVT_STEALABLE); + db_bind_mvt_account_id(stmt, db, account); + db_bind_mvt_tags(stmt, tags); + db_bind_credit_debit(stmt, ev->credit, ev->debit); + db_bind_u64(stmt, ev->timestamp); + db_bind_outpoint(stmt, &ev->outpoint); + if (ev->spending_txid) + db_bind_txid(stmt, ev->spending_txid); + else + db_bind_null(stmt); + if (ev->peer_id) + db_bind_node_id(stmt, ev->peer_id); + else + db_bind_null(stmt); + if (ev->payment_id) + db_bind_sha256(stmt, ev->payment_id); + else + db_bind_null(stmt); + db_bind_int(stmt, ev->blockheight); + if (!amount_msat_to_sat(&output_sat, ev->output_value)) + abort(); + db_bind_amount_sat(stmt, output_sat); + if (ev->origin_acct) { + struct mvt_account_id *orig_account = tal(ev, struct mvt_account_id); + set_mvt_account_id(orig_account, NULL, ev->origin_acct); + db_bind_mvt_account_id(stmt, db, orig_account); + } else { + db_bind_null(stmt); + db_bind_null(stmt); + } + if (ev->output_count > 0) + db_bind_int(stmt, ev->output_count); + else + db_bind_null(stmt); + db_exec_prepared_v2(take(stmt)); + + /* Put descriptions into datastore for bookkeeper */ + if (ev->desc) { + log_debug(ld->log, "Adding utxo description '%s' to %s", + ev->desc, fmt_bitcoin_outpoint(tmpctx, &ev->outpoint)); + wallet_datastore_save_utxo_description(db, &ev->outpoint, ev->desc); + descriptions_migrated++; + } + } + + for (size_t i = 0; i < tal_count(channel_events); i++) { + const struct channel_event *ev = channel_events[i]; + struct mvt_account_id *account = tal(ev, struct mvt_account_id); + enum mvt_tag tag; + u64 id; + + stmt = db_prepare_v2(db, + SQL("INSERT INTO channel_moves (" + " id," + " account_channel_id," + " account_nonchannel_id," + " credit_or_debit," + " tag_bitmap," + " timestamp," + " payment_hash," + " payment_part_id," + " payment_group_id," + " fees) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?);")); + set_mvt_account_id(account, NULL, ev->acct_name); + id = channel_mvt_index_created(ld, db, account, ev->credit, ev->debit); + db_bind_u64(stmt, id); + db_bind_mvt_account_id(stmt, db, account); + db_bind_credit_debit(stmt, ev->credit, ev->debit); + if (!mvt_tag_parse(ev->tag, strlen(ev->tag), &tag)) + abort(); + db_bind_mvt_tags(stmt, tag_to_mvt_tags(tag)); + db_bind_u64(stmt, ev->timestamp); + if (ev->payment_id) + db_bind_sha256(stmt, ev->payment_id); + else + db_bind_null(stmt); + if (ev->part_id) { + db_bind_u64(stmt, ev->part_id); + /* Unf. this was not recorded! */ + db_bind_u64(stmt, 0); + } else { + db_bind_null(stmt); + db_bind_null(stmt); + } + db_bind_amount_msat(stmt, ev->fees); + db_exec_prepared_v2(take(stmt)); + + /* Put descriptions into datastore for bookkeeper */ + if (ev->desc && ev->payment_id) { + wallet_datastore_save_payment_description(db, ev->payment_id, ev->desc); + descriptions_migrated++; + } + } + + log_info(ld->log, "bookkeeper migration complete: migrated %zu chainmoves, %zu channelmoves, %zu descriptions", + tal_count(chain_events), + tal_count(channel_events), + descriptions_migrated); + +out: + if (olddir) { + if (chdir(olddir) != 0) + fatal("Unable to switch to back to %s", + olddir); + tal_free(olddir); + } +} diff --git a/wallet/account_migration.h b/wallet/account_migration.h new file mode 100644 index 000000000000..a4af40608636 --- /dev/null +++ b/wallet/account_migration.h @@ -0,0 +1,10 @@ +#ifndef LIGHTNING_WALLET_ACCOUNT_MIGRATION_H +#define LIGHTNING_WALLET_ACCOUNT_MIGRATION_H +#include "config.h" + +struct lightningd; +struct db; + +/* Some migrations are so epic they get their own file. Not in a good way. */ +void migrate_from_account_db(struct lightningd *ld, struct db *db); +#endif /* LIGHTNING_WALLET_ACCOUNT_MIGRATION_H */ diff --git a/wallet/db.c b/wallet/db.c index bfed9455f634..6057952dd9c1 100644 --- a/wallet/db.c +++ b/wallet/db.c @@ -18,6 +18,7 @@ #include #include #include +#include #include #include #include @@ -1045,6 +1046,53 @@ static struct migration dbmigrations[] = { {SQL("ALTER TABLE channel_funding_inflights ADD i_sent_sigs INTEGER DEFAULT 0"), NULL}, {SQL("ALTER TABLE channels ADD old_scids BLOB DEFAULT NULL;"), NULL}, {NULL, migrate_initialize_alias_local}, + /* Avoids duplication in chain_moves and coin_moves tables */ + {SQL("CREATE TABLE move_accounts (" + " id BIGSERIAL," + " name TEXT," + " PRIMARY KEY (id)," + " UNIQUE (name)" + ")"), NULL}, + {SQL("CREATE TABLE chain_moves (" + " id BIGSERIAL," + /* One of these is null */ + " account_channel_id BIGINT references channels(id)," + " account_nonchannel_id BIGINT references move_accounts(id)," + " tag_bitmap BIGINT NOT NULL," + " credit_or_debit BIGINT NOT NULL," + " timestamp BIGINT NOT NULL," + " utxo BLOB NOT NULL," + " spending_txid BLOB," + /* This does NOT reference peers(node_id), since we can have + * MVT_CHANNEL_PROPOSED events on zeroconf channels where we end up + * forgetting the channel, thus the peer */ + " peer_id BLOB," + " payment_hash BLOB," + " block_height INTEGER NOT NULL," + " output_sat BIGINT NOT NULL," + /* One of these is null */ + " originating_channel_id BIGINT references channels(id)," + " originating_nonchannel_id BIGINT references move_accounts(id)," + " output_count INTEGER," + " PRIMARY KEY (id)" + ")"), NULL}, + {SQL("CREATE TABLE channel_moves (" + " id BIGSERIAL," + /* One of these is null */ + " account_channel_id BIGINT references channels(id)," + " account_nonchannel_id BIGINT references move_accounts(id)," + " tag_bitmap BIGINT NOT NULL," + " credit_or_debit BIGINT NOT NULL," + " timestamp BIGINT NOT NULL," + " payment_hash BLOB," + " payment_part_id BIGINT," + " payment_group_id BIGINT," + " fees BIGINT NOT NULL," + " PRIMARY KEY (id)" + ")"), NULL}, + /* We do a lookup before each append, to avoid duplicates */ + {SQL("CREATE INDEX chain_moves_utxo_idx ON chain_moves (utxo)"), NULL}, + {NULL, migrate_from_account_db}, }; /** @@ -1125,7 +1173,7 @@ static void db_error(struct lightningd *ld, bool fatal, const char *fmt, va_list struct db *db_setup(const tal_t *ctx, struct lightningd *ld, const struct ext_key *bip32_base) { - struct db *db = db_open(ctx, ld->wallet_dsn, ld->developer, + struct db *db = db_open(ctx, ld->wallet_dsn, ld->developer, true, db_error, ld); bool migrated; diff --git a/wallet/invoices.c b/wallet/invoices.c index 8122cac51c5a..1655c8f96f85 100644 --- a/wallet/invoices.c +++ b/wallet/invoices.c @@ -777,13 +777,14 @@ static u64 invoice_index_inc(struct lightningd *ld, invstrname = "bolt11"; - return wait_index_increment(ld, WAIT_SUBSYSTEM_INVOICE, idx, - "status", state ? invoice_status_str(*state) : NULL, - /* We don't want to add more JSON escapes here! */ - "=label", label ? tal_fmt(tmpctx, "\"%s\"", label->s) : NULL, - invstrname, invstring, - "description", description, - NULL); + return wait_index_increment(ld, ld->wallet->db, + WAIT_SUBSYSTEM_INVOICE, idx, + "status", state ? invoice_status_str(*state) : NULL, + /* We don't want to add more JSON escapes here! */ + "=label", label ? tal_fmt(tmpctx, "\"%s\"", label->s) : NULL, + invstrname, invstring, + "description", description, + NULL); } void invoice_index_deleted(struct lightningd *ld, diff --git a/wallet/test/Makefile b/wallet/test/Makefile index 61789189963f..c986342dfbcf 100644 --- a/wallet/test/Makefile +++ b/wallet/test/Makefile @@ -10,6 +10,7 @@ WALLET_TEST_COMMON_OBJS := \ common/autodata.o \ common/base32.o \ common/blockheight_states.o \ + common/channel_id.o \ common/channel_type.o \ common/derive_basepoints.o \ common/features.o \ diff --git a/wallet/test/run-db.c b/wallet/test/run-db.c index b5eca59ae8ed..106cd38026ae 100644 --- a/wallet/test/run-db.c +++ b/wallet/test/run-db.c @@ -36,12 +36,29 @@ void bitcoind_getrawblockbyheight_(const tal_t *ctx UNNEEDED, void *arg) UNNEEDED, void *arg UNNEEDED) { fprintf(stderr, "bitcoind_getrawblockbyheight_ called!\n"); abort(); } +/* Generated stub for chain_mvt_index_created */ +u64 chain_mvt_index_created(struct lightningd *ld UNNEEDED, + struct db *db UNNEEDED, + const struct mvt_account_id *account UNNEEDED, + struct amount_msat credit UNNEEDED, + struct amount_msat debit UNNEEDED) +{ fprintf(stderr, "chain_mvt_index_created called!\n"); abort(); } +/* Generated stub for channel_by_dbid */ +struct channel *channel_by_dbid(struct lightningd *ld UNNEEDED, const u64 dbid UNNEEDED) +{ fprintf(stderr, "channel_by_dbid called!\n"); abort(); } /* Generated stub for channel_gossip_get_remote_update */ const struct peer_update *channel_gossip_get_remote_update(const struct channel *channel UNNEEDED) { fprintf(stderr, "channel_gossip_get_remote_update called!\n"); abort(); } /* Generated stub for channel_gossip_update */ void channel_gossip_update(struct channel *channel UNNEEDED) { fprintf(stderr, "channel_gossip_update called!\n"); abort(); } +/* Generated stub for channel_mvt_index_created */ +u64 channel_mvt_index_created(struct lightningd *ld UNNEEDED, + struct db *db UNNEEDED, + const struct mvt_account_id *account UNNEEDED, + struct amount_msat credit UNNEEDED, + struct amount_msat debit UNNEEDED) +{ fprintf(stderr, "channel_mvt_index_created called!\n"); abort(); } /* Generated stub for channel_scid_or_local_alias */ struct short_channel_id channel_scid_or_local_alias(const struct channel *chan UNNEEDED) { fprintf(stderr, "channel_scid_or_local_alias called!\n"); abort(); } @@ -59,10 +76,6 @@ struct onionreply *create_onionreply(const tal_t *ctx UNNEEDED, const struct secret *shared_secret UNNEEDED, const u8 *failure_msg UNNEEDED) { fprintf(stderr, "create_onionreply called!\n"); abort(); } -/* Generated stub for derive_channel_id */ -void derive_channel_id(struct channel_id *channel_id UNNEEDED, - const struct bitcoin_outpoint *outpoint UNNEEDED) -{ fprintf(stderr, "derive_channel_id called!\n"); abort(); } /* Generated stub for fatal */ void fatal(const char *fmt UNNEEDED, ...) { fprintf(stderr, "fatal called!\n"); abort(); } @@ -72,9 +85,6 @@ void fatal_vfmt(const char *fmt UNNEEDED, va_list ap UNNEEDED) /* Generated stub for find_peer_by_dbid */ struct peer *find_peer_by_dbid(struct lightningd *ld UNNEEDED, u64 dbid UNNEEDED) { fprintf(stderr, "find_peer_by_dbid called!\n"); abort(); } -/* Generated stub for fmt_channel_id */ -char *fmt_channel_id(const tal_t *ctx UNNEEDED, const struct channel_id *channel_id UNNEEDED) -{ fprintf(stderr, "fmt_channel_id called!\n"); abort(); } /* Generated stub for forward_index_created */ u64 forward_index_created(struct lightningd *ld UNNEEDED, enum forward_status status UNNEEDED, @@ -167,6 +177,9 @@ void memleak_scan_outpointfilter(struct htable *memtable UNNEEDED, /* Generated stub for mk_mvt_tags_ */ struct mvt_tags mk_mvt_tags_(enum mvt_tag tag UNNEEDED, ...) { fprintf(stderr, "mk_mvt_tags_ called!\n"); abort(); } +/* Generated stub for mvt_tags_valid */ +bool mvt_tags_valid(struct mvt_tags tags UNNEEDED) +{ fprintf(stderr, "mvt_tags_valid called!\n"); abort(); } /* Generated stub for new_channel */ struct channel *new_channel(struct peer *peer UNNEEDED, u64 dbid UNNEEDED, /* NULL or stolen */ @@ -243,6 +256,19 @@ struct channel *new_channel(struct peer *peer UNNEEDED, u64 dbid UNNEEDED, const struct channel_stats *stats UNNEEDED, struct channel_state_change **state_changes STEALS UNNEEDED) { fprintf(stderr, "new_channel called!\n"); abort(); } +/* Generated stub for new_channel_coin_mvt_general */ +struct channel_coin_mvt *new_channel_coin_mvt_general(const tal_t *ctx UNNEEDED, + const struct channel *channel UNNEEDED, + const struct channel_id *cid UNNEEDED, + u64 timestamp UNNEEDED, + const struct sha256 *payment_hash TAKES UNNEEDED, + const u64 *part_id UNNEEDED, + const u64 *group_id UNNEEDED, + enum coin_mvt_dir direction UNNEEDED, + struct amount_msat amount UNNEEDED, + struct mvt_tags tags UNNEEDED, + struct amount_msat fees UNNEEDED) +{ fprintf(stderr, "new_channel_coin_mvt_general called!\n"); abort(); } /* Generated stub for new_channel_state_change */ struct channel_state_change *new_channel_state_change(const tal_t *ctx UNNEEDED, struct timeabs timestamp UNNEEDED, @@ -251,6 +277,28 @@ struct channel_state_change *new_channel_state_change(const tal_t *ctx UNNEEDED, enum state_change cause UNNEEDED, const char *message TAKES UNNEEDED) { fprintf(stderr, "new_channel_state_change called!\n"); abort(); } +/* Generated stub for new_coin_channel_open_general */ +struct chain_coin_mvt *new_coin_channel_open_general(const tal_t *ctx UNNEEDED, + const struct channel *channel UNNEEDED, + const struct channel_id *cid UNNEEDED, + u64 timestamp UNNEEDED, + const struct bitcoin_outpoint *out UNNEEDED, + const struct node_id *peer_id UNNEEDED, + u32 blockheight UNNEEDED, + const struct amount_msat amount UNNEEDED, + const struct amount_sat output_val UNNEEDED, + bool is_opener UNNEEDED, + bool is_leased UNNEEDED) +{ fprintf(stderr, "new_coin_channel_open_general called!\n"); abort(); } +/* Generated stub for new_coin_channel_push_general */ +struct channel_coin_mvt *new_coin_channel_push_general(const tal_t *ctx UNNEEDED, + const struct channel *channel UNNEEDED, + const struct channel_id *cid UNNEEDED, + u64 timestamp UNNEEDED, + enum coin_mvt_dir direction UNNEEDED, + struct amount_msat amount UNNEEDED, + struct mvt_tags tags UNNEEDED) +{ fprintf(stderr, "new_coin_channel_push_general called!\n"); abort(); } /* Generated stub for new_coin_wallet_deposit */ struct chain_coin_mvt *new_coin_wallet_deposit(const tal_t *ctx UNNEEDED, const struct bitcoin_outpoint *outpoint UNNEEDED, @@ -294,8 +342,14 @@ struct peer *new_peer(struct lightningd *ld UNNEEDED, u64 dbid UNNEEDED, { fprintf(stderr, "new_peer called!\n"); abort(); } /* Generated stub for notify_chain_mvt */ void notify_chain_mvt(struct lightningd *ld UNNEEDED, - const struct chain_coin_mvt *chain_mvt UNNEEDED) + const struct chain_coin_mvt *chain_mvt UNNEEDED, + u64 id UNNEEDED) { fprintf(stderr, "notify_chain_mvt called!\n"); abort(); } +/* Generated stub for notify_channel_mvt */ +void notify_channel_mvt(struct lightningd *ld UNNEEDED, + const struct channel_coin_mvt *chan_mvt UNNEEDED, + u64 id UNNEEDED) +{ fprintf(stderr, "notify_channel_mvt called!\n"); abort(); } /* Generated stub for notify_forward_event */ void notify_forward_event(struct lightningd *ld UNNEEDED, const struct htlc_in *in UNNEEDED, @@ -331,6 +385,9 @@ void outpointfilter_remove(struct outpointfilter *of UNNEEDED, /* Generated stub for peer_set_dbid */ void peer_set_dbid(struct peer *peer UNNEEDED, u64 dbid UNNEEDED) { fprintf(stderr, "peer_set_dbid called!\n"); abort(); } +/* Generated stub for primary_mvt_tag */ +enum mvt_tag primary_mvt_tag(struct mvt_tags tags UNNEEDED) +{ fprintf(stderr, "primary_mvt_tag called!\n"); abort(); } /* Generated stub for psbt_fixup */ const u8 *psbt_fixup(const tal_t *ctx UNNEEDED, const u8 *psbtblob UNNEEDED) { fprintf(stderr, "psbt_fixup called!\n"); abort(); } @@ -395,6 +452,10 @@ void plugin_hook_db_sync(struct db *db UNNEEDED) { } +void migrate_from_account_db(struct lightningd *ld UNNEEDED, struct db *db UNNEEDED) +{ +} + static struct db *create_test_db(void) { struct db *db; @@ -407,7 +468,7 @@ static struct db *create_test_db(void) dsn = tal_fmt(NULL, "sqlite3://%s", filename); tal_free(filename); - db = db_open(NULL, dsn, true, db_error, (struct lightningd *)NULL); + db = db_open(NULL, dsn, true, true, db_error, (struct lightningd *)NULL); db->data_version = 0; db->report_changes_fn = NULL; diff --git a/wallet/test/run-wallet.c b/wallet/test/run-wallet.c index c55cc173027b..92ec32bc3ad5 100644 --- a/wallet/test/run-wallet.c +++ b/wallet/test/run-wallet.c @@ -97,6 +97,20 @@ void broadcast_tx_(const tal_t *ctx UNNEEDED, bool (*refresh)(struct channel * UNNEEDED, const struct bitcoin_tx ** UNNEEDED, void *) UNNEEDED, void *cbarg TAKES UNNEEDED) { fprintf(stderr, "broadcast_tx_ called!\n"); abort(); } +/* Generated stub for chain_mvt_index_created */ +u64 chain_mvt_index_created(struct lightningd *ld UNNEEDED, + struct db *db UNNEEDED, + const struct mvt_account_id *account UNNEEDED, + struct amount_msat credit UNNEEDED, + struct amount_msat debit UNNEEDED) +{ fprintf(stderr, "chain_mvt_index_created called!\n"); abort(); } +/* Generated stub for channel_mvt_index_created */ +u64 channel_mvt_index_created(struct lightningd *ld UNNEEDED, + struct db *db UNNEEDED, + const struct mvt_account_id *account UNNEEDED, + struct amount_msat credit UNNEEDED, + struct amount_msat debit UNNEEDED) +{ fprintf(stderr, "channel_mvt_index_created called!\n"); abort(); } /* Generated stub for channel_unsaved_close_conn */ void channel_unsaved_close_conn(struct channel *channel UNNEEDED, const char *why UNNEEDED) { fprintf(stderr, "channel_unsaved_close_conn called!\n"); abort(); } @@ -233,10 +247,6 @@ bool depthcb_update_scid(struct channel *channel UNNEEDED, const struct bitcoin_txid *txid UNNEEDED, const struct bitcoin_outpoint *outpoint UNNEEDED) { fprintf(stderr, "depthcb_update_scid called!\n"); abort(); } -/* Generated stub for derive_channel_id */ -void derive_channel_id(struct channel_id *channel_id UNNEEDED, - const struct bitcoin_outpoint *outpoint UNNEEDED) -{ fprintf(stderr, "derive_channel_id called!\n"); abort(); } /* Generated stub for dev_disconnect_permanent */ bool dev_disconnect_permanent(struct lightningd *ld UNNEEDED) { fprintf(stderr, "dev_disconnect_permanent called!\n"); abort(); } @@ -254,9 +264,6 @@ void fatal(const char *fmt UNNEEDED, ...) /* Generated stub for fatal_vfmt */ void fatal_vfmt(const char *fmt UNNEEDED, va_list ap UNNEEDED) { fprintf(stderr, "fatal_vfmt called!\n"); abort(); } -/* Generated stub for fmt_channel_id */ -char *fmt_channel_id(const tal_t *ctx UNNEEDED, const struct channel_id *channel_id UNNEEDED) -{ fprintf(stderr, "fmt_channel_id called!\n"); abort(); } /* Generated stub for force_peer_disconnect */ void force_peer_disconnect(struct lightningd *ld UNNEEDED, const struct peer *peer UNNEEDED, @@ -286,10 +293,6 @@ u64 forward_index_update_status(struct lightningd *ld UNNEEDED, struct amount_msat in_amount UNNEEDED, const struct short_channel_id *out_channel UNNEEDED) { fprintf(stderr, "forward_index_update_status called!\n"); abort(); } -/* Generated stub for fromwire_channel_id */ -bool fromwire_channel_id(const u8 **cursor UNNEEDED, size_t *max UNNEEDED, - struct channel_id *channel_id UNNEEDED) -{ fprintf(stderr, "fromwire_channel_id called!\n"); abort(); } /* Generated stub for fromwire_channel_type */ struct channel_type *fromwire_channel_type(const tal_t *ctx UNNEEDED, const u8 **cursor UNNEEDED, size_t *plen UNNEEDED) { fprintf(stderr, "fromwire_channel_type called!\n"); abort(); } @@ -655,6 +658,22 @@ void memleak_scan_outpointfilter(struct htable *memtable UNNEEDED, /* Generated stub for mk_mvt_tags_ */ struct mvt_tags mk_mvt_tags_(enum mvt_tag tag UNNEEDED, ...) { fprintf(stderr, "mk_mvt_tags_ called!\n"); abort(); } +/* Generated stub for mvt_tags_valid */ +bool mvt_tags_valid(struct mvt_tags tags UNNEEDED) +{ fprintf(stderr, "mvt_tags_valid called!\n"); abort(); } +/* Generated stub for new_channel_coin_mvt_general */ +struct channel_coin_mvt *new_channel_coin_mvt_general(const tal_t *ctx UNNEEDED, + const struct channel *channel UNNEEDED, + const struct channel_id *cid UNNEEDED, + u64 timestamp UNNEEDED, + const struct sha256 *payment_hash TAKES UNNEEDED, + const u64 *part_id UNNEEDED, + const u64 *group_id UNNEEDED, + enum coin_mvt_dir direction UNNEEDED, + struct amount_msat amount UNNEEDED, + struct mvt_tags tags UNNEEDED, + struct amount_msat fees UNNEEDED) +{ fprintf(stderr, "new_channel_coin_mvt_general called!\n"); abort(); } /* Generated stub for new_channel_mvt_invoice_hin */ struct channel_coin_mvt *new_channel_mvt_invoice_hin(const tal_t *ctx UNNEEDED, const struct htlc_in *hin UNNEEDED, @@ -675,6 +694,28 @@ struct channel_coin_mvt *new_channel_mvt_routed_hout(const tal_t *ctx UNNEEDED, const struct htlc_out *hout UNNEEDED, const struct channel *channel UNNEEDED) { fprintf(stderr, "new_channel_mvt_routed_hout called!\n"); abort(); } +/* Generated stub for new_coin_channel_open_general */ +struct chain_coin_mvt *new_coin_channel_open_general(const tal_t *ctx UNNEEDED, + const struct channel *channel UNNEEDED, + const struct channel_id *cid UNNEEDED, + u64 timestamp UNNEEDED, + const struct bitcoin_outpoint *out UNNEEDED, + const struct node_id *peer_id UNNEEDED, + u32 blockheight UNNEEDED, + const struct amount_msat amount UNNEEDED, + const struct amount_sat output_val UNNEEDED, + bool is_opener UNNEEDED, + bool is_leased UNNEEDED) +{ fprintf(stderr, "new_coin_channel_open_general called!\n"); abort(); } +/* Generated stub for new_coin_channel_push_general */ +struct channel_coin_mvt *new_coin_channel_push_general(const tal_t *ctx UNNEEDED, + const struct channel *channel UNNEEDED, + const struct channel_id *cid UNNEEDED, + u64 timestamp UNNEEDED, + enum coin_mvt_dir direction UNNEEDED, + struct amount_msat amount UNNEEDED, + struct mvt_tags tags UNNEEDED) +{ fprintf(stderr, "new_coin_channel_push_general called!\n"); abort(); } /* Generated stub for new_coin_wallet_deposit */ struct chain_coin_mvt *new_coin_wallet_deposit(const tal_t *ctx UNNEEDED, const struct bitcoin_outpoint *outpoint UNNEEDED, @@ -702,11 +743,13 @@ bool node_announcement_same(const u8 *nann1 UNNEEDED, const u8 *nann2 UNNEEDED) { fprintf(stderr, "node_announcement_same called!\n"); abort(); } /* Generated stub for notify_chain_mvt */ void notify_chain_mvt(struct lightningd *ld UNNEEDED, - const struct chain_coin_mvt *chain_mvt UNNEEDED) + const struct chain_coin_mvt *chain_mvt UNNEEDED, + u64 id UNNEEDED) { fprintf(stderr, "notify_chain_mvt called!\n"); abort(); } /* Generated stub for notify_channel_mvt */ void notify_channel_mvt(struct lightningd *ld UNNEEDED, - const struct channel_coin_mvt *chan_mvt UNNEEDED) + const struct channel_coin_mvt *chan_mvt UNNEEDED, + u64 id UNNEEDED) { fprintf(stderr, "notify_channel_mvt called!\n"); abort(); } /* Generated stub for notify_channel_open_failed */ void notify_channel_open_failed(struct lightningd *ld UNNEEDED, @@ -958,6 +1001,9 @@ bool plugin_hook_call_(struct lightningd *ld UNNEEDED, const char *cmd_id TAKES UNNEEDED, tal_t *cb_arg STEALS UNNEEDED) { fprintf(stderr, "plugin_hook_call_ called!\n"); abort(); } +/* Generated stub for primary_mvt_tag */ +enum mvt_tag primary_mvt_tag(struct mvt_tags tags UNNEEDED) +{ fprintf(stderr, "primary_mvt_tag called!\n"); abort(); } /* Generated stub for process_onionpacket */ struct route_step *process_onionpacket( const tal_t * ctx UNNEEDED, @@ -1053,9 +1099,6 @@ void topology_add_sync_waiter_(const tal_t *ctx UNNEEDED, /* Generated stub for towire_announcement_signatures */ u8 *towire_announcement_signatures(const tal_t *ctx UNNEEDED, const struct channel_id *channel_id UNNEEDED, struct short_channel_id short_channel_id UNNEEDED, const secp256k1_ecdsa_signature *node_signature UNNEEDED, const secp256k1_ecdsa_signature *bitcoin_signature UNNEEDED) { fprintf(stderr, "towire_announcement_signatures called!\n"); abort(); } -/* Generated stub for towire_channel_id */ -void towire_channel_id(u8 **pptr UNNEEDED, const struct channel_id *channel_id UNNEEDED) -{ fprintf(stderr, "towire_channel_id called!\n"); abort(); } /* Generated stub for towire_channel_reestablish */ u8 *towire_channel_reestablish(const tal_t *ctx UNNEEDED, const struct channel_id *channel_id UNNEEDED, u64 next_commitment_number UNNEEDED, u64 next_revocation_number UNNEEDED, const struct secret *your_last_per_commitment_secret UNNEEDED, const struct pubkey *my_current_per_commitment_point UNNEEDED, const struct tlv_channel_reestablish_tlvs *channel_reestablish UNNEEDED) { fprintf(stderr, "towire_channel_reestablish called!\n"); abort(); } @@ -1310,6 +1353,10 @@ u32 get_block_height(const struct chain_topology *topo UNNEEDED) return 0; } +void migrate_from_account_db(struct lightningd *ld UNNEEDED, struct db *db UNNEEDED) +{ +} + /** * mempat -- Set the memory to a pattern * @@ -1342,7 +1389,7 @@ static struct wallet *create_test_wallet(struct lightningd *ld, const tal_t *ctx close(fd); dsn = tal_fmt(NULL, "sqlite3://%s", filename); - w->db = db_open(w, dsn, true, test_error, ld); + w->db = db_open(w, dsn, true, true, test_error, ld); w->db->report_changes_fn = NULL; tal_free(dsn); tal_add_destructor2(w, cleanup_test_wallet, filename); @@ -1366,6 +1413,8 @@ static struct wallet *create_test_wallet(struct lightningd *ld, const tal_t *ctx /* Create fresh channels map */ ld->channels_by_scid = tal(ld, struct channel_scid_map); channel_scid_map_init(ld->channels_by_scid); + ld->channels_by_dbid = tal(ld, struct channel_dbid_map); + channel_dbid_map_init(ld->channels_by_dbid); return w; } @@ -2096,8 +2145,10 @@ static bool test_channel_inflight_crud(struct lightningd *ld, const tal_t *ctx) /* do inflights get correctly added to the channel? */ wallet_inflight_add(w, inflight); - /* Hack to remove scids from htable so we don't clash! */ + /* Hack to remove scid & dbid from htables so we don't clash! */ chanmap_remove(ld, chan, *chan->alias[LOCAL]); + remove_from_dbid_map(chan); + tal_del_destructor(chan, remove_from_dbid_map); /* do inflights get correctly loaded from the database? */ CHECK_MSG(c2 = wallet_channel_load(w, chan->dbid), @@ -2393,6 +2444,8 @@ int main(int argc, const char *argv[]) list_head_init(&ld->wait_commands); ld->closed_channels = tal(ld, struct closed_channel_map); closed_channel_map_init(ld->closed_channels); + ld->channels_by_dbid = tal(ld, struct channel_dbid_map); + channel_dbid_map_init(ld->channels_by_dbid); /* We do a runtime test here, so we still check compile! */ if (HAVE_SQLITE3) { diff --git a/wallet/wallet.c b/wallet/wallet.c index 6c2819d57083..ddc933d70ced 100644 --- a/wallet/wallet.c +++ b/wallet/wallet.c @@ -245,6 +245,38 @@ struct wallet *wallet_new(struct lightningd *ld, struct timers *timers) return wallet; } +/* Get id for move_accounts; create if necessary */ +u64 move_accounts_id(struct db *db, const char *name) +{ + struct db_stmt *stmt; + u64 ret; + + /* FIXME: Postgres can do this in one step with CONFLICT(name) + * DO NOTHING RETURNING id, and RETURNING is supported in + * SQLite 3.35+ (released 2021), but not with INSERT OR + * IGNORE. So we do this in two steps (it likely exists) */ + stmt = db_prepare_v2( + db, + SQL("SELECT id FROM move_accounts WHERE name = ?")); + db_bind_text(stmt, name); + db_query_prepared(stmt); + + if (db_step(stmt)) { + ret = db_col_u64(stmt, "id"); + tal_free(stmt); + return ret; + } + tal_free(stmt); + + /* Does not exist, so create */ + stmt = db_prepare_v2(db, + SQL("INSERT INTO move_accounts (name) VALUES (?)")); + db_bind_text(stmt, name); + db_exec_prepared_v2(stmt); + + return db_last_insert_id_v2(take(stmt)); +} + /** * wallet_add_utxo - Register an UTXO which we (partially) own * @@ -467,19 +499,11 @@ struct utxo **wallet_get_all_utxos(const tal_t *ctx, struct wallet *w) return gather_utxos(ctx, stmt); } -/** - * wallet_get_unspent_utxos - Return reserved and unreserved UTXOs. - * - * Returns a `tal_arr` of `utxo` structs. Double indirection in order - * to be able to steal individual elements onto something else. - * - * Use utxo_is_reserved() to test if it's reserved. - */ -struct utxo **wallet_get_unspent_utxos(const tal_t *ctx, struct wallet *w) +static struct utxo **db_get_unspent_utxos(const tal_t *ctx, struct db *db) { struct db_stmt *stmt; - stmt = db_prepare_v2(w->db, SQL("SELECT" + stmt = db_prepare_v2(db, SQL("SELECT" " prev_out_tx" ", prev_out_index" ", value" @@ -502,6 +526,19 @@ struct utxo **wallet_get_unspent_utxos(const tal_t *ctx, struct wallet *w) return gather_utxos(ctx, stmt); } +/** + * wallet_get_unspent_utxos - Return reserved and unreserved UTXOs. + * + * Returns a `tal_arr` of `utxo` structs. Double indirection in order + * to be able to steal individual elements onto something else. + * + * Use utxo_is_reserved() to test if it's reserved. + */ +struct utxo **wallet_get_unspent_utxos(const tal_t *ctx, struct wallet *w) +{ + return db_get_unspent_utxos(ctx, w->db); +} + struct utxo **wallet_get_unconfirmed_closeinfo_utxos(const tal_t *ctx, struct wallet *w) { @@ -2929,6 +2966,7 @@ void wallet_channel_close(struct wallet *w, * reestablish messages with enough information for nodes with lost * dbs to recover. */ struct db_stmt *stmt; + u64 new_move_id; /* Delete entries from `channel_htlcs` */ stmt = db_prepare_v2(w->db, SQL("DELETE FROM channel_htlcs " @@ -2984,6 +3022,37 @@ void wallet_channel_close(struct wallet *w, db_bind_u64(stmt, channel_state_in_db(CLOSED)); db_bind_u64(stmt, chan->dbid); db_exec_prepared_v2(take(stmt)); + + /* Update all accouting records to use channel_id string, instead of + * referring to dbid. This is robust if we delete in future, and saves + * a lookup in the load path. */ + new_move_id = move_accounts_id(w->db, fmt_channel_id(tmpctx, &chan->cid)); + stmt = db_prepare_v2(w->db, SQL("UPDATE chain_moves " + "SET account_channel_id=?," + " account_nonchannel_id=? " + "WHERE account_channel_id=?")); + db_bind_null(stmt); + db_bind_u64(stmt, new_move_id); + db_bind_u64(stmt, chan->dbid); + db_exec_prepared_v2(take(stmt)); + + stmt = db_prepare_v2(w->db, SQL("UPDATE channel_moves " + "SET account_channel_id=?," + " account_nonchannel_id=? " + "WHERE account_channel_id=?")); + db_bind_null(stmt); + db_bind_u64(stmt, new_move_id); + db_bind_u64(stmt, chan->dbid); + db_exec_prepared_v2(take(stmt)); + + stmt = db_prepare_v2(w->db, SQL("UPDATE chain_moves " + "SET originating_channel_id=?," + " originating_nonchannel_id=? " + "WHERE originating_channel_id=?")); + db_bind_null(stmt); + db_bind_u64(stmt, new_move_id); + db_bind_u64(stmt, chan->dbid); + db_exec_prepared_v2(take(stmt)); } /* Completely unused channels get wiped entirely (we've already closed it above) */ @@ -3131,7 +3200,7 @@ static void got_utxo(struct wallet *w, *blockheight, utxo->amount, mk_mvt_tags(MVT_DEPOSIT)); - notify_chain_mvt(w->ld, mvt); + wallet_save_chain_mvt(w->ld, mvt); } if (!wallet_add_utxo(w, utxo, utxo->utxotype == UTXO_P2SH_P2WPKH ? WALLET_OUTPUT_P2SH_WPKH : WALLET_OUTPUT_OUR_CHANGE)) { @@ -6206,11 +6275,11 @@ void wallet_datastore_update(struct wallet *w, const char **key, const u8 *data) db_exec_prepared_v2(take(stmt)); } -void wallet_datastore_create(struct wallet *w, const char **key, const u8 *data) +static void db_datastore_create(struct db *db, const char **key, const u8 *data) { struct db_stmt *stmt; - stmt = db_prepare_v2(w->db, + stmt = db_prepare_v2(db, SQL("INSERT INTO datastore VALUES (?, ?, 0);")); db_bind_datastore_key(stmt, key); @@ -6218,6 +6287,11 @@ void wallet_datastore_create(struct wallet *w, const char **key, const u8 *data) db_exec_prepared_v2(take(stmt)); } +void wallet_datastore_create(struct wallet *w, const char **key, const u8 *data) +{ + db_datastore_create(w->db, key, data); +} + static void db_datastore_remove(struct db *db, const char **key) { struct db_stmt *stmt; @@ -6228,6 +6302,40 @@ static void db_datastore_remove(struct db *db, const char **key) db_exec_prepared_v2(take(stmt)); } +void wallet_datastore_save_utxo_description(struct db *db, + const struct bitcoin_outpoint *outpoint, + const char *desc) +{ + const char **key; + + key = tal_arr(tmpctx, const char *, 4); + key[0] = "bookkeeper"; + key[1] = "description"; + key[2] = "utxo"; + key[3] = fmt_bitcoin_outpoint(key, outpoint); + + /* In case it's a duplicate, remove first */ + db_datastore_remove(db, key); + db_datastore_create(db, key, tal_dup_arr(key, u8, (u8 *)desc, strlen(desc), 0)); +} + +void wallet_datastore_save_payment_description(struct db *db, + const struct sha256 *payment_hash, + const char *desc) +{ + const char **key; + + key = tal_arr(tmpctx, const char *, 4); + key[0] = "bookkeeper"; + key[1] = "description"; + key[2] = "payment"; + key[3] = fmt_sha256(key, payment_hash); + + /* In case it's a duplicate, remove first */ + db_datastore_remove(db, key); + db_datastore_create(db, key, tal_dup_arr(key, u8, (u8 *)desc, strlen(desc), 0)); +} + void wallet_datastore_remove(struct wallet *w, const char **key) { db_datastore_remove(w->db, key); @@ -6805,6 +6913,453 @@ struct issued_address_type *wallet_list_addresses(const tal_t *ctx, struct walle return addresseslist; } +void db_bind_credit_debit(struct db_stmt *stmt, + struct amount_msat credit, + struct amount_msat debit) +{ + if (amount_msat_is_zero(debit)) + db_bind_amount_msat(stmt, credit); + else { + s64 debit_msats = debit.millisatoshis; /* Raw: negating */ + assert(amount_msat_is_zero(credit)); + db_bind_s64(stmt, -debit_msats); + } +} + +void db_bind_mvt_account_id(struct db_stmt *stmt, + struct db *db, + const struct mvt_account_id *account) +{ + if (account->channel) { + db_bind_u64(stmt, account->channel->dbid); + db_bind_null(stmt); + } else { + db_bind_null(stmt); + db_bind_u64(stmt, move_accounts_id(db, account->alt_account)); + } +} + +void db_bind_mvt_tags(struct db_stmt *stmt, struct mvt_tags tags) +{ + assert(mvt_tags_valid(tags)); + db_bind_u64(stmt, tags.bits); +} + +static u64 insert_channel_mvt(struct lightningd *ld, + struct db *db, + const struct channel_coin_mvt *chan_mvt) +{ + struct db_stmt *stmt; + u64 id; + + stmt = db_prepare_v2(db, + SQL("INSERT INTO channel_moves (" + " id," + " account_channel_id," + " account_nonchannel_id," + " credit_or_debit," + " tag_bitmap," + " timestamp," + " payment_hash," + " payment_part_id," + " payment_group_id," + " fees) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?);")); + id = channel_mvt_index_created(ld, db, + &chan_mvt->account, + chan_mvt->credit, chan_mvt->debit); + db_bind_u64(stmt, id); + db_bind_mvt_account_id(stmt, db, &chan_mvt->account); + db_bind_credit_debit(stmt, chan_mvt->credit, chan_mvt->debit); + db_bind_mvt_tags(stmt, chan_mvt->tags); + db_bind_u64(stmt, chan_mvt->timestamp); + /* push funding / leases don't have a payment_hash */ + if (chan_mvt->payment_hash) + db_bind_sha256(stmt, chan_mvt->payment_hash); + else + db_bind_null(stmt); + if (chan_mvt->part_and_group) { + db_bind_u64(stmt, chan_mvt->part_and_group->part_id); + db_bind_u64(stmt, chan_mvt->part_and_group->group_id); + } else { + db_bind_null(stmt); + db_bind_null(stmt); + } + db_bind_amount_msat(stmt, chan_mvt->fees); + db_exec_prepared_v2(take(stmt)); + + notify_channel_mvt(ld, chan_mvt, id); + if (taken(chan_mvt)) + tal_free(chan_mvt); + + return id; +} + +void wallet_save_channel_mvt(struct lightningd *ld, + const struct channel_coin_mvt *chan_mvt) +{ + insert_channel_mvt(ld, ld->wallet->db, chan_mvt); +} + +static u64 insert_chain_mvt(struct lightningd *ld, + struct db *db, + const struct chain_coin_mvt *chain_mvt) +{ + struct db_stmt *stmt; + u64 id; + + stmt = db_prepare_v2(db, + SQL("INSERT INTO chain_moves (" + " id," + " account_channel_id," + " account_nonchannel_id," + " tag_bitmap," + " credit_or_debit," + " timestamp," + " utxo," + " spending_txid," + " peer_id," + " payment_hash," + " block_height," + " output_sat," + " originating_channel_id," + " originating_nonchannel_id," + " output_count) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)")); + id = chain_mvt_index_created(ld, db, + &chain_mvt->account, + chain_mvt->credit, chain_mvt->debit); + db_bind_u64(stmt, id); + db_bind_mvt_account_id(stmt, db, &chain_mvt->account); + db_bind_mvt_tags(stmt, chain_mvt->tags); + db_bind_credit_debit(stmt, chain_mvt->credit, chain_mvt->debit); + db_bind_u64(stmt, chain_mvt->timestamp); + db_bind_outpoint(stmt, &chain_mvt->outpoint); + if (chain_mvt->spending_txid) + db_bind_txid(stmt, chain_mvt->spending_txid); + else + db_bind_null(stmt); + if (chain_mvt->peer_id) + db_bind_node_id(stmt, chain_mvt->peer_id); + else + db_bind_null(stmt); + if (chain_mvt->payment_hash) + db_bind_sha256(stmt, chain_mvt->payment_hash); + else + db_bind_null(stmt); + db_bind_int(stmt, chain_mvt->blockheight); + db_bind_amount_sat(stmt, chain_mvt->output_val); + if (chain_mvt->originating_acct) { + db_bind_mvt_account_id(stmt, db, chain_mvt->originating_acct); + } else { + db_bind_null(stmt); + db_bind_null(stmt); + } + if (chain_mvt->output_count > 0) + db_bind_int(stmt, chain_mvt->output_count); + else + db_bind_null(stmt); + db_exec_prepared_v2(take(stmt)); + return id; +} + +void wallet_save_chain_mvt(struct lightningd *ld, + const struct chain_coin_mvt *chain_mvt) +{ + struct db_stmt *stmt; + u64 id; + + /* On restart, we do chain replay. For this (and other + * reorgs) we need to de-duplicate here. The other db tables + * do this by deleting old entries on reorg, but we never + * delete. */ + if (chain_mvt->account.channel) { + stmt = db_prepare_v2(ld->wallet->db, + SQL("SELECT" + " cm.spending_txid, cm.tag_bitmap, cm.id" + " FROM chain_moves cm" + " WHERE " + " account_channel_id = ?" + " AND utxo = ?")); + db_bind_u64(stmt, chain_mvt->account.channel->dbid); + } else { + stmt = db_prepare_v2(ld->wallet->db, + SQL("SELECT" + " cm.spending_txid, cm.tag_bitmap, cm.id" + " FROM chain_moves cm" + " JOIN move_accounts ma ON cm.account_nonchannel_id = ma.id" + " WHERE" + " ma.name = ?" + " AND utxo = ?")); + db_bind_text(stmt, chain_mvt->account.alt_account); + } + db_bind_outpoint(stmt, &chain_mvt->outpoint); + db_query_prepared(stmt); + + /* Check that spending_txid and primary_tag match. We could + * probably just match on spending_txid, but this is robust. */ + while (db_step(stmt)) { + struct mvt_tags tags; + + /* Access this now so it never complains we don't */ + tags.bits = db_col_u64(stmt, "cm.tag_bitmap"); + id = db_col_u64(stmt, "cm.id"); + + /* spending_txid must match */ + if (chain_mvt->spending_txid) { + struct bitcoin_txid txid; + if (db_col_is_null(stmt, "cm.spending_txid")) + continue; + db_col_txid(stmt, "cm.spending_txid", &txid); + /* This would only happen for reorgs */ + if (!bitcoin_txid_eq(&txid, chain_mvt->spending_txid)) + continue; + } else { + if (!db_col_is_null(stmt, "cm.spending_txid")) + continue; + } + + /* Tags must match */ + if (primary_mvt_tag(tags) != primary_mvt_tag(chain_mvt->tags)) + continue; + + /* It's a duplicate. Don't re-add. */ + tal_free(stmt); + goto out; + } + tal_free(stmt); + + id = insert_chain_mvt(ld, ld->wallet->db, chain_mvt), + notify_chain_mvt(ld, chain_mvt, id); +out: + if (taken(chain_mvt)) + tal_free(chain_mvt); +} + +static void db_cols_account(struct db_stmt *stmt, + struct lightningd *ld, + const char *channel_colname, + const char *nonnonchannel_colname, + struct mvt_account_id *account) +{ + if (db_col_is_null(stmt, channel_colname)) { + account->channel = NULL; + account->alt_account = db_col_strdup(account, stmt, nonnonchannel_colname); + } else { + account->alt_account = NULL; + db_col_ignore(stmt, nonnonchannel_colname); + account->channel = channel_by_dbid(ld, db_col_u64(stmt, channel_colname)); + assert(account->channel); + } +} + +static void db_col_credit_or_debit(struct db_stmt *stmt, + const char *colname, + struct amount_msat *credit, + struct amount_msat *debit) +{ + s64 amount = db_col_s64(stmt, colname); + if (amount < 0) { + *credit = AMOUNT_MSAT(0); + *debit = amount_msat(-amount); + } else { + *credit = amount_msat(amount); + *debit = AMOUNT_MSAT(0); + } +} + +static struct mvt_tags db_col_mvt_tags(struct db_stmt *stmt, + const char *colname) +{ + struct mvt_tags tags; + tags.bits = db_col_u64(stmt, colname); + assert(mvt_tags_valid(tags)); + return tags; +} + +struct chain_coin_mvt *wallet_chain_move_extract(const tal_t *ctx, + struct db_stmt *stmt, + struct lightningd *ld, + u64 *id) +{ + struct chain_coin_mvt *chain_mvt = tal(ctx, struct chain_coin_mvt); + + *id = db_col_u64(stmt, "chain_moves.id"); + db_cols_account(stmt, ld, + "account_channel_id", "ma1.name", + &chain_mvt->account); + chain_mvt->tags = db_col_mvt_tags(stmt, "tag_bitmap"); + db_col_credit_or_debit(stmt, "credit_or_debit", + &chain_mvt->credit, + &chain_mvt->debit); + chain_mvt->timestamp = db_col_u64(stmt, "timestamp"); + db_col_outpoint(stmt, "utxo", &chain_mvt->outpoint); + + if (db_col_is_null(stmt, "spending_txid")) + chain_mvt->spending_txid = NULL; + else { + /* Need non-const for db_col_txid */ + struct bitcoin_txid *txid; + chain_mvt->spending_txid = txid = tal(chain_mvt, struct bitcoin_txid); + db_col_txid(stmt, "spending_txid", txid); + } + if (db_col_is_null(stmt, "peer_id")) + chain_mvt->peer_id = NULL; + else { + /* Need non-const temporary */ + struct node_id *peer_id; + chain_mvt->peer_id = peer_id = tal(chain_mvt, struct node_id); + db_col_node_id(stmt, "peer_id", peer_id); + } + if (db_col_is_null(stmt, "payment_hash")) + chain_mvt->payment_hash = NULL; + else { + /* Need non-const temporary */ + struct sha256 *ph; + chain_mvt->payment_hash = ph = tal(chain_mvt, struct sha256); + db_col_sha256(stmt, "payment_hash", ph); + } + chain_mvt->blockheight = db_col_int(stmt, "block_height"); + if (db_col_is_null(stmt, "originating_channel_id") && db_col_is_null(stmt, "ma2.name")) { + chain_mvt->originating_acct = NULL; + } else { + /* Need non-const temporary */ + struct mvt_account_id *acct; + chain_mvt->originating_acct = acct = tal(chain_mvt, struct mvt_account_id); + db_cols_account(stmt, ld, + "originating_channel_id", "ma2.name", + acct); + } + chain_mvt->output_val = db_col_amount_sat(stmt, "output_sat"); + if (db_col_is_null(stmt, "output_count")) + chain_mvt->output_count = 0; + else + chain_mvt->output_count = db_col_int(stmt, "output_count"); + + return chain_mvt; +} + +struct db_stmt *wallet_chain_moves_first(struct wallet *wallet, + u64 liststart, + u32 *listlimit) +{ + struct db_stmt *stmt = db_prepare_v2(wallet->db, + SQL("SELECT" + " chain_moves.id" + ", account_channel_id" + ", ma1.name" + ", tag_bitmap" + ", credit_or_debit" + ", timestamp" + ", utxo" + ", spending_txid" + ", peer_id" + ", payment_hash" + ", block_height" + ", output_sat" + ", originating_channel_id" + ", ma2.name" + ", output_count" + " FROM chain_moves" + " LEFT JOIN" + " move_accounts ma1 ON account_nonchannel_id = ma1.id" + " LEFT JOIN" + " move_accounts ma2 ON originating_nonchannel_id = ma2.id" + " WHERE chain_moves.id >= ?" + " ORDER BY chain_moves.id" + " LIMIT ?;")); + db_bind_u64(stmt, liststart); + if (listlimit) + db_bind_int(stmt, *listlimit); + else + db_bind_int(stmt, INT_MAX); + db_query_prepared(stmt); + return wallet_chain_moves_next(wallet, stmt); +} + +struct db_stmt *wallet_chain_moves_next(struct wallet *wallet, struct db_stmt *stmt) +{ + if (!db_step(stmt)) + return tal_free(stmt); + + return stmt; +} + +struct channel_coin_mvt *wallet_channel_move_extract(const tal_t *ctx, + struct db_stmt *stmt, + struct lightningd *ld, + u64 *id) +{ + struct channel_coin_mvt *chan_mvt = tal(ctx, struct channel_coin_mvt); + + *id = db_col_u64(stmt, "channel_moves.id"); + db_cols_account(stmt, ld, + "account_channel_id", "ma.name", + &chan_mvt->account); + db_col_credit_or_debit(stmt, "credit_or_debit", + &chan_mvt->credit, + &chan_mvt->debit); + chan_mvt->tags = db_col_mvt_tags(stmt, "tag_bitmap"); + chan_mvt->timestamp = db_col_u64(stmt, "timestamp"); + if (db_col_is_null(stmt, "payment_hash")) + chan_mvt->payment_hash = NULL; + else { + /* Need non-const temporary */ + struct sha256 *ph; + chan_mvt->payment_hash = ph = tal(chan_mvt, struct sha256); + db_col_sha256(stmt, "payment_hash", ph); + } + if (db_col_is_null(stmt, "payment_part_id")) { + db_col_ignore(stmt, "payment_group_id"); + chan_mvt->part_and_group = NULL; + } else { + /* Non-const temporary */ + struct channel_coin_mvt_id *pandg; + chan_mvt->part_and_group = pandg = tal(chan_mvt, struct channel_coin_mvt_id); + pandg->part_id = db_col_u64(stmt, "payment_part_id"); + pandg->group_id = db_col_u64(stmt, "payment_group_id"); + } + chan_mvt->fees = db_col_amount_msat(stmt, "fees"); + return chan_mvt; +} + +struct db_stmt *wallet_channel_moves_first(struct wallet *wallet, + u64 liststart, + u32 *listlimit) +{ + struct db_stmt *stmt = db_prepare_v2(wallet->db, + SQL("SELECT" + " channel_moves.id" + ", account_channel_id" + ", ma.name" + ", credit_or_debit" + ", tag_bitmap" + ", timestamp" + ", payment_hash" + ", payment_part_id" + ", payment_group_id" + ", fees" + " FROM channel_moves" + " LEFT JOIN" + " move_accounts ma ON account_nonchannel_id = ma.id" + " WHERE channel_moves.id >= ?" + " ORDER BY channel_moves.id" + " LIMIT ?;")); + db_bind_u64(stmt, liststart); + if (listlimit) + db_bind_int(stmt, *listlimit); + else + db_bind_int(stmt, INT_MAX); + db_query_prepared(stmt); + return wallet_channel_moves_next(wallet, stmt); +} + +struct db_stmt *wallet_channel_moves_next(struct wallet *wallet, struct db_stmt *stmt) +{ + if (!db_step(stmt)) + return tal_free(stmt); + + return stmt; +} + struct missing { size_t num_found; struct missing_addr *addrs; @@ -6967,3 +7522,135 @@ void wallet_begin_old_close_rescan(struct lightningd *ld) bitcoind_getrawblockbyheight(missing, ld->topology->bitcoind, earliest_block, mutual_close_p2pkh_catch, missing); } + +/* An existing node without accounting. Fill in what we have so far. */ +void migrate_setup_coinmoves(struct lightningd *ld, struct db *db) +{ + struct utxo **utxos = db_get_unspent_utxos(tmpctx, db); + struct db_stmt *stmt; + u64 base_timestamp = time_now().ts.tv_sec - 2; + + for (size_t i = 0; i < tal_count(utxos); i++) { + struct chain_coin_mvt *mvt; + + /* Only confirmed ones */ + if (!utxos[i]->blockheight) + continue; + mvt = new_coin_wallet_deposit(tmpctx, + &utxos[i]->outpoint, + *utxos[i]->blockheight, + utxos[i]->amount, + mk_mvt_tags(MVT_DEPOSIT)); + insert_chain_mvt(ld, db, mvt); + } + + /* Now channels. We create the open event, and then pushed/leased, + * the finally fixup with a journal entry. */ + stmt = db_prepare_v2(db, SQL("SELECT" + " p.node_id" + ", scid" + ", full_channel_id" + ", funding_tx_id" + ", funding_tx_outnum" + ", funder" + ", push_msatoshi" + ", lease_commit_sig" + ", funding_satoshi" + ", our_funding_satoshi" + ", msatoshi_local" + " FROM channels c" + " JOIN peers p ON c.peer_id = p.id" + " WHERE c.scid IS NOT NULL" + " AND c.state != ?;")); + db_bind_int(stmt, CLOSED); + db_query_prepared(stmt); + + while (db_step(stmt)) { + struct chain_coin_mvt *mvt; + struct bitcoin_outpoint funding; + struct channel_id cid; + struct node_id peerid; + struct amount_sat funding_sat, our_funding_sat; + struct amount_msat start_balance, our_msat, push_msat; + struct short_channel_id scid; + enum side opener; + bool is_leased; + + db_col_node_id(stmt, "p.node_id", &peerid); + scid = db_col_short_channel_id(stmt, "scid"); + db_col_txid(stmt, "funding_tx_id", &funding.txid); + funding.n = db_col_int(stmt, "funding_tx_outnum"); + db_col_channel_id(stmt, "full_channel_id", &cid); + opener = db_col_int(stmt, "funder"); + funding_sat = db_col_amount_sat(stmt, "funding_satoshi"); + our_funding_sat = db_col_amount_sat(stmt, "our_funding_satoshi"); + push_msat = db_col_amount_msat(stmt, "push_msatoshi"); + is_leased = !db_col_is_null(stmt, "lease_commit_sig"); + our_msat = db_col_amount_msat(stmt, "msatoshi_local"); + + /* If funds were pushed, add/sub them from the starting balance */ + if (opener == LOCAL) { + if (!amount_sat_sub_msat(&start_balance, + our_funding_sat, push_msat)) + abort(); + } else { + if (!amount_msat_add_sat(&start_balance, + push_msat, our_funding_sat)) + abort(); + } + mvt = new_coin_channel_open_general(tmpctx, + NULL, + &cid, + /* We ensure strict ordering of events */ + base_timestamp, + &funding, + &peerid, + short_channel_id_blocknum(scid), + start_balance, + funding_sat, + opener == LOCAL, + is_leased); + insert_chain_mvt(ld, db, mvt); + + /* If we pushed, mark that transfer in channel. */ + if (!amount_msat_is_zero(push_msat)) { + struct channel_coin_mvt *chan_mvt; + + chan_mvt = new_coin_channel_push_general(tmpctx, + NULL, + &cid, + base_timestamp + 1, + opener == REMOTE ? COIN_CREDIT : COIN_DEBIT, + push_msat, + is_leased + ? mk_mvt_tags(MVT_LEASE_FEE) + : mk_mvt_tags(MVT_PUSHED)); + insert_channel_mvt(ld, db, chan_mvt); + } + + /* If our funds are not exactly what we expect, journal entry to adjust */ + if (!amount_msat_eq(our_msat, start_balance)) { + struct amount_msat diff; + enum coin_mvt_dir direction; + struct channel_coin_mvt *chan_mvt; + if (amount_msat_sub(&diff, our_msat, start_balance)) + direction = COIN_CREDIT; + else { + if (!amount_msat_sub(&diff, start_balance, our_msat)) + abort(); + direction = COIN_DEBIT; + } + + chan_mvt = new_channel_coin_mvt_general(tmpctx, NULL, &cid, + base_timestamp + 2, + NULL, NULL, NULL, + direction, + diff, + mk_mvt_tags(MVT_JOURNAL), + AMOUNT_MSAT(0)); + insert_channel_mvt(ld, db, chan_mvt); + } + } + tal_free(stmt); +} + diff --git a/wallet/wallet.h b/wallet/wallet.h index 8788a0140365..584f17b03076 100644 --- a/wallet/wallet.h +++ b/wallet/wallet.h @@ -1873,6 +1873,53 @@ struct issued_address_type *wallet_list_addresses(const tal_t *ctx, struct walle */ void wallet_begin_old_close_rescan(struct lightningd *ld); +/* Coin movement storage: also calls notifications */ +void wallet_save_channel_mvt(struct lightningd *ld, + const struct channel_coin_mvt *chan_mvt); + +void wallet_save_chain_mvt(struct lightningd *ld, + const struct chain_coin_mvt *chain_mvt); + +/* coin movement table iterators */ +struct db_stmt *wallet_chain_moves_first(struct wallet *wallet, + u64 liststart, + u32 *listlimit); +struct db_stmt *wallet_chain_moves_next(struct wallet *wallet, + struct db_stmt *stmt); + +struct chain_coin_mvt *wallet_chain_move_extract(const tal_t *ctx, + struct db_stmt *stmt, + struct lightningd *ld, + u64 *id); + +struct db_stmt *wallet_channel_moves_first(struct wallet *wallet, + u64 liststart, + u32 *listlimit); +struct db_stmt *wallet_channel_moves_next(struct wallet *wallet, + struct db_stmt *stmt); + +struct channel_coin_mvt *wallet_channel_move_extract(const tal_t *ctx, + struct db_stmt *stmt, + struct lightningd *ld, + u64 *id); + +/* For bookkeeper migration */ +void db_bind_mvt_tags(struct db_stmt *stmt, struct mvt_tags tags); +void db_bind_mvt_account_id(struct db_stmt *stmt, + struct db *db, + const struct mvt_account_id *account); +void db_bind_credit_debit(struct db_stmt *stmt, + struct amount_msat credit, + struct amount_msat debit); +u64 move_accounts_id(struct db *db, const char *name); +void wallet_datastore_save_utxo_description(struct db *db, + const struct bitcoin_outpoint *outpoint, + const char *desc); +void wallet_datastore_save_payment_description(struct db *db, + const struct sha256 *payment_hash, + const char *desc); +void migrate_setup_coinmoves(struct lightningd *ld, struct db *db); + /** * wallet_memleak_scan - Check for memleaks in wallet. */ diff --git a/wallet/walletrpc.c b/wallet/walletrpc.c index 282ecea2969e..677db8bab66f 100644 --- a/wallet/walletrpc.c +++ b/wallet/walletrpc.c @@ -950,8 +950,7 @@ static void maybe_notify_new_external_send(struct lightningd *ld, mvt->originating_acct = new_mvt_account_id(mvt, NULL, ACCOUNT_NAME_WALLET); - notify_chain_mvt(ld, mvt); - tal_free(mvt); + wallet_save_chain_mvt(ld, take(mvt)); }