mirror of
https://github.com/ElementsProject/lightning.git
synced 2025-03-01 09:40:19 +01:00
bitcoin/tx_parts: infrastructure for partial bitcoin txs.
`struct tx_parts` is just a txid and a bunch of inputs and outputs, some of which may be NULL. This is both a nod towards a future where we (or our peer) can combine HTLCs or (in an eltoo world) commitments, although for the moment all our tx_parts will be complete. It also matches our plan to split `bitcoin_tx` into two types: this `struct tx_parts` where we don't know input amounts etc, and `psbt` where we do. Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>
This commit is contained in:
parent
7e48f77d43
commit
dafaf854c5
4 changed files with 428 additions and 0 deletions
|
@ -15,6 +15,7 @@ BITCOIN_SRC := \
|
|||
bitcoin/short_channel_id.c \
|
||||
bitcoin/signature.c \
|
||||
bitcoin/tx.c \
|
||||
bitcoin/tx_parts.c \
|
||||
bitcoin/varint.c
|
||||
|
||||
BITCOIN_OBJS := $(BITCOIN_SRC:.c=.o)
|
||||
|
@ -35,6 +36,7 @@ BITCOIN_HEADERS := bitcoin/address.h \
|
|||
bitcoin/short_channel_id.h \
|
||||
bitcoin/signature.h \
|
||||
bitcoin/tx.h \
|
||||
bitcoin/tx_parts.h \
|
||||
bitcoin/varint.h
|
||||
|
||||
check-source: $(BITCOIN_SRC:%=check-src-include-order/%) \
|
||||
|
|
396
bitcoin/tx_parts.c
Normal file
396
bitcoin/tx_parts.c
Normal file
|
@ -0,0 +1,396 @@
|
|||
#include <assert.h>
|
||||
#include <bitcoin/tx_parts.h>
|
||||
#include <common/utils.h>
|
||||
#include <wire/wire.h>
|
||||
|
||||
/* This destructor makes it behave like a native tal tree (a little!) */
|
||||
static void destroy_wally_tx_input(struct wally_tx_input *in)
|
||||
{
|
||||
wally_tx_input_free(in);
|
||||
}
|
||||
|
||||
static struct wally_tx_input *clone_input(const tal_t *ctx,
|
||||
const struct wally_tx_input *src)
|
||||
{
|
||||
struct wally_tx_input *in;
|
||||
int ret;
|
||||
|
||||
if (is_elements(chainparams)) {
|
||||
ret = wally_tx_elements_input_init_alloc
|
||||
(src->txhash, sizeof(src->txhash),
|
||||
src->index, src->sequence,
|
||||
src->script, src->script_len,
|
||||
src->witness,
|
||||
src->blinding_nonce, sizeof(src->blinding_nonce),
|
||||
src->entropy, sizeof(src->entropy),
|
||||
src->issuance_amount, src->issuance_amount_len,
|
||||
src->inflation_keys, src->inflation_keys_len,
|
||||
src->issuance_amount_rangeproof,
|
||||
src->issuance_amount_rangeproof_len,
|
||||
src->inflation_keys_rangeproof,
|
||||
src->inflation_keys_rangeproof_len,
|
||||
src->pegin_witness,
|
||||
&in);
|
||||
} else {
|
||||
ret = wally_tx_input_init_alloc(src->txhash, sizeof(src->txhash),
|
||||
src->index, src->sequence,
|
||||
src->script, src->script_len,
|
||||
src->witness, &in);
|
||||
}
|
||||
assert(ret == WALLY_OK);
|
||||
|
||||
tal_add_destructor(in, destroy_wally_tx_input);
|
||||
return tal_steal(ctx, in);
|
||||
}
|
||||
|
||||
static void destroy_wally_tx_output(struct wally_tx_output *out)
|
||||
{
|
||||
wally_tx_output_free(out);
|
||||
}
|
||||
|
||||
static struct wally_tx_output *clone_output(const tal_t *ctx,
|
||||
const struct wally_tx_output *src)
|
||||
{
|
||||
struct wally_tx_output *out;
|
||||
int ret;
|
||||
|
||||
if (is_elements(chainparams)) {
|
||||
ret = wally_tx_elements_output_init_alloc
|
||||
(src->script, src->script_len,
|
||||
src->asset, src->asset_len,
|
||||
src->value, src->value_len,
|
||||
src->nonce, src->nonce_len,
|
||||
src->surjectionproof, src->surjectionproof_len,
|
||||
src->rangeproof, src->rangeproof_len,
|
||||
&out);
|
||||
} else {
|
||||
ret = wally_tx_output_init_alloc(src->satoshi,
|
||||
src->script, src->script_len,
|
||||
&out);
|
||||
}
|
||||
assert(ret == WALLY_OK);
|
||||
|
||||
tal_add_destructor(out, destroy_wally_tx_output);
|
||||
return tal_steal(ctx, out);
|
||||
}
|
||||
|
||||
struct tx_parts *tx_parts_from_wally_tx(const tal_t *ctx,
|
||||
const struct wally_tx *wtx,
|
||||
int input, int output)
|
||||
{
|
||||
struct tx_parts *txp = tal(ctx, struct tx_parts);
|
||||
|
||||
wally_txid(wtx, &txp->txid);
|
||||
txp->inputs = tal_arrz(txp, struct wally_tx_input *, wtx->num_inputs);
|
||||
txp->outputs = tal_arrz(txp, struct wally_tx_output *, wtx->num_outputs);
|
||||
|
||||
for (size_t i = 0; i < wtx->num_inputs; i++) {
|
||||
if (input != -1 && input != i)
|
||||
continue;
|
||||
txp->inputs[i] = clone_input(txp->inputs, &wtx->inputs[i]);
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < wtx->num_outputs; i++) {
|
||||
if (output != -1 && output != i)
|
||||
continue;
|
||||
txp->outputs[i] = clone_output(txp->outputs, &wtx->outputs[i]);
|
||||
}
|
||||
|
||||
return txp;
|
||||
}
|
||||
|
||||
/* FIXME: If libwally exposed their linearization code, we could use it */
|
||||
static struct wally_tx_witness_stack *
|
||||
fromwire_wally_tx_witness_stack(const tal_t *ctx,
|
||||
const u8 **cursor,
|
||||
size_t *max)
|
||||
{
|
||||
struct wally_tx_witness_stack *ws;
|
||||
size_t num;
|
||||
int ret;
|
||||
|
||||
num = fromwire_u32(cursor, max);
|
||||
if (num == 0)
|
||||
return NULL;
|
||||
|
||||
ret = wally_tx_witness_stack_init_alloc(num, &ws);
|
||||
if (ret != WALLY_OK) {
|
||||
fromwire_fail(cursor, max);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < num; i++) {
|
||||
u8 *w = fromwire_tal_arrn(tmpctx,
|
||||
cursor, max,
|
||||
fromwire_u32(cursor, max));
|
||||
ret = wally_tx_witness_stack_add(ws, w, tal_bytelen(w));
|
||||
if (ret != WALLY_OK) {
|
||||
wally_tx_witness_stack_free(ws);
|
||||
fromwire_fail(cursor, max);
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
return ws;
|
||||
}
|
||||
|
||||
static void towire_wally_tx_witness_stack(u8 **pptr,
|
||||
const struct wally_tx_witness_stack *ws)
|
||||
{
|
||||
if (!ws) {
|
||||
towire_u32(pptr, 0);
|
||||
return;
|
||||
}
|
||||
|
||||
towire_u32(pptr, ws->num_items);
|
||||
for (size_t i = 0; i < ws->num_items; i++) {
|
||||
towire_u32(pptr, ws->items[i].witness_len);
|
||||
towire_u8_array(pptr,
|
||||
ws->items[i].witness,
|
||||
ws->items[i].witness_len);
|
||||
}
|
||||
}
|
||||
|
||||
static struct wally_tx_input *fromwire_wally_tx_input(const tal_t *ctx,
|
||||
const u8 **cursor,
|
||||
size_t *max)
|
||||
{
|
||||
struct wally_tx_input *in;
|
||||
struct bitcoin_txid txid;
|
||||
u32 index, sequence;
|
||||
u8 *script;
|
||||
struct wally_tx_witness_stack *ws;
|
||||
int ret;
|
||||
|
||||
fromwire_bitcoin_txid(cursor, max, &txid);
|
||||
index = fromwire_u32(cursor, max);
|
||||
sequence = fromwire_u32(cursor, max);
|
||||
script = fromwire_tal_arrn(tmpctx,
|
||||
cursor, max, fromwire_u32(cursor, max));
|
||||
/* libwally doesn't like non-NULL ptrs with zero lengths. */
|
||||
if (tal_bytelen(script) == 0)
|
||||
script = tal_free(script);
|
||||
ws = fromwire_wally_tx_witness_stack(tmpctx, cursor, max);
|
||||
|
||||
if (is_elements(chainparams)) {
|
||||
u8 *blinding_nonce, *entropy, *issuance_amount,
|
||||
*inflation_keys, *issuance_amount_rangeproof,
|
||||
*inflation_keys_rangeproof;
|
||||
struct wally_tx_witness_stack *pegin_witness;
|
||||
|
||||
blinding_nonce = fromwire_tal_arrn(tmpctx,
|
||||
cursor, max,
|
||||
fromwire_u32(cursor, max));
|
||||
entropy = fromwire_tal_arrn(tmpctx,
|
||||
cursor, max,
|
||||
fromwire_u32(cursor, max));
|
||||
issuance_amount = fromwire_tal_arrn(tmpctx,
|
||||
cursor, max,
|
||||
fromwire_u32(cursor, max));
|
||||
inflation_keys = fromwire_tal_arrn(tmpctx,
|
||||
cursor, max,
|
||||
fromwire_u32(cursor, max));
|
||||
issuance_amount_rangeproof = fromwire_tal_arrn(tmpctx,
|
||||
cursor, max,
|
||||
fromwire_u32(cursor, max));
|
||||
inflation_keys_rangeproof = fromwire_tal_arrn(tmpctx,
|
||||
cursor, max,
|
||||
fromwire_u32(cursor, max));
|
||||
pegin_witness = fromwire_wally_tx_witness_stack(tmpctx,
|
||||
cursor, max);
|
||||
ret = wally_tx_elements_input_init_alloc
|
||||
(txid.shad.sha.u.u8, sizeof(txid.shad.sha.u.u8),
|
||||
index, sequence,
|
||||
script, tal_bytelen(script),
|
||||
ws,
|
||||
blinding_nonce, tal_bytelen(blinding_nonce),
|
||||
entropy, tal_bytelen(entropy),
|
||||
issuance_amount, tal_bytelen(issuance_amount),
|
||||
inflation_keys, tal_bytelen(inflation_keys),
|
||||
issuance_amount_rangeproof,
|
||||
tal_bytelen(issuance_amount_rangeproof),
|
||||
inflation_keys_rangeproof,
|
||||
tal_bytelen(inflation_keys_rangeproof),
|
||||
pegin_witness,
|
||||
&in);
|
||||
} else {
|
||||
ret = wally_tx_input_init_alloc(txid.shad.sha.u.u8,
|
||||
sizeof(txid.shad.sha.u.u8),
|
||||
index, sequence,
|
||||
script, tal_bytelen(script),
|
||||
ws, &in);
|
||||
}
|
||||
if (ret != WALLY_OK) {
|
||||
fromwire_fail(cursor, max);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
tal_add_destructor(in, destroy_wally_tx_input);
|
||||
return tal_steal(ctx, in);
|
||||
}
|
||||
|
||||
static struct wally_tx_output *fromwire_wally_tx_output(const tal_t *ctx,
|
||||
const u8 **cursor,
|
||||
size_t *max)
|
||||
{
|
||||
struct wally_tx_output *out;
|
||||
unsigned char *script;
|
||||
int ret;
|
||||
|
||||
script = fromwire_tal_arrn(tmpctx,
|
||||
cursor, max, fromwire_u32(cursor, max));
|
||||
|
||||
if (is_elements(chainparams)) {
|
||||
u8 *asset, *value, *nonce, *surjectionproof, *rangeproof;
|
||||
|
||||
asset = fromwire_tal_arrn(tmpctx,
|
||||
cursor, max,
|
||||
fromwire_u32(cursor, max));
|
||||
value = fromwire_tal_arrn(tmpctx,
|
||||
cursor, max,
|
||||
fromwire_u32(cursor, max));
|
||||
nonce = fromwire_tal_arrn(tmpctx,
|
||||
cursor, max,
|
||||
fromwire_u32(cursor, max));
|
||||
surjectionproof = fromwire_tal_arrn(tmpctx,
|
||||
cursor, max,
|
||||
fromwire_u32(cursor, max));
|
||||
rangeproof = fromwire_tal_arrn(tmpctx,
|
||||
cursor, max,
|
||||
fromwire_u32(cursor, max));
|
||||
ret = wally_tx_elements_output_init_alloc
|
||||
(script, tal_bytelen(script),
|
||||
asset, tal_bytelen(asset),
|
||||
value, tal_bytelen(value),
|
||||
nonce, tal_bytelen(nonce),
|
||||
surjectionproof, tal_bytelen(surjectionproof),
|
||||
rangeproof, tal_bytelen(rangeproof),
|
||||
&out);
|
||||
} else {
|
||||
u64 satoshi;
|
||||
satoshi = fromwire_u64(cursor, max);
|
||||
ret = wally_tx_output_init_alloc(satoshi,
|
||||
script, tal_bytelen(script),
|
||||
&out);
|
||||
}
|
||||
if (ret != WALLY_OK) {
|
||||
fromwire_fail(cursor, max);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
tal_add_destructor(out, destroy_wally_tx_output);
|
||||
return tal_steal(ctx, out);
|
||||
}
|
||||
|
||||
static void towire_wally_tx_input(u8 **pptr, const struct wally_tx_input *in)
|
||||
{
|
||||
/* Just like a bitcoin_txid */
|
||||
towire_u8_array(pptr, in->txhash, sizeof(in->txhash));
|
||||
towire_u32(pptr, in->index);
|
||||
towire_u32(pptr, in->sequence);
|
||||
towire_u32(pptr, in->script_len);
|
||||
towire_u8_array(pptr, in->script, in->script_len);
|
||||
towire_wally_tx_witness_stack(pptr, in->witness);
|
||||
|
||||
if (is_elements(chainparams)) {
|
||||
towire_u8_array(pptr, in->blinding_nonce,
|
||||
sizeof(in->blinding_nonce));
|
||||
towire_u8_array(pptr, in->entropy, sizeof(in->entropy));
|
||||
towire_u32(pptr, in->issuance_amount_len);
|
||||
towire_u8_array(pptr, in->issuance_amount,
|
||||
in->issuance_amount_len);
|
||||
towire_u32(pptr, in->inflation_keys_len);
|
||||
towire_u8_array(pptr, in->inflation_keys,
|
||||
in->inflation_keys_len);
|
||||
towire_u32(pptr, in->issuance_amount_rangeproof_len);
|
||||
towire_u8_array(pptr, in->issuance_amount_rangeproof,
|
||||
in->issuance_amount_rangeproof_len);
|
||||
towire_u32(pptr, in->inflation_keys_rangeproof_len);
|
||||
towire_u8_array(pptr, in->inflation_keys_rangeproof,
|
||||
in->inflation_keys_rangeproof_len);
|
||||
towire_wally_tx_witness_stack(pptr, in->pegin_witness);
|
||||
}
|
||||
}
|
||||
|
||||
static void towire_wally_tx_output(u8 **pptr, const struct wally_tx_output *out)
|
||||
{
|
||||
towire_u32(pptr, out->script_len);
|
||||
towire_u8_array(pptr, out->script, out->script_len);
|
||||
|
||||
if (is_elements(chainparams)) {
|
||||
towire_u32(pptr, out->asset_len);
|
||||
towire_u8_array(pptr, out->asset, out->asset_len);
|
||||
towire_u32(pptr, out->value_len);
|
||||
towire_u8_array(pptr, out->value, out->value_len);
|
||||
towire_u32(pptr, out->nonce_len);
|
||||
towire_u8_array(pptr, out->nonce, out->nonce_len);
|
||||
towire_u32(pptr, out->surjectionproof_len);
|
||||
towire_u8_array(pptr, out->surjectionproof,
|
||||
out->surjectionproof_len);
|
||||
towire_u32(pptr, out->rangeproof_len);
|
||||
towire_u8_array(pptr, out->rangeproof, out->rangeproof_len);
|
||||
} else {
|
||||
towire_u64(pptr, out->satoshi);
|
||||
}
|
||||
}
|
||||
|
||||
/* Wire marshalling and unmarshalling */
|
||||
struct tx_parts *fromwire_tx_parts(const tal_t *ctx,
|
||||
const u8 **cursor, size_t *max)
|
||||
{
|
||||
struct tx_parts *txp = tal(ctx, struct tx_parts);
|
||||
u32 num_inputs, num_outputs;
|
||||
|
||||
fromwire_bitcoin_txid(cursor, max, &txp->txid);
|
||||
num_inputs = fromwire_u32(cursor, max);
|
||||
txp->inputs = tal_arr(txp, struct wally_tx_input *, num_inputs);
|
||||
for (size_t i = 0; i < num_inputs; i++) {
|
||||
if (fromwire_bool(cursor, max)) {
|
||||
txp->inputs[i] = fromwire_wally_tx_input(txp->inputs,
|
||||
cursor, max);
|
||||
} else {
|
||||
txp->inputs[i] = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
num_outputs = fromwire_u32(cursor, max);
|
||||
txp->outputs = tal_arr(txp, struct wally_tx_output *, num_outputs);
|
||||
for (size_t i = 0; i < num_outputs; i++) {
|
||||
if (fromwire_bool(cursor, max)) {
|
||||
txp->outputs[i] = fromwire_wally_tx_output(txp->outputs,
|
||||
cursor, max);
|
||||
} else {
|
||||
txp->outputs[i] = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
if (*cursor == NULL)
|
||||
return tal_free(txp);
|
||||
|
||||
return txp;
|
||||
}
|
||||
|
||||
void towire_tx_parts(u8 **pptr, const struct tx_parts *txp)
|
||||
{
|
||||
towire_bitcoin_txid(pptr, &txp->txid);
|
||||
|
||||
towire_u32(pptr, tal_count(txp->inputs));
|
||||
for (size_t i = 0; i < tal_count(txp->inputs); i++) {
|
||||
if (txp->inputs[i]) {
|
||||
towire_bool(pptr, true);
|
||||
towire_wally_tx_input(pptr, txp->inputs[i]);
|
||||
} else {
|
||||
towire_bool(pptr, false);
|
||||
}
|
||||
}
|
||||
|
||||
towire_u32(pptr, tal_count(txp->outputs));
|
||||
for (size_t i = 0; i < tal_count(txp->outputs); i++) {
|
||||
if (txp->outputs[i]) {
|
||||
towire_bool(pptr, true);
|
||||
towire_wally_tx_output(pptr, txp->outputs[i]);
|
||||
} else {
|
||||
towire_bool(pptr, false);
|
||||
}
|
||||
}
|
||||
}
|
29
bitcoin/tx_parts.h
Normal file
29
bitcoin/tx_parts.h
Normal file
|
@ -0,0 +1,29 @@
|
|||
/* This represents a specific part of a transaction, without including
|
||||
* all the metadata (which we might not know, if we didn't make the
|
||||
* transction ourselves). */
|
||||
#ifndef LIGHTNING_BITCOIN_TX_PARTS_H
|
||||
#define LIGHTNING_BITCOIN_TX_PARTS_H
|
||||
#include "config.h"
|
||||
#include <bitcoin/tx.h>
|
||||
#include <wally_transaction.h>
|
||||
|
||||
struct tx_parts {
|
||||
/* The txid of this transacation */
|
||||
struct bitcoin_txid txid;
|
||||
/* A subset of inputs: NULL means it's not included. */
|
||||
struct wally_tx_input **inputs;
|
||||
/* A subset of outputs: NULL means it's not included. */
|
||||
struct wally_tx_output **outputs;
|
||||
};
|
||||
|
||||
/* Initialize this from a wally_tx: input/output == -1 for all,
|
||||
* otherwise the input/output number to include. */
|
||||
struct tx_parts *tx_parts_from_wally_tx(const tal_t *ctx,
|
||||
const struct wally_tx *wtx,
|
||||
int input, int output);
|
||||
|
||||
/* Wire marshalling and unmarshalling */
|
||||
struct tx_parts *fromwire_tx_parts(const tal_t *ctx,
|
||||
const u8 **cursor, size_t *max);
|
||||
void towire_tx_parts(u8 **pptr, const struct tx_parts *tx_parts);
|
||||
#endif /* LIGHTNING_BITCOIN_TX_PARTS_H */
|
|
@ -234,6 +234,7 @@ class Type(FieldSet):
|
|||
'feature_set',
|
||||
'onionmsg_path',
|
||||
'route_hop',
|
||||
'tx_parts',
|
||||
]
|
||||
|
||||
# Some BOLT types are re-typed based on their field name
|
||||
|
|
Loading…
Add table
Reference in a new issue