Skip to content

Commit cd99044

Browse files
committed
psbt: support finalization of Green csv/optimized csv inputs
Includes support for optimized single signature recovery signing.
1 parent 7db34e7 commit cd99044

File tree

1 file changed

+81
-0
lines changed

1 file changed

+81
-0
lines changed

src/psbt.c

Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4743,6 +4743,7 @@ static bool finalize_multisig(struct wally_psbt_input *input,
47434743
sigs, n_found * EC_SIGNATURE_LEN,
47444744
sighashes, n_found, 0,
47454745
script, sizeof(script), &script_len) != WALLY_OK ||
4746+
script_len > sizeof(script) ||
47464747
wally_psbt_input_set_final_scriptsig(input, script, script_len) != WALLY_OK)
47474748
goto fail;
47484749
}
@@ -4767,6 +4768,79 @@ static bool finalize_p2tr(struct wally_psbt_input *input)
47674768
return true;
47684769
}
47694770

4771+
static bool is_input_csv_expired(const struct wally_psbt_input *input, uint32_t blocks)
4772+
{
4773+
if (input->sequence & ((1 << 31) | (1 << 22)))
4774+
return false; /* Locktime opt-out enabled, or time-based lock */
4775+
/* Note we don't mask out the locktime value, because we don't want to
4776+
* finalize inputs if a soft-fork we don't know about has changed the
4777+
* meaning of locktime extra bits in a way we don't understand.
4778+
*/
4779+
return blocks <= input->sequence;
4780+
}
4781+
4782+
static bool finalize_csv2of2_1(const struct wally_psbt *psbt,
4783+
struct wally_psbt_input *input,
4784+
const unsigned char *out_script, size_t out_script_len,
4785+
bool is_witness, bool is_p2sh, bool is_optimized)
4786+
{
4787+
const struct wally_map_item *sig_1, *sig_2, empty = { 0 };
4788+
const unsigned char *pk_1, *pk_2;
4789+
const uint32_t tx_version = psbt->tx ? psbt->tx->version : psbt->tx_version;
4790+
uint32_t blocks;
4791+
bool is_expired;
4792+
4793+
if (!is_witness)
4794+
return false; /* Only supported for segwit inputs */
4795+
4796+
if (wally_scriptpubkey_csv_blocks_from_csv_2of2_then_1(out_script, out_script_len,
4797+
&blocks) != WALLY_OK)
4798+
return false;
4799+
4800+
is_expired = tx_version >= 2 && is_input_csv_expired(input, blocks);
4801+
4802+
if (is_optimized) {
4803+
pk_1 = out_script + 1;
4804+
pk_2 = out_script + 1 + EC_PUBLIC_KEY_LEN + 1 + 1;
4805+
} else {
4806+
pk_1 = out_script + 4;
4807+
pk_2 = out_script + out_script_len - 1 - EC_PUBLIC_KEY_LEN;
4808+
if (is_expired) {
4809+
pk_1 = pk_2;
4810+
}
4811+
}
4812+
4813+
sig_1 = wally_map_get(&input->signatures, pk_1, EC_PUBLIC_KEY_LEN);
4814+
if (is_expired) {
4815+
sig_2 = &empty; /* Expired, spend with an empty witness element */
4816+
} else {
4817+
sig_2 = wally_map_get(&input->signatures, pk_2, EC_PUBLIC_KEY_LEN);
4818+
}
4819+
4820+
if (!sig_1 || !sig_2)
4821+
return false; /* Missing required signature(s) */
4822+
4823+
if (is_optimized) {
4824+
/* Swap the order of the sigs */
4825+
const struct wally_map_item *tmp = sig_1;
4826+
sig_1 = sig_2;
4827+
sig_2 = tmp;
4828+
}
4829+
4830+
if (wally_tx_witness_stack_init_alloc(3, &input->final_witness) != WALLY_OK)
4831+
return false;
4832+
4833+
if (wally_tx_witness_stack_add(input->final_witness, sig_1->value, sig_1->value_len) == WALLY_OK &&
4834+
wally_tx_witness_stack_add(input->final_witness, sig_2->value, sig_2->value_len) == WALLY_OK &&
4835+
wally_tx_witness_stack_add(input->final_witness, out_script, out_script_len) == WALLY_OK) {
4836+
if (!is_p2sh || finalize_p2sh_wrapped(input))
4837+
return true;
4838+
}
4839+
wally_tx_witness_stack_free(input->final_witness);
4840+
input->final_witness = NULL;
4841+
return false;
4842+
}
4843+
47704844
int wally_psbt_finalize_input(struct wally_psbt *psbt, size_t index, uint32_t flags)
47714845
{
47724846
struct wally_psbt_input *input = psbt_get_input(psbt, index);
@@ -4836,6 +4910,13 @@ int wally_psbt_finalize_input(struct wally_psbt *psbt, size_t index, uint32_t fl
48364910
if (!finalize_p2tr(input))
48374911
return WALLY_OK;
48384912
break;
4913+
case WALLY_SCRIPT_TYPE_CSV2OF2_1:
4914+
case WALLY_SCRIPT_TYPE_CSV2OF2_1_OPT:
4915+
if (!finalize_csv2of2_1(psbt, input, out_script, out_script_len,
4916+
is_witness, is_p2sh,
4917+
type == WALLY_SCRIPT_TYPE_CSV2OF2_1_OPT))
4918+
return WALLY_OK;
4919+
break;
48394920
default:
48404921
return WALLY_OK; /* Unhandled script type */
48414922
}

0 commit comments

Comments
 (0)