Skip to content

Commit 455ec5b

Browse files
committed
tx: pre-allocate the full number of witnesses when deserializing
Prevents quadratic resizing for deserializing non-standard txs with many witnesses. Note in the transaction.c case, analyze_tx has already run and validated that the number of witnesses is sane.
1 parent 9e8f945 commit 455ec5b

3 files changed

Lines changed: 28 additions & 9 deletions

File tree

src/internal.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -132,6 +132,12 @@ const struct wally_map_item *map_find_equal_integer(const struct wally_map *lhs,
132132
/* Clamp initial witness stack allocation sizing */
133133
#define MAX_WITNESS_ITEMS_ALLOC 100u /* Non-Taproot standardness limit */
134134

135+
/* Allows allocating a larger witness for e.g deserializing */
136+
struct wally_tx_witness_stack;
137+
int tx_witness_stack_init_alloc(size_t allocation_len,
138+
size_t max_allocation_len,
139+
struct wally_tx_witness_stack **output);
140+
135141
/* Absolute maximum number of inputs and outputs for BTC.
136142
* Liquid numbers are smaller; we use the upper limit */
137143
#define TX_MAX_INPUTS (TX_MAX_INPUTS_ALLOC * 10)

src/pullpush.c

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -260,7 +260,8 @@ int pull_witness(const unsigned char **cursor, size_t *max,
260260
/* Not enough bytes remaining for num_witnesses empty witnesses */
261261
return WALLY_EINVAL;
262262
}
263-
ret = wally_tx_witness_stack_init_alloc(num_witnesses, witness_out);
263+
ret = tx_witness_stack_init_alloc(num_witnesses, num_witnesses,
264+
witness_out);
264265

265266
for (i = 0; ret == WALLY_OK && i < num_witnesses; ++i) {
266267
const unsigned char *wit;

src/transaction.c

Lines changed: 20 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -138,7 +138,8 @@ int wally_tx_witness_stack_clone_alloc(const struct wally_tx_witness_stack *stac
138138
if (!stack)
139139
return WALLY_EINVAL;
140140

141-
ret = wally_tx_witness_stack_init_alloc(stack->items_allocation_len, output);
141+
ret = tx_witness_stack_init_alloc(stack->items_allocation_len,
142+
stack->items_allocation_len, output);
142143
for (i = 0; ret == WALLY_OK && i < stack->num_items; ++i) {
143144
ret = wally_tx_witness_stack_set(*output, i,
144145
stack->items[i].witness,
@@ -151,15 +152,16 @@ int wally_tx_witness_stack_clone_alloc(const struct wally_tx_witness_stack *stac
151152
return ret;
152153
}
153154

154-
int wally_tx_witness_stack_init_alloc(size_t allocation_len,
155-
struct wally_tx_witness_stack **output)
155+
int tx_witness_stack_init_alloc(size_t allocation_len,
156+
size_t max_allocation_len,
157+
struct wally_tx_witness_stack **output)
156158
{
157159
OUTPUT_CHECK;
158160
OUTPUT_ALLOC(struct wally_tx_witness_stack);
159161

160162
if (allocation_len) {
161-
if (allocation_len > MAX_WITNESS_ITEMS_ALLOC)
162-
allocation_len = MAX_WITNESS_ITEMS_ALLOC;
163+
if (allocation_len > max_allocation_len)
164+
allocation_len = max_allocation_len;
163165
(*output)->items = wally_calloc(allocation_len * sizeof(struct wally_tx_witness_item));
164166
if (!(*output)->items) {
165167
wally_free(*output);
@@ -172,6 +174,16 @@ int wally_tx_witness_stack_init_alloc(size_t allocation_len,
172174
return WALLY_OK;
173175
}
174176

177+
int wally_tx_witness_stack_init_alloc(size_t allocation_len,
178+
struct wally_tx_witness_stack **output)
179+
{
180+
/* The public interface is limited to pre-allocating enough
181+
* witness items for a standard tx, and will be slow if adding more
182+
*/
183+
return tx_witness_stack_init_alloc(allocation_len,
184+
MAX_WITNESS_ITEMS_ALLOC, output);
185+
}
186+
175187
static int tx_witness_stack_free(struct wally_tx_witness_stack *stack,
176188
bool free_parent)
177189
{
@@ -2371,7 +2383,7 @@ static int witness_stack_from_bytes(const unsigned char *bytes, struct wally_tx_
23712383
const unsigned char *p = bytes;
23722384
p += varint_from_bytes(p, &num_witnesses);
23732385
if (num_witnesses) {
2374-
ret = wally_tx_witness_stack_init_alloc(num_witnesses, witness);
2386+
ret = tx_witness_stack_init_alloc(num_witnesses, num_witnesses, witness);
23752387
if (ret != WALLY_OK)
23762388
goto cleanup;
23772389

@@ -2485,8 +2497,8 @@ static int tx_from_bytes(const unsigned char *bytes, size_t bytes_len,
24852497
p += varint_from_bytes(p, &num_witnesses);
24862498
if (!num_witnesses)
24872499
continue;
2488-
ret = wally_tx_witness_stack_init_alloc(num_witnesses,
2489-
&(*output)->inputs[i].witness);
2500+
ret = tx_witness_stack_init_alloc(num_witnesses, num_witnesses,
2501+
&(*output)->inputs[i].witness);
24902502
if (ret != WALLY_OK)
24912503
goto fail;
24922504

0 commit comments

Comments
 (0)