Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
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
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ src/test_bech32*
src/test_blech32*
src/test_clear*
src/test_tx*
src/test_psbt*
src/test_elements_tx*
src/test-suite.log
src/swig_java/swig_java_wrap.c
Expand Down
5 changes: 4 additions & 1 deletion include/wally_psbt.h
Original file line number Diff line number Diff line change
Expand Up @@ -509,7 +509,10 @@ WALLY_CORE_API int wally_psbt_get_length(
* :param psbt: the PSBT to serialize.
* :param bytes_out: Bytes to create the transaction from.
* :param bytes_len: Length of ``bytes`` in bytes (use `wally_psbt_get_length`).
* :param bytes_written: number of bytes written to bytes_out
* :param bytes_written: number of bytes written to bytes_out.
*
* If @bytes_len is insufficient, this will return WALLY_EINVAL, but
* @bytes_written will be filled in the the amount which would be required.
*/
WALLY_CORE_API int wally_psbt_to_bytes(
const struct wally_psbt *psbt,
Expand Down
11 changes: 11 additions & 0 deletions src/Makefile.am
Original file line number Diff line number Diff line change
Expand Up @@ -185,6 +185,7 @@ libwallycore_la_SOURCES = \
mnemonic.c \
pbkdf2.c \
psbt.c \
pullpush.c \
script.c \
scrypt.c \
sign.c \
Expand Down Expand Up @@ -225,6 +226,16 @@ noinst_PROGRAMS += test_bech32
test_bech32_SOURCES = ctest/test_bech32.c
test_bech32_CFLAGS = -I$(top_srcdir)/include $(AM_CFLAGS)
test_bech32_LDADD = $(lib_LTLIBRARIES) @CTEST_EXTRA_STATIC@
TESTS += test_psbt
noinst_PROGRAMS += test_psbt
test_psbt_SOURCES = ctest/test_psbt.c ccan/ccan/str/hex/hex.c
test_psbt_CFLAGS = -I$(top_srcdir)/include $(AM_CFLAGS) -I$(srcdir)/ccan
test_psbt_LDADD = $(lib_LTLIBRARIES) @CTEST_EXTRA_STATIC@
TESTS += test_psbt_limits
noinst_PROGRAMS += test_psbt_limits
test_psbt_limits_SOURCES = ctest/test_psbt_limits.c ccan/ccan/str/hex/hex.c
test_psbt_limits_CFLAGS = -I$(top_srcdir)/include $(AM_CFLAGS) -I$(srcdir)/ccan
test_psbt_limits_LDADD = $(lib_LTLIBRARIES) @CTEST_EXTRA_STATIC@
if USE_PTHREAD
TESTS += test_clear
noinst_PROGRAMS += test_clear
Expand Down
299 changes: 299 additions & 0 deletions src/ctest/psbts.h

Large diffs are not rendered by default.

96 changes: 96 additions & 0 deletions src/ctest/test_psbt.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
#include "config.h"

#include <wally_psbt.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <stdbool.h>
#include <err.h>
#include <ccan/str/hex/hex.h>

#include "psbts.h"

static size_t mallocs, frees;

static void *test_malloc(size_t size)
{
mallocs++;
return malloc(size);
}

static void test_free(void *ptr)
{
if (ptr != NULL)
frees++;
free(ptr);
}

static const struct wally_operations test_ops = {
test_malloc, test_free, NULL, NULL
};

int main(void)
{
size_t i;

wally_set_operations(&test_ops);

for (i = 0; i < sizeof(invalid_psbts) / sizeof(invalid_psbts[0]); i++) {
struct wally_psbt *psbt;

mallocs = frees = 0;
if (wally_psbt_from_base64(invalid_psbts[i].base64, &psbt) != WALLY_OK) {
if (mallocs != frees) {
errx(1, "Memleak failing parse psbt %s: %zu mallocs, %zu frees",
invalid_psbts[i].base64, mallocs, frees);
}
continue;
}
errx(1, "Should have failed to parse psbt %s", invalid_psbts[i].base64);
}

for (i = 0; i < sizeof(valid_psbts) / sizeof(valid_psbts[0]); i++) {
struct wally_psbt *psbt;
char *output;
unsigned char *bytes;
size_t len, actual_len;

mallocs = frees = 0;
if (wally_psbt_from_base64(valid_psbts[i].base64, &psbt) != WALLY_OK) {
errx(1, "Failed to parse psbt %s", valid_psbts[i].base64);
}
if (wally_psbt_to_base64(psbt, &output) != WALLY_OK) {
errx(1, "Failed to base64 psbt %s", valid_psbts[i].base64);
}
if (strcmp(output, valid_psbts[i].base64) != 0) {
errx(1, "psbt %s turned into %s?", valid_psbts[i].base64, output);
}
test_free(output);
if (wally_psbt_get_length(psbt, &len) != WALLY_OK) {
errx(1, "Failed to get pbst %s len", valid_psbts[i].base64);
}
bytes = malloc(len);
if (wally_psbt_to_bytes(psbt, bytes, len, &actual_len) != WALLY_OK) {
errx(1, "psbt %s could not to_bytes?", valid_psbts[i].base64);
}
if (len != actual_len) {
errx(1, "psbt %s to_bytes to %zu not %zu?", valid_psbts[i].base64,
actual_len, len);
}
output = malloc(hex_str_size(len));
hex_encode(bytes, len, output, hex_str_size(len));
if (strcmp(output, valid_psbts[i].hex) != 0) {
errx(1, "psbt[%zi] bytes %s not %s", i, output, valid_psbts[i].hex);
}
free(bytes);
free(output);
wally_psbt_free(psbt);

if (mallocs != frees) {
errx(1, "Memleak parsing psbt %s: %zu mallocs, %zu frees",
valid_psbts[i].base64, mallocs, frees);
}
}

return 0;
}
144 changes: 144 additions & 0 deletions src/ctest/test_psbt_limits.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,144 @@
/* This is a superset of test_psbt, but requires mmap */
#include "config.h"

#include <wally_psbt.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <stdbool.h>
#include <err.h>
#include <ccan/str/hex/hex.h>
#include <sys/mman.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <assert.h>

#include "psbts.h"

static size_t mallocs, frees;

static void *test_malloc(size_t size)
{
mallocs++;
return malloc(size);
}

static void test_free(void *ptr)
{
if (ptr != NULL)
frees++;
free(ptr);
}

static const struct wally_operations test_ops = {
test_malloc, test_free, NULL, NULL
};

/* Create a cliff: any access past the end will SEGV */
static unsigned char *cliff(size_t *size)
{
unsigned char *p;

/* One page is enough for our tests so far */
*size = getpagesize();

/* MAP_ANON isn't POSIX, but MacOS doesn't let us mmap /dev/zero */
p = mmap(NULL, *size + getpagesize(),
PROT_READ | PROT_WRITE, MAP_ANON | MAP_PRIVATE, -1, 0);
if (p == MAP_FAILED)
err(1, "Failed to mmap anon");

/* Remove second page. */
if (munmap(p + *size, getpagesize()) != 0)
err(1, "Failed to munmap /dev/zero");
return p;
}

/* Test that we don't read past end of buffer when unmarshalling */
static void test_psbt_read(const struct psbt_test *test,
unsigned char *p, size_t plen)
{
size_t i;

/* It can fit, otherwise adjust cliff() */
assert(hex_data_size(strlen(test->hex)) <= plen);

/* Unpack right next to the cliff */
for (i = 0; i <= hex_data_size(strlen(test->hex)); i++) {
struct wally_psbt *psbt;
size_t bit;

if (!hex_decode(test->hex, i * 2, p + plen - i, i))
abort();

/* Try it raw: probably will fail. */
mallocs = frees = 0;
if (wally_psbt_from_bytes(p + plen - i, i, &psbt) == WALLY_OK)
wally_psbt_free(psbt);
if (mallocs != frees) {
errx(1, "psbt %s length %zu: mallocs = %zu, frees = %zu",
test->base64, i, mallocs, frees);
}

/* Now try flipping each bit in last byte. */
for (bit = 0; bit < 8; bit++) {
p[plen - 1] ^= (1 << bit);
mallocs = frees = 0;
if (wally_psbt_from_bytes(p + plen - i, i, &psbt) == WALLY_OK)
wally_psbt_free(psbt);
if (mallocs != frees) {
errx(1, "psbt %s length %zu bitfplip %zu: mallocs = %zu, frees = %zu",
test->base64, i, bit, mallocs, frees);
}
p[plen - 1] ^= (1 << bit);
}
}
}

/* Test that we don't write past end of buffer when marshaling */
static void test_psbt_write(const struct psbt_test *test,
unsigned char *p, size_t plen)
{
size_t i, written;
struct wally_psbt *psbt;

mallocs = frees = 0;
if (wally_psbt_from_base64(test->base64, &psbt) != WALLY_OK)
abort();

for (i = 0;; i++) {
if (wally_psbt_to_bytes(psbt, p + plen - i, i, &written) == WALLY_OK)
break;
}
/* Should have fit exactly */
if (written != i)
errx(1, "wally_psbt_to_bytes %s wrote %zu in %zu bytes?",
test->base64, written, i);
wally_psbt_free(psbt);
if (mallocs != frees) {
errx(1, "psbt write %s: mallocs = %zu, frees = %zu",
test->base64, mallocs, frees);
}
}

int main(void)
{
size_t i;
size_t plen;
unsigned char *p = cliff(&plen);

wally_set_operations(&test_ops);

for (i = 0; i < sizeof(invalid_psbts) / sizeof(invalid_psbts[0]); i++) {
test_psbt_read(invalid_psbts + i, p, plen);
}

for (i = 0; i < sizeof(valid_psbts) / sizeof(valid_psbts[0]); i++) {
test_psbt_read(valid_psbts + i, p, plen);
test_psbt_write(valid_psbts + i, p, plen);
}

return 0;
}
Loading