Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
21 commits
Select commit Hold shift + click to select a range
85a53ea
libwally: update to latest commit
niftynei Jun 4, 2020
f6b714e
psbt: check return value on init for wally input
niftynei May 21, 2020
eb1a151
psbt: try one big alloc and fail instead of incremental buffer increases
niftynei May 21, 2020
5b83fd6
utxo: fill in scriptPubkey to NULL
niftynei May 21, 2020
dc00b16
psbt: remove input_amounts from bitcoin tx
niftynei May 27, 2020
7e17cb0
tx-psbt: pass in the witness script (if known) when adding an input
niftynei May 21, 2020
8e6f26b
tx: update comment
niftynei May 21, 2020
109991e
psbt: populate last commitment transaction's input info at db
niftynei May 21, 2020
8bfd9be
psbt: add to/from byte helpers
niftynei May 22, 2020
7161bea
tx: add setter for tx locktime
niftynei May 22, 2020
6c2ed80
bitcoin/tx: implement wally_tx_clone (badly) for now.
rustyrussell May 27, 2020
86ea16d
psbt: add 'wally_psbt_clone' function, to clone a psbt
niftynei May 29, 2020
d260ed4
psbt: helpers for adding a pubkey or signature to a psbt
niftynei May 27, 2020
80c8a16
psbt: add method to confirm 'finalized' status of psbt
niftynei May 29, 2020
aa90f98
psbt: move `channels.last_tx` field to be a psbt
niftynei May 22, 2020
2ffb3d5
hsm: decouple hsm from wallet; init before wallet
niftynei May 26, 2020
92c2347
psbt: database migration for converting last_tx to a psbt
niftynei May 22, 2020
b85ec68
psbt: affirm database upgrade works for last_tx -> psbt
niftynei May 26, 2020
3568c09
channel_tx: add the commitment sig and pubkey data to the commit tx
niftynei May 27, 2020
5ff9c68
psbt: if a transaction has witnesses/scriptSig set, add it to psbt
niftynei May 29, 2020
2f22bb7
psbt: handle 'unsetting' final witness stack
niftynei May 29, 2020
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
227 changes: 209 additions & 18 deletions bitcoin/psbt.c
Original file line number Diff line number Diff line change
@@ -1,6 +1,12 @@
#include <assert.h>
#include <bitcoin/psbt.h>
#include <bitcoin/pubkey.h>
#include <bitcoin/script.h>
#include <bitcoin/signature.h>
#include <ccan/cast/cast.h>
#include <ccan/ccan/array_size/array_size.h>
#include <common/amount.h>
#include <common/utils.h>
#include <string.h>
#include <wally_psbt.h>
#include <wally_transaction.h>
Expand All @@ -14,6 +20,18 @@
memmove((arr) + (pos), (arr) + (pos) + 1, \
sizeof(*(arr)) * ((num) - ((pos) + 1)))

/* FIXME: someday this will break, because it's been exposed in libwally */
int wally_psbt_clone(const struct wally_psbt *psbt, struct wally_psbt **output)
{
int ret;
size_t byte_len;
const u8 *bytes = psbt_get_bytes(NULL, psbt, &byte_len);

ret = wally_psbt_from_bytes(bytes, byte_len, output);
tal_free(bytes);
return ret;
}

void psbt_destroy(struct wally_psbt *psbt)
{
wally_psbt_free(psbt);
Expand Down Expand Up @@ -50,9 +68,26 @@ struct wally_psbt *new_psbt(const tal_t *ctx, const struct wally_tx *wtx)

/* set the scripts + witnesses back */
for (size_t i = 0; i < wtx->num_inputs; i++) {
int wally_err;

wtx->inputs[i].script = (unsigned char *)scripts[i];
wtx->inputs[i].script_len = script_lens[i];
wtx->inputs[i].witness = witnesses[i];

/* add these scripts + witnesses to the psbt */
if (scripts[i]) {
wally_err =
wally_psbt_input_set_final_script_sig(&psbt->inputs[i],
(unsigned char *)scripts[i],
script_lens[i]);
assert(wally_err == WALLY_OK);
}
if (witnesses[i]) {
wally_err =
wally_psbt_input_set_final_witness(&psbt->inputs[i],
witnesses[i]);
assert(wally_err == WALLY_OK);
}
}

tal_free(witnesses);
Expand All @@ -62,6 +97,17 @@ struct wally_psbt *new_psbt(const tal_t *ctx, const struct wally_tx *wtx)
return tal_steal(ctx, psbt);
}

bool psbt_is_finalized(struct wally_psbt *psbt)
{
for (size_t i = 0; i < psbt->num_inputs; i++) {
if (!psbt->inputs[i].final_script_sig &&
!psbt->inputs[i].final_witness)
return false;
}

return true;
}

struct wally_psbt_input *psbt_add_input(struct wally_psbt *psbt,
struct wally_tx_input *input,
size_t insert_at)
Expand All @@ -72,6 +118,7 @@ struct wally_psbt_input *psbt_add_input(struct wally_psbt *psbt,
tx = psbt->tx;
assert(insert_at <= tx->num_inputs);
wally_tx_add_input(tx, input);

tmp_in = tx->inputs[tx->num_inputs - 1];
MAKE_ROOM(tx->inputs, insert_at, tx->num_inputs);
tx->inputs[insert_at] = tmp_in;
Expand Down Expand Up @@ -138,22 +185,169 @@ void psbt_rm_output(struct wally_psbt *psbt,
psbt->num_outputs -= 1;
}

void psbt_input_add_pubkey(struct wally_psbt *psbt, size_t in,
const struct pubkey *pubkey)
{
int wally_err;
u32 empty_path[1] = {0};
unsigned char fingerprint[4];
struct ripemd160 hash;
u8 pk_der[PUBKEY_CMPR_LEN];

assert(in < psbt->num_inputs);

/* Find the key identifier fingerprint:
* the first 32 bits of the identifier, where the identifier
* is the hash160 of the ECDSA serialized public key
* https://github.com/bitcoin/bips/blob/master/bip-0032.mediawiki#key-identifiers
* */
pubkey_to_hash160(pubkey, &hash);
memcpy(fingerprint, hash.u.u8, sizeof(fingerprint));

/* we serialize the compressed version of the key, wally likes this */
pubkey_to_der(pk_der, pubkey);

if (!psbt->inputs[in].keypaths)
if (wally_keypath_map_init_alloc(1, &psbt->inputs[in].keypaths) != WALLY_OK)
abort();

wally_err = wally_add_new_keypath(psbt->inputs[in].keypaths,
pk_der, sizeof(pk_der),
fingerprint, sizeof(fingerprint),
empty_path, ARRAY_SIZE(empty_path));

assert(wally_err == WALLY_OK);
}

void psbt_input_set_partial_sig(struct wally_psbt *psbt, size_t in,
const struct pubkey *pubkey,
const struct bitcoin_signature *sig)
{
int wally_err;
u8 pk_der[PUBKEY_CMPR_LEN];

assert(in < psbt->num_inputs);
if (!psbt->inputs[in].partial_sigs)
if (wally_partial_sigs_map_init_alloc(1, &psbt->inputs[in].partial_sigs) != WALLY_OK)
abort();

/* we serialize the compressed version of the key, wally likes this */
pubkey_to_der(pk_der, pubkey);
wally_err = wally_add_new_partial_sig(psbt->inputs[in].partial_sigs,
pk_der, sizeof(pk_der),
cast_const(unsigned char *, sig->s.data),
sizeof(sig->s.data));
assert(wally_err == WALLY_OK);

wally_err = wally_psbt_input_set_sighash_type(&psbt->inputs[in],
sig->sighash_type);
assert(wally_err == WALLY_OK);
}

void psbt_input_set_prev_utxo(struct wally_psbt *psbt, size_t in,
const u8 *scriptPubkey, struct amount_sat amt)
{
struct wally_tx_output *prev_out;
int wally_err;
u8 *scriptpk;

assert(psbt->num_inputs > in);
if (scriptPubkey) {
assert(is_p2wsh(scriptPubkey, NULL) || is_p2wpkh(scriptPubkey, NULL)
|| is_p2sh(scriptPubkey, NULL));
scriptpk = cast_const(u8 *, scriptPubkey);
} else {
/* Adding a NULL scriptpubkey is an error, *however* there is the
* possiblity we're spending a UTXO that we didn't save the
* scriptpubkey data for. in this case we set it to an 'empty'
* or zero-len script */
scriptpk = tal_arr(psbt, u8, 1);
scriptpk[0] = 0x00;
Comment thread
cdecker marked this conversation as resolved.
}

wally_err = wally_tx_output_init_alloc(amt.satoshis, /* Raw: type conv */
scriptpk,
tal_bytelen(scriptpk),
&prev_out);
assert(wally_err == WALLY_OK);
wally_err = wally_psbt_input_set_witness_utxo(&psbt->inputs[in],
prev_out);
assert(wally_err == WALLY_OK);
tal_steal(psbt, psbt->inputs[in].witness_utxo);
}

void psbt_input_set_prev_utxo_wscript(struct wally_psbt *psbt, size_t in,
const u8 *wscript, struct amount_sat amt)
{
int wally_err;
const u8 *scriptPubkey;

if (wscript) {
scriptPubkey = scriptpubkey_p2wsh(psbt, wscript);
wally_err = wally_psbt_input_set_witness_script(&psbt->inputs[in],
cast_const(u8 *, wscript),
tal_bytelen(wscript));
assert(wally_err == WALLY_OK);
} else
scriptPubkey = NULL;
psbt_input_set_prev_utxo(psbt, in, scriptPubkey, amt);
}

struct amount_sat psbt_input_get_amount(struct wally_psbt *psbt,
size_t in)
{
struct amount_sat val;
assert(in < psbt->num_inputs);
if (psbt->inputs[in].witness_utxo) {
val.satoshis = psbt->inputs[in].witness_utxo->satoshi; /* Raw: type conversion */
} else if (psbt->inputs[in].non_witness_utxo) {
int idx = psbt->tx->inputs[in].index;
struct wally_tx *prev_tx = psbt->inputs[in].non_witness_utxo;
val.satoshis = prev_tx->outputs[idx].satoshi; /* Raw: type conversion */
} else
abort();

return val;
}

const u8 *psbt_get_bytes(const tal_t *ctx, const struct wally_psbt *psbt,
size_t *bytes_written)
{
/* the libwally API doesn't do anything helpful for allocating
* things here -- to compensate we do a single shot large alloc
*/
size_t room = 1024 * 1000;
Comment thread
cdecker marked this conversation as resolved.
u8 *pbt_bytes = tal_arr(ctx, u8, room);
if (wally_psbt_to_bytes(psbt, pbt_bytes, room, bytes_written) != WALLY_OK) {
/* something went wrong. bad libwally ?? */
abort();
}
tal_resize(&pbt_bytes, *bytes_written);
return pbt_bytes;
}

struct wally_psbt *psbt_from_bytes(const tal_t *ctx, const u8 *bytes,
size_t byte_len)
{
struct wally_psbt *psbt;

if (wally_psbt_from_bytes(bytes, byte_len, &psbt) != WALLY_OK)
return NULL;

/* We promised it would be owned by ctx: libwally uses a dummy owner */
tal_steal(ctx, psbt);
tal_add_destructor(psbt, psbt_destroy);
return psbt;
}

void towire_psbt(u8 **pptr, const struct wally_psbt *psbt)
{
/* Let's include the PSBT bytes */
for (size_t room = 1024; room < 1024 * 1000; room *= 2) {
u8 *pbt_bytes = tal_arr(NULL, u8, room);
size_t bytes_written;
if (wally_psbt_to_bytes(psbt, pbt_bytes, room, &bytes_written) == WALLY_OK) {
towire_u32(pptr, bytes_written);
towire_u8_array(pptr, pbt_bytes, bytes_written);
tal_free(pbt_bytes);
return;
}
tal_free(pbt_bytes);
}
/* PSBT is too big */
abort();
size_t bytes_written;
const u8 *pbt_bytes = psbt_get_bytes(NULL, psbt, &bytes_written);
towire_u32(pptr, bytes_written);
towire_u8_array(pptr, pbt_bytes, bytes_written);
tal_free(pbt_bytes);
}

struct wally_psbt *fromwire_psbt(const tal_t *ctx,
Expand All @@ -168,13 +362,10 @@ struct wally_psbt *fromwire_psbt(const tal_t *ctx,
if (!psbt_buf)
return NULL;

if (wally_psbt_from_bytes(psbt_buf, psbt_byte_len, &psbt) != WALLY_OK)
psbt = psbt_from_bytes(ctx, psbt_buf, psbt_byte_len);
if (!psbt)
return fromwire_fail(cursor, max);

/* We promised it would be owned by ctx: libwally uses a dummy owner */
tal_steal(ctx, psbt);
tal_add_destructor(psbt, psbt_destroy);

#if DEVELOPER
/* Re-marshall for sanity check! */
u8 *tmpbuf = tal_arr(NULL, u8, psbt_byte_len);
Expand Down
34 changes: 34 additions & 0 deletions bitcoin/psbt.h
Original file line number Diff line number Diff line change
Expand Up @@ -10,12 +10,28 @@ struct wally_tx_output;
struct wally_psbt;
struct wally_psbt_input;
struct wally_tx;
struct amount_sat;
struct bitcoin_signature;
struct pubkey;

int wally_psbt_clone(const struct wally_psbt *psbt, struct wally_psbt **output);

void psbt_destroy(struct wally_psbt *psbt);

struct wally_psbt *new_psbt(const tal_t *ctx,
const struct wally_tx *wtx);

/**
* psbt_is_finalized - Check if tx is ready to be extracted
*
* The libwally library requires a transaction be *ready* for
* extraction before it will add/append all of the sigs/witnesses
* onto the global transaction. This check returns true if
* a psbt has the finalized script sig and/or witness data populated
* for such a call
*/
bool psbt_is_finalized(struct wally_psbt *psbt);

struct wally_psbt_input *psbt_add_input(struct wally_psbt *psbt,
struct wally_tx_input *input,
size_t insert_at);
Expand All @@ -30,6 +46,24 @@ struct wally_psbt_output *psbt_add_output(struct wally_psbt *psbt,
void psbt_rm_output(struct wally_psbt *psbt,
size_t remove_at);

void psbt_input_add_pubkey(struct wally_psbt *psbt, size_t in,
const struct pubkey *pubkey);

void psbt_input_set_partial_sig(struct wally_psbt *psbt, size_t in,
const struct pubkey *pubkey,
const struct bitcoin_signature *sig);

void psbt_input_set_prev_utxo(struct wally_psbt *psbt, size_t in,
const u8 *wscript, struct amount_sat amt);
void psbt_input_set_prev_utxo_wscript(struct wally_psbt *psbt, size_t in,
const u8 *wscript, struct amount_sat amt);
struct amount_sat psbt_input_get_amount(struct wally_psbt *psbt,
size_t in);

const u8 *psbt_get_bytes(const tal_t *ctx, const struct wally_psbt *psbt,
size_t *bytes_written);
struct wally_psbt *psbt_from_bytes(const tal_t *ctx, const u8 *bytes,
size_t byte_len);
void towire_psbt(u8 **pptr, const struct wally_psbt *psbt);
struct wally_psbt *fromwire_psbt(const tal_t *ctx,
const u8 **curosr, size_t *max);
Expand Down
11 changes: 8 additions & 3 deletions bitcoin/signature.c
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
#include "signature.h"
#include "tx.h"
#include <assert.h>
#include <bitcoin/psbt.h>
#include <ccan/cast/cast.h>
#include <ccan/mem/mem.h>
#include <common/type_to_string.h>
Expand Down Expand Up @@ -119,11 +120,15 @@ void bitcoin_tx_hash_for_sig(const struct bitcoin_tx *tx, unsigned int in,
{
int ret;
u8 value[9];
u64 satoshis = tx->input_amounts[in]->satoshis /* Raw: sig-helper */;
u64 input_val_sats;
struct amount_sat input_amt;
int flags = WALLY_TX_FLAG_USE_WITNESS;

input_amt = psbt_input_get_amount(tx->psbt, in);
input_val_sats = input_amt.satoshis; /* Raw: type conversion */

if (is_elements(chainparams)) {
ret = wally_tx_confidential_value_from_satoshi(satoshis, value, sizeof(value));
ret = wally_tx_confidential_value_from_satoshi(input_val_sats, value, sizeof(value));
assert(ret == WALLY_OK);
ret = wally_tx_get_elements_signature_hash(
tx->wtx, in, script, tal_bytelen(script), value,
Expand All @@ -132,7 +137,7 @@ void bitcoin_tx_hash_for_sig(const struct bitcoin_tx *tx, unsigned int in,
assert(ret == WALLY_OK);
} else {
ret = wally_tx_get_btc_signature_hash(
tx->wtx, in, script, tal_bytelen(script), satoshis,
tx->wtx, in, script, tal_bytelen(script), input_val_sats,
sighash_type, flags, dest->sha.u.u8, sizeof(*dest));
assert(ret == WALLY_OK);
}
Expand Down
18 changes: 18 additions & 0 deletions bitcoin/test/run-bitcoin_block_from_hex.c
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,24 @@ u16 fromwire_u16(const u8 **cursor UNNEEDED, size_t *max UNNEEDED)
/* 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 is_p2sh */
bool is_p2sh(const u8 *script UNNEEDED, struct ripemd160 *addr UNNEEDED)
{ fprintf(stderr, "is_p2sh called!\n"); abort(); }
/* Generated stub for is_p2wpkh */
bool is_p2wpkh(const u8 *script UNNEEDED, struct bitcoin_address *addr UNNEEDED)
{ fprintf(stderr, "is_p2wpkh called!\n"); abort(); }
/* Generated stub for is_p2wsh */
bool is_p2wsh(const u8 *script UNNEEDED, struct sha256 *addr UNNEEDED)
{ fprintf(stderr, "is_p2wsh called!\n"); abort(); }
/* Generated stub for pubkey_to_der */
void pubkey_to_der(u8 der[PUBKEY_CMPR_LEN] UNNEEDED, const struct pubkey *key UNNEEDED)
{ fprintf(stderr, "pubkey_to_der called!\n"); abort(); }
/* Generated stub for pubkey_to_hash160 */
void pubkey_to_hash160(const struct pubkey *pk UNNEEDED, struct ripemd160 *hash UNNEEDED)
{ fprintf(stderr, "pubkey_to_hash160 called!\n"); abort(); }
/* Generated stub for scriptpubkey_p2wsh */
u8 *scriptpubkey_p2wsh(const tal_t *ctx UNNEEDED, const u8 *witnessscript UNNEEDED)
{ fprintf(stderr, "scriptpubkey_p2wsh called!\n"); abort(); }
/* Generated stub for towire_amount_sat */
void towire_amount_sat(u8 **pptr UNNEEDED, const struct amount_sat sat UNNEEDED)
{ fprintf(stderr, "towire_amount_sat called!\n"); abort(); }
Expand Down
Loading