From d981b5823494e5ea00fc04e54553140c9e97a44c Mon Sep 17 00:00:00 2001 From: Christian Decker Date: Wed, 2 Oct 2019 19:36:28 +0200 Subject: [PATCH] wallet: Retrieve transaction annotations when listing transactions This triple join should be efficient to read, and to process. We have a one-to-many (tx-to-annotations), followed by a one-to-one (annotation-to-channel) join, so we are limited to annotations x transactions results. --- wallet/wallet.c | 88 +++++++++++++++++++++++++++++++++++++++------- wallet/wallet.h | 20 +++++++++-- wallet/walletrpc.c | 6 ---- 3 files changed, 93 insertions(+), 21 deletions(-) diff --git a/wallet/wallet.c b/wallet/wallet.c index 6e5ac83c3..fa2015a73 100644 --- a/wallet/wallet.c +++ b/wallet/wallet.c @@ -3410,24 +3410,88 @@ struct wallet_transaction *wallet_transactions_get(struct wallet *w, const tal_t { struct db_stmt *stmt; size_t count; - struct wallet_transaction *cur, *txs = tal_arr(ctx, struct wallet_transaction, 0); + struct wallet_transaction *cur = NULL, *txs = tal_arr(ctx, struct wallet_transaction, 0); + struct bitcoin_txid last; + + /* Make sure we can check for changing txids */ + memset(&last, 0, sizeof(last)); stmt = db_prepare_v2( w->db, - SQL("SELECT id, id, rawtx, blockheight, txindex, type, channel_id " - "FROM transactions")); + SQL("SELECT" + " t.id" + ", t.rawtx" + ", t.blockheight" + ", t.txindex" + ", t.type as txtype" + ", c2.short_channel_id as txchan" + ", a.location" + ", a.idx as ann_idx" + ", a.type as annotation_type" + ", c.short_channel_id" + " FROM" + " transactions t LEFT JOIN" + " transaction_annotations a ON (a.txid = t.id) LEFT JOIN" + " channels c ON (a.channel = c.id) LEFT JOIN" + " channels c2 ON (t.channel_id = c2.id) " + "ORDER BY blockheight, txindex ASC")); db_query_prepared(stmt); for (count = 0; db_step(stmt); count++) { - tal_resize(&txs, count + 1); - cur = &txs[count]; - db_column_txid(stmt, 1, &cur->id); - cur->rawtx = tal_dup_arr(txs, u8, db_column_blob(stmt, 2), - db_column_bytes(stmt, 2), 0); - cur->blockheight = db_column_int(stmt, 3); - cur->txindex = db_column_int(stmt, 4); - cur->type = db_column_int(stmt, 5); - cur->channel_id = db_column_int(stmt, 6); + struct bitcoin_txid curtxid; + db_column_txid(stmt, 0, &curtxid); + + /* If this is a new entry, allocate it in the array and set + * the common fields (all fields from the transactions + * table. */ + if (!bitcoin_txid_eq(&last, &curtxid)) { + last = curtxid; + tal_resize(&txs, count + 1); + cur = &txs[count]; + db_column_txid(stmt, 0, &cur->id); + cur->tx = db_column_tx(txs, stmt, 1); + cur->rawtx = tal_dup_arr(txs, u8, db_column_blob(stmt, 1), + db_column_bytes(stmt, 1), 0); + cur->blockheight = db_column_int(stmt, 2); + cur->txindex = db_column_int(stmt, 3); + if (!db_column_is_null(stmt, 4)) + cur->annotation.type = db_column_u64(stmt, 4); + else + cur->annotation.type = 0; + if (!db_column_is_null(stmt, 5)) + db_column_short_channel_id(stmt, 5, &cur->annotation.channel); + else + cur->annotation.channel.u64 = 0; + + cur->output_annotations = tal_arrz(txs, struct tx_annotation, cur->tx->wtx->num_outputs); + cur->input_annotations = tal_arrz(txs, struct tx_annotation, cur->tx->wtx->num_inputs); + } + + /* This should always be set by the above if-statement, + * otherwise we have a txid of all 0x00 bytes... */ + assert(cur != NULL); + + /* Check if we have any annotations. If there are none the + * fields are all set to null */ + if (!db_column_is_null(stmt, 6)) { + enum wallet_tx_annotation_type loc = db_column_int(stmt, 6); + int idx = db_column_int(stmt, 7); + struct tx_annotation *ann; + + /* Select annotation from array to fill in. */ + if (loc == OUTPUT_ANNOTATION) + ann = &cur->output_annotations[idx]; + else if (loc == INPUT_ANNOTATION) + ann = &cur->input_annotations[idx]; + else + fatal("Transaction annotations are only available for inputs and outputs. Value %d", loc); + + ann->type = db_column_int(stmt, 8); + if (!db_column_is_null(stmt, 9)) + db_column_short_channel_id(stmt, 9, &ann->channel); + else + ann->channel.u64 = 0; + } } tal_free(stmt); return txs; diff --git a/wallet/wallet.h b/wallet/wallet.h index 7316f0a68..61e4bb724 100644 --- a/wallet/wallet.h +++ b/wallet/wallet.h @@ -219,6 +219,11 @@ enum wallet_payment_status { PAYMENT_FAILED = 2 }; +struct tx_annotation { + enum wallet_tx_type type; + struct short_channel_id channel; +}; + static inline enum wallet_payment_status wallet_payment_status_in_db(enum wallet_payment_status w) { switch (w) { @@ -295,8 +300,17 @@ struct wallet_transaction { u32 blockheight; u32 txindex; u8 *rawtx; - enum wallet_tx_type type; - u64 channel_id; + + /* Fully parsed transaction */ + const struct bitcoin_tx *tx; + + struct tx_annotation annotation; + + /* tal_arr containing the annotation types, if any, for the respective + * inputs and outputs. 0 if there are no annotations for the + * element. */ + struct tx_annotation *input_annotations; + struct tx_annotation *output_annotations; }; /** @@ -1179,7 +1193,7 @@ void free_unreleased_txs(struct wallet *w); * * @param ctx: allocation context for the returned list * @param wallet: Wallet to load from. - * @return A tal_arr of wallet transactions + * @return A tal_arr of wallet annotated transactions */ struct wallet_transaction *wallet_transactions_get(struct wallet *w, const tal_t *ctx); diff --git a/wallet/walletrpc.c b/wallet/walletrpc.c index ebd7b8885..95e0228bb 100644 --- a/wallet/walletrpc.c +++ b/wallet/walletrpc.c @@ -936,12 +936,6 @@ static struct command_result *json_listtransactions(struct command *cmd, json_add_hex_talarr(response, "rawtx", txs[i].rawtx); json_add_u64(response, "blockheight", txs[i].blockheight); json_add_num(response, "txindex", txs[i].txindex); - json_add_txtypes(response, "type", txs[i].type); - if (txs[i].channel_id != 0) { - json_add_num(response, "channel_id", txs[i].channel_id); - } else { - json_add_null(response, "channel_id"); - } json_object_end(response); } json_array_end(response);