diff --git a/README.md b/README.md index 794d1a3db68a..2e717e6cefeb 100644 --- a/README.md +++ b/README.md @@ -205,6 +205,13 @@ If you encrypt your `hsm_secret`, you will have to pass the `--encrypted-hsm` st Developers wishing to contribute should start with the developer guide [here](doc/contribute-to-core-lightning/coding-style-guidelines.md). +### RGB Support + +This is an experiemental branch that implement the RGB support in core lightning. + +It is still a reseach project, so if you would like to help or look inside +the work that it is currently done, please run `git grep -n "TODO(bitfinix):"` + [blockstream-store-blog]: https://blockstream.com/2018/01/16/en-lightning-charge/ [std]: https://github.com/lightning/bolts [prs]: https://img.shields.io/badge/PRs-welcome-brightgreen.svg?style=flat diff --git a/bitcoin/test/run-psbt-from-tx.c b/bitcoin/test/run-psbt-from-tx.c index 3c71675e55ae..f537a011e4e4 100644 --- a/bitcoin/test/run-psbt-from-tx.c +++ b/bitcoin/test/run-psbt-from-tx.c @@ -112,6 +112,23 @@ int main(int argc, char *argv[]) assert(final_scriptsig->value_len > 0); assert(tx2->psbt->inputs[0].final_witness != NULL); + char *hex_tx = "0200000001882d49b841cb341defb60509f5863b163d32d1a819545761d31420cbf9db94d60000000000f8858a80024a01000000000000220020b6d8d2f575683f606ece57e33f59eaa146152f5c7163fb3e917b3cd19b6a558038073ba40b00000022002076f0b18b3c072222fd000432a309620dd43dfb776ab3fd710c9bda5efb0235f49c49c120"; + tx = bitcoin_tx_from_hex(tmpctx, hex_tx, strlen(hex_tx)); + psbt_input_get_amount(tx->psbt, 0); + + msg = tal_arr(tmpctx, u8, 0); + /* convert to wire format */ + towire_bitcoin_tx(&msg, tx); + + len = tal_bytelen(msg); + assert(len > 0); + + tx2 = fromwire_bitcoin_tx(tmpctx, + cast_const2(const u8 **, &msg), &len); + assert(tx2 != NULL); + + psbt_input_get_amount(tx2->psbt, 0); + common_shutdown(); return 0; } diff --git a/bitcoin/test/run-tx-encode.c b/bitcoin/test/run-tx-encode.c index b28f3191c00c..98e83fb81731 100644 --- a/bitcoin/test/run-tx-encode.c +++ b/bitcoin/test/run-tx-encode.c @@ -9,6 +9,7 @@ #include #include #include +#include /* AUTOGENERATED MOCKS START */ /* Generated stub for amount_asset_is_main */ @@ -125,6 +126,7 @@ int main(int argc, const char *argv[]) chainparams = chainparams_for_network("bitcoin"); struct bitcoin_tx *tx; + char *hex_tx; tx = bitcoin_tx_from_hex(NULL, extended_tx, strlen(extended_tx)); assert(tx); @@ -161,6 +163,10 @@ int main(int argc, const char *argv[]) "0204096eb817f7efb414ef4d3d8be39dd04374256d3b054a322d4a6ee22736d0" "3b"); + hex_tx = "020000000180e1806045e74fb8fe0356e6568fe723c2755e54563b2e8df2f7e99202c3d896000000000029635b80024a01000000000000220020ac0192675893119abba0193b6b52dc188d6d1032078903890e72a676ffa237df4294d5e829000000220020ad270f8b9c3719fafeaa3be48aebaeac2a953a1ef658f3d55e0d0439b51df508648d1a20"; + tx = bitcoin_tx_from_hex(NULL, hex_tx, strlen(hex_tx)); + assert(strcmp(hex_tx, fmt_bitcoin_tx(NULL, tx)) == 0); + tal_free(tx); common_shutdown(); return 0; diff --git a/bitcoin/tx.c b/bitcoin/tx.c index 4245fda3b932..0df3671f0943 100644 --- a/bitcoin/tx.c +++ b/bitcoin/tx.c @@ -430,6 +430,7 @@ static bool uses_witness(const struct wally_tx *wtx) { size_t i; + assert(wtx); for (i = 0; i < wtx->num_inputs; i++) { if (wtx->inputs[i].witness) return true; diff --git a/common/bolt11.h b/common/bolt11.h index e802fc801d4a..506a9a4bb94d 100644 --- a/common/bolt11.h +++ b/common/bolt11.h @@ -80,6 +80,11 @@ struct bolt11 { u8 *metadata; struct list_head extra_fields; + + // FIXME(vincenzopalazzo): adds rgb information + // + // 1. `rgb_amt` + // 2. `rgb_contract_id` }; /* Decodes and checks signature; returns NULL on error; description is diff --git a/common/bolt11_json.c b/common/bolt11_json.c index c2e7f225d22c..05bd8c4cccd5 100644 --- a/common/bolt11_json.c +++ b/common/bolt11_json.c @@ -121,4 +121,9 @@ void json_add_bolt11(struct json_stream *response, json_add_string(response, "signature", fmt_secp256k1_ecdsa_signature(tmpctx, &b11->sig)); + + // FIXME(vincenzopalazzo): adding tag RGB information + // + // 1. Adds `rgb_amt` + // 2. Adds `rgb_contract_id` } diff --git a/common/json_parse.c b/common/json_parse.c index 9fc9882e22e7..39e4702eed0c 100644 --- a/common/json_parse.c +++ b/common/json_parse.c @@ -581,6 +581,15 @@ bool json_to_txid(const char *buffer, const jsmntok_t *tok, tok->end - tok->start, txid); } +bool json_to_tx(const char *buffer, const jsmntok_t *tok, + struct bitcoin_tx **tx) +{ + *tx = bitcoin_tx_from_hex(buffer, buffer + tok->start, + tok->end - tok->start); + return *tx != NULL; +} + + bool json_to_outpoint(const char *buffer, const jsmntok_t *tok, struct bitcoin_outpoint *op) { diff --git a/common/json_parse.h b/common/json_parse.h index 1569a4a4e56e..d6ee5e54913c 100644 --- a/common/json_parse.h +++ b/common/json_parse.h @@ -7,6 +7,7 @@ /* Simple helpers are here: this file contains heavier ones */ #include #include +#include struct json_escape; struct json_stream; @@ -102,6 +103,10 @@ bool json_to_msat(const char *buffer, const jsmntok_t *tok, bool json_to_txid(const char *buffer, const jsmntok_t *tok, struct bitcoin_txid *txid); +/* Extract a bitcoin tx from this */ +bool json_to_tx(const char *buffer, const jsmntok_t *tok, + struct bitcoin_tx **tx); + /* Extract a bitcoin outpoint from this */ bool json_to_outpoint(const char *buffer, const jsmntok_t *tok, struct bitcoin_outpoint *op); diff --git a/common/test/run-json_scan.c b/common/test/run-json_scan.c index 46ccee8e9f99..910684ab34fe 100644 --- a/common/test/run-json_scan.c +++ b/common/test/run-json_scan.c @@ -1,8 +1,11 @@ #include "config.h" #include "../json_parse.c" #include "../json_parse_simple.c" +#include #include #include +#include +#include #include /* AUTOGENERATED MOCKS START */ @@ -244,5 +247,21 @@ int main(int argc, char *argv[]) assert(toks); assert(toks->size == 4); + char *hex_tx = "020000000180e1806045e74fb8fe0356e6568fe723c2755e54563b2e8df2f7e99202c3d896000000000029635b80024a01000000000000220020ac0192675893119abba0193b6b52dc188d6d1032078903890e72a676ffa237df4294d5e829000000220020ad270f8b9c3719fafeaa3be48aebaeac2a953a1ef658f3d55e0d0439b51df508648d1a20"; + buf = tal_fmt(tmpctx, "{\"tx\":\"%s\"}", hex_tx); + toks = json_parse_simple(tmpctx, buf, strlen(buf)); + toks = json_get_member(buf, toks, "tx"); + assert(toks->end - toks->start == strlen(hex_tx)); + + chainparams = chainparams_for_network("bitcoin"); + struct bitcoin_tx *tx = bitcoin_tx_from_hex(NULL, hex_tx, strlen(hex_tx)); + assert(tx); + assert(strcmp(hex_tx, fmt_bitcoin_tx(NULL, tx)) == 0); + + tx = NULL; + assert(json_to_tx(buf, toks, &tx)); + assert(tx); + assert(strcmp(hex_tx, fmt_bitcoin_tx(NULL, tx)) == 0); + common_shutdown(); } diff --git a/lightningd/invoice.c b/lightningd/invoice.c index ea8f60fc0d6b..33c1c53e87f9 100644 --- a/lightningd/invoice.c +++ b/lightningd/invoice.c @@ -840,6 +840,11 @@ invoice_complete(struct invoice_info *info, "preimage already used"); } + // FIXME(vincenzopalazzo): to support create rgb lightning invoice we should add: + // + // 1: `rgb_amount` tag that shows the rgb amount on the chain + // 2: `rgb_contract_id` tag that fix the invoice from a specific + // RGB assert. if (!invoices_create(wallet->invoices, &inv_dbid, info->b11->msat, diff --git a/lightningd/opening_control.c b/lightningd/opening_control.c index 0406f57da61a..d15d489a4d03 100644 --- a/lightningd/opening_control.c +++ b/lightningd/opening_control.c @@ -676,7 +676,7 @@ static void openchannel_hook_serialize(struct openchannel_hook_payload *payload, /* openingd dies? Remove openingd ptr from payload */ static void openchannel_payload_remove_openingd(struct subd *openingd, - struct openchannel_hook_payload *payload) + struct openchannel_hook_payload *payload) { assert(payload->openingd == openingd); payload->openingd = NULL; @@ -860,6 +860,94 @@ static void opening_got_offer(struct subd *openingd, plugin_hook_call_openchannel(openingd->ld, NULL, payload); } +struct onfunding_channel_tx_hook_payload { + struct subd *openingd; + struct bitcoin_tx *tx; + struct channel_id *cid; +}; + +static void onfunding_channel_tx_hook_final(struct onfunding_channel_tx_hook_payload *payload STEALS) +{ + struct subd *openingd = payload->openingd; + log_info(openingd->log, "from hook sending the tx %s", fmt_bitcoin_tx(tmpctx, payload->tx)); + // FIXME(bitfinix): manage the errors! + subd_send_msg(openingd, + take(towire_openingd_on_funding_tx_reply(NULL, payload->tx))); +} + +static void onfunding_channel_tx_hook_serialize(struct onfunding_channel_tx_hook_payload *payload, + struct json_stream *stream, + struct plugin *plugin) +{ + struct bitcoin_txid txid; + + bitcoin_txid(payload->tx, &txid); + json_object_start(stream, "onfunding_channel_tx"); + json_add_tx(stream, "tx", payload->tx); + json_add_txid(stream, "txid", &txid); + json_add_psbt(stream, "psbt", payload->tx->psbt); + json_add_channel_id(stream, "channel_id", payload->cid); + json_object_end(stream); +} + +static bool onfunding_channel_tx_hook_deserialize(struct onfunding_channel_tx_hook_payload *payload, + const char *buffer, + const jsmntok_t *toks) +{ + const jsmntok_t *result_tok, *error_tok, + *tx_tok, *psbt_tok; + + if ((error_tok = json_get_member(buffer, toks, "error")) != NULL) + fatal("Plugin returned an error inside the response to the" + " onfunding_channel_tx hook: %.*s", + toks[0].end - toks[0].start, buffer + toks[0].start); + + if ((result_tok = json_get_member(buffer, toks, "result")) == NULL) + fatal("Plugin returned an invalid response (missing result) to the" + " onfunding_channel_tx hook: %.*s", + toks[0].end - toks[0].start, buffer + toks[0].start); + + if ((tx_tok = json_get_member(buffer, result_tok, "tx")) == NULL) + fatal("Plugin returned an invalid response (missing tx) to the" + " onfunding_channel_tx hook: %.*s", + toks[0].end - toks[0].start, buffer + toks[0].start); + + if ((psbt_tok = json_get_member(buffer, result_tok, "psbt")) == NULL) + fatal("Plugin returned an invalid response (missing psbt) to the" + " onfunding_channel_tx hook: %.*s", + toks[0].end - toks[0].start, buffer + toks[0].start); + + if (!json_to_tx(buffer, tx_tok, &payload->tx)) + fatal("Plugin returned an invalid (json to tx) response to the" + " onfunding_channel_tx hook: %.*s", + tx_tok[0].end - tx_tok[0].start, buffer + tx_tok[0].start); + + payload->tx->psbt = json_to_psbt(buffer, buffer, psbt_tok); + return true; +} + +REGISTER_PLUGIN_HOOK(onfunding_channel_tx, + onfunding_channel_tx_hook_deserialize, + onfunding_channel_tx_hook_final, + onfunding_channel_tx_hook_serialize, + struct onfunding_channel_tx_hook_payload *); + + +static char *opening_on_funding_tx(struct subd *openingd, const u8 *msg) +{ + struct onfunding_channel_tx_hook_payload *payload; + payload = tal(openingd, struct onfunding_channel_tx_hook_payload); + payload->cid = tal(payload, struct channel_id); + payload->tx = tal(payload, struct bitcoin_tx); + payload->openingd = openingd; + + if (!fromwire_openingd_on_funding_tx(msg, msg, &payload->tx, payload->cid)) + return tal_fmt(tmpctx, "Unexpected encoding of openingd_on_funding_tx msg: %s", + tal_hex(tmpctx, msg)); + plugin_hook_call_onfunding_channel_tx(openingd->ld, NULL, payload); + return NULL; +} + static unsigned int openingd_msg(struct subd *openingd, const u8 *msg, const int *fds) { @@ -900,13 +988,20 @@ static unsigned int openingd_msg(struct subd *openingd, case WIRE_OPENINGD_GOT_OFFER: opening_got_offer(openingd, msg, uc); return 0; - + case WIRE_OPENINGD_ON_FUNDING_TX: + const char *err; + if ((err = opening_on_funding_tx(openingd, msg)) != NULL) { + log_debug(openingd->log, "Unexpected error while handling on funding tx: %s", err); + return 1; + } + return 0; /* We send these! */ case WIRE_OPENINGD_INIT: case WIRE_OPENINGD_FUNDER_START: case WIRE_OPENINGD_FUNDER_COMPLETE: case WIRE_OPENINGD_FUNDER_CANCEL: case WIRE_OPENINGD_GOT_OFFER_REPLY: + case WIRE_OPENINGD_ON_FUNDING_TX_REPLY: case WIRE_OPENINGD_DEV_MEMLEAK: /* Replies never get here */ case WIRE_OPENINGD_DEV_MEMLEAK_REPLY: diff --git a/openingd/openingd.c b/openingd/openingd.c index 447a0381f573..7a7db43289f5 100644 --- a/openingd/openingd.c +++ b/openingd/openingd.c @@ -8,6 +8,7 @@ * commit to the database once openingd succeeds. */ #include "config.h" +#include #include #include #include @@ -31,6 +32,7 @@ #include #include #include +#include /* stdin == lightningd, 3 == peer, 4 = hsmd */ #define REQ_FD STDIN_FILENO @@ -703,6 +705,15 @@ static bool funder_finalize_channel_setup(struct state *state, else *pbase = NULL; + assert((*tx)->psbt->inputs[0].witness_utxo); + msg = towire_openingd_on_funding_tx(tmpctx, *tx, &cid); + wire_sync_write(REQ_FD, msg); + msg = wire_sync_read(tmpctx, REQ_FD); + if (!fromwire_openingd_on_funding_tx_reply(msg, msg, tx)) + status_failed(STATUS_FAIL_MASTER_IO, "Bad onfunding_tx %s", + tal_hex(tmpctx, msg)); + + assert((*tx)->psbt->inputs[0].witness_utxo); /* We ask the HSM to sign their commitment transaction for us: it knows * our funding key, it just needs the remote funding key to create the * witness script. It also needs the amount of the funding output, @@ -815,6 +826,7 @@ static bool funder_finalize_channel_setup(struct state *state, goto fail; } + // TODO(bitfixnix): RGB for initial tx channel? validate_initial_commitment_signature(HSM_FD, *tx, sig); if (!check_tx_sig(*tx, 0, NULL, wscript, &state->their_funding_pubkey, sig)) { @@ -1501,6 +1513,8 @@ static u8 *handle_master_in(struct state *state) case WIRE_OPENINGD_FAILED: case WIRE_OPENINGD_GOT_OFFER: case WIRE_OPENINGD_GOT_OFFER_REPLY: + case WIRE_OPENINGD_ON_FUNDING_TX: + case WIRE_OPENINGD_ON_FUNDING_TX_REPLY: break; } diff --git a/openingd/openingd_wire.csv b/openingd/openingd_wire.csv index 65aac949fda4..681945a5daf9 100644 --- a/openingd/openingd_wire.csv +++ b/openingd/openingd_wire.csv @@ -140,6 +140,15 @@ msgdata,openingd_fundee,remote_shutdown_len,u16, msgdata,openingd_fundee,remote_shutdown_scriptpubkey,u8,remote_shutdown_len msgdata,openingd_fundee,channel_type,channel_type, +# Communicate the Funding Tx to master +msgtype,openingd_on_funding_tx,6045 +msgdata,openingd_on_funding_tx,tx,bitcoin_tx, +msgdata,openingd_on_funding_tx,channel_id,channel_id, + +# Get a reply from master -> openingd +msgtype,openingd_on_funding_tx_reply,6145 +msgdata,openingd_on_funding_tx_reply,tx,bitcoin_tx, + # master -> openingd: do you have a memleak? msgtype,openingd_dev_memleak,6033 diff --git a/plugins/offers.c b/plugins/offers.c index 03360d1a573f..7b1692391b56 100644 --- a/plugins/offers.c +++ b/plugins/offers.c @@ -1194,6 +1194,9 @@ static struct command_result *json_decode(struct command *cmd, response = jsonrpc_stream_success(cmd); json_add_string(response, "type", decodable->type); + // FIXME(vincenzopalazzo): To decode an RGB invoice we should + // make sure that the RGB is only supported in the bolt11, + // otherwise throws an error. if (decodable->offer) json_add_offer(response, decodable->offer); if (decodable->invreq) diff --git a/plugins/pay.c b/plugins/pay.c index 595d9fffd814..f164eafaf0bc 100644 --- a/plugins/pay.c +++ b/plugins/pay.c @@ -1073,6 +1073,8 @@ static struct command_result *json_pay(struct command *cmd, /* FIXME: parameter should be invstring now */ p_req("bolt11", param_invstring, &b11str), p_opt("amount_msat", param_msat, &msat), + // FIXME(vincenzopalazzo): adds the rgb_amount value that + // can be (maybe) optional inside the invoice. p_opt("label", param_string, &label), p_opt_def("riskfactor", param_millionths, &riskfactor_millionths, 10000000), @@ -1130,6 +1132,7 @@ static struct command_result *json_pay(struct command *cmd, "Invalid bolt11:" " sets feature var_onion with no secret"); } else { + // FIXME(vincenzopalazzo): fails if it is an bolt12 offer. b12 = invoice_decode(tmpctx, b11str, strlen(b11str), plugin_feature_set(cmd->plugin), chainparams, &b12_fail);