Browse Source
`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>nifty/pset-pre
4 changed files with 428 additions and 0 deletions
@ -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); |
||||
|
} |
||||
|
} |
||||
|
} |
@ -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 */ |
Loading…
Reference in new issue