diff --git a/include/secp256k1_bulletproofs.h b/include/secp256k1_bulletproofs.h index 889d790b..1ddd9699 100644 --- a/include/secp256k1_bulletproofs.h +++ b/include/secp256k1_bulletproofs.h @@ -9,7 +9,62 @@ extern "C" { #include -/* TODO */ +/** Opaque structure representing a large number of NUMS generators */ +typedef struct secp256k1_bulletproofs_generators secp256k1_bulletproofs_generators; + +/** Allocates and initializes a list of NUMS generators. + * Returns a list of generators, or calls the error callback if the allocation fails. + * Args: ctx: pointer to a context object + * n: number of NUMS generators to produce. + * + * TODO: In a followup range-proof PR, this is would still require 16 + 8 = 24 NUMS + * points. We will later use G = H0(required for compatibility with pedersen_commitment DS) + * in a separate commit to make review easier. + */ +SECP256K1_API secp256k1_bulletproofs_generators *secp256k1_bulletproofs_generators_create( + const secp256k1_context* ctx, + size_t n +) SECP256K1_ARG_NONNULL(1); + +/** Allocates a list of generators from a static array + * Returns a list of generators or NULL in case of failure. + * Args: ctx: pointer to a context object + * In: data: data that came from `secp256k1_bulletproofs_generators_serialize` + * data_len: the length of the `data` buffer + */ +SECP256K1_API secp256k1_bulletproofs_generators* secp256k1_bulletproofs_generators_parse( + const secp256k1_context* ctx, + const unsigned char* data, + size_t data_len +) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2); + +/** Serializes a list of generators to an array + * Returns 1 on success, 0 if the provided array was not large enough + * Args: ctx: pointer to a context object + * gen: pointer to the generator set to be serialized + * Out: data: pointer to buffer into which the generators will be serialized + * In/Out: data_len: the length of the `data` buffer. Should be at least + * k = 33 * num_gens. Will be set to k on successful return + * + * TODO: For ease of review, this setting G = H0 is not included in this commit. We will + * add it in the follow-up rangeproof PR. + */ +SECP256K1_API int secp256k1_bulletproofs_generators_serialize( + const secp256k1_context* ctx, + const secp256k1_bulletproofs_generators* gen, + unsigned char* data, + size_t *data_len +) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3) SECP256K1_ARG_NONNULL(4); + +/** Destroys a list of NUMS generators, freeing allocated memory + * Args: ctx: pointer to a context object + * gen: pointer to the generator set to be destroyed + * (can be NULL, in which case this function is a no-op) + */ +SECP256K1_API void secp256k1_bulletproofs_generators_destroy( + const secp256k1_context* ctx, + secp256k1_bulletproofs_generators* gen +) SECP256K1_ARG_NONNULL(1); # ifdef __cplusplus } diff --git a/src/modules/bulletproofs/main_impl.h b/src/modules/bulletproofs/main_impl.h index 9c61eaf7..ef0ac78c 100644 --- a/src/modules/bulletproofs/main_impl.h +++ b/src/modules/bulletproofs/main_impl.h @@ -7,6 +7,113 @@ #ifndef _SECP256K1_MODULE_BULLETPROOFS_MAIN_ #define _SECP256K1_MODULE_BULLETPROOFS_MAIN_ -/* TODO */ +#include "include/secp256k1_bulletproofs.h" +#include "include/secp256k1_generator.h" +#include "modules/generator/main_impl.h" /* for generator_{load, save} */ +#include "hash.h" +#include "util.h" + +struct secp256k1_bulletproofs_generators { + size_t n; + /* n total generators; includes both G_i and H_i */ + secp256k1_ge* gens; +}; + +secp256k1_bulletproofs_generators *secp256k1_bulletproofs_generators_create(const secp256k1_context *ctx, size_t n) { + secp256k1_bulletproofs_generators *ret; + secp256k1_rfc6979_hmac_sha256 rng; + unsigned char seed[64]; + size_t i; + + VERIFY_CHECK(ctx != NULL); + + ret = (secp256k1_bulletproofs_generators *)checked_malloc(&ctx->error_callback, sizeof(*ret)); + if (ret == NULL) { + return NULL; + } + ret->gens = (secp256k1_ge*)checked_malloc(&ctx->error_callback, n * sizeof(*ret->gens)); + if (ret->gens == NULL) { + free(ret); + return NULL; + } + ret->n = n; + + secp256k1_fe_get_b32(&seed[0], &secp256k1_ge_const_g.x); + secp256k1_fe_get_b32(&seed[32], &secp256k1_ge_const_g.y); + + secp256k1_rfc6979_hmac_sha256_initialize(&rng, seed, 64); + for (i = 0; i < n; i++) { + secp256k1_generator gen; + unsigned char tmp[32] = { 0 }; + secp256k1_rfc6979_hmac_sha256_generate(&rng, tmp, 32); + CHECK(secp256k1_generator_generate(ctx, &gen, tmp)); + secp256k1_generator_load(&ret->gens[i], &gen); + } + + return ret; +} + +secp256k1_bulletproofs_generators* secp256k1_bulletproofs_generators_parse(const secp256k1_context* ctx, const unsigned char* data, size_t data_len) { + size_t n = data_len / 33; + secp256k1_bulletproofs_generators* ret; + + VERIFY_CHECK(ctx != NULL); + ARG_CHECK(data != NULL); + + if (data_len % 33 != 0) { + return NULL; + } + + ret = (secp256k1_bulletproofs_generators *)checked_malloc(&ctx->error_callback, sizeof(*ret)); + if (ret == NULL) { + return NULL; + } + ret->n = n; + ret->gens = (secp256k1_ge*)checked_malloc(&ctx->error_callback, n * sizeof(*ret->gens)); + if (ret->gens == NULL) { + free(ret); + return NULL; + } + + while (n--) { + secp256k1_generator gen; + if (!secp256k1_generator_parse(ctx, &gen, &data[33 * n])) { + free(ret->gens); + free(ret); + return NULL; + } + secp256k1_generator_load(&ret->gens[n], &gen); + } + return ret; +} + +int secp256k1_bulletproofs_generators_serialize(const secp256k1_context* ctx, const secp256k1_bulletproofs_generators* gens, unsigned char* data, size_t *data_len) { + size_t i; + + VERIFY_CHECK(ctx != NULL); + ARG_CHECK(gens != NULL); + ARG_CHECK(data != NULL); + ARG_CHECK(data_len != NULL); + ARG_CHECK(*data_len >= 33 * gens->n); + + memset(data, 0, *data_len); + for (i = 0; i < gens->n; i++) { + secp256k1_generator gen; + secp256k1_generator_save(&gen, &gens->gens[i]); + secp256k1_generator_serialize(ctx, &data[33 * i], &gen); + } + + *data_len = 33 * gens->n; + return 1; +} + +void secp256k1_bulletproofs_generators_destroy(const secp256k1_context* ctx, secp256k1_bulletproofs_generators *gens) { + VERIFY_CHECK(ctx != NULL); + (void) ctx; + if (gens != NULL) { + free(gens->gens); + free(gens); + } +} #endif diff --git a/src/modules/bulletproofs/tests_impl.h b/src/modules/bulletproofs/tests_impl.h index 0c88fb78..f75b7294 100644 --- a/src/modules/bulletproofs/tests_impl.h +++ b/src/modules/bulletproofs/tests_impl.h @@ -7,8 +7,116 @@ #ifndef _SECP256K1_MODULE_BULLETPROOFS_TEST_ #define _SECP256K1_MODULE_BULLETPROOFS_TEST_ +static void test_bulletproofs_generators_api(void) { + /* The BP generator API requires no precomp */ + secp256k1_context *none = secp256k1_context_create(SECP256K1_CONTEXT_NONE); + + secp256k1_bulletproofs_generators *gens; + secp256k1_bulletproofs_generators *gens_orig; + unsigned char gens_ser[330]; + size_t len = sizeof(gens_ser); + + int32_t ecount = 0; + + secp256k1_context_set_error_callback(none, counting_illegal_callback_fn, &ecount); + secp256k1_context_set_illegal_callback(none, counting_illegal_callback_fn, &ecount); + + /* Create */ + gens = secp256k1_bulletproofs_generators_create(none, 10); + CHECK(gens != NULL && ecount == 0); + gens_orig = gens; /* Preserve for round-trip test */ + + /* Serialize */ + ecount = 0; + CHECK(!secp256k1_bulletproofs_generators_serialize(none, NULL, gens_ser, &len)); + CHECK(ecount == 1); + CHECK(!secp256k1_bulletproofs_generators_serialize(none, gens, NULL, &len)); + CHECK(ecount == 2); + CHECK(!secp256k1_bulletproofs_generators_serialize(none, gens, gens_ser, NULL)); + CHECK(ecount == 3); + len = 0; + CHECK(!secp256k1_bulletproofs_generators_serialize(none, gens, gens_ser, &len)); + CHECK(ecount == 4); + len = sizeof(gens_ser) - 1; + CHECK(!secp256k1_bulletproofs_generators_serialize(none, gens, gens_ser, &len)); + CHECK(ecount == 5); + len = sizeof(gens_ser); + { + /* Output buffer can be greater than minimum needed */ + unsigned char gens_ser_tmp[331]; + size_t len_tmp = sizeof(gens_ser_tmp); + CHECK(secp256k1_bulletproofs_generators_serialize(none, gens, gens_ser_tmp, &len_tmp)); + CHECK(len_tmp == sizeof(gens_ser_tmp) - 1); + CHECK(ecount == 5); + } + + /* Parse */ + CHECK(secp256k1_bulletproofs_generators_serialize(none, gens, gens_ser, &len)); + ecount = 0; + gens = secp256k1_bulletproofs_generators_parse(none, NULL, sizeof(gens_ser)); + CHECK(gens == NULL && ecount == 1); + /* Not a multiple of 33 */ + gens = secp256k1_bulletproofs_generators_parse(none, gens_ser, sizeof(gens_ser) - 1); + CHECK(gens == NULL && ecount == 1); + gens = secp256k1_bulletproofs_generators_parse(none, gens_ser, sizeof(gens_ser)); + CHECK(gens != NULL && ecount == 1); + /* Not valid generators */ + memset(gens_ser, 1, sizeof(gens_ser)); + CHECK(secp256k1_bulletproofs_generators_parse(none, gens_ser, sizeof(gens_ser)) == NULL); + CHECK(ecount == 1); + + /* Check that round-trip succeeded */ + CHECK(gens->n == gens_orig->n); + for (len = 0; len < gens->n; len++) { + ge_equals_ge(&gens->gens[len], &gens_orig->gens[len]); + } + + /* Destroy (we allow destroying a NULL context, it's just a noop. like free().) */ + ecount = 0; + secp256k1_bulletproofs_generators_destroy(none, NULL); + secp256k1_bulletproofs_generators_destroy(none, gens); + secp256k1_bulletproofs_generators_destroy(none, gens_orig); + CHECK(ecount == 0); + + secp256k1_context_destroy(none); +} + +static void test_bulletproofs_generators_fixed(void) { + secp256k1_bulletproofs_generators *gens = secp256k1_bulletproofs_generators_create(ctx, 3); + unsigned char gens_ser[330]; + const unsigned char fixed_first_3[99] = { + 0x0b, + 0xb3, 0x4d, 0x5f, 0xa6, 0xb8, 0xf3, 0xd1, 0x38, + 0x49, 0xce, 0x51, 0x91, 0xb7, 0xf6, 0x76, 0x18, + 0xfe, 0x5b, 0xd1, 0x2a, 0x88, 0xb2, 0x0e, 0xac, + 0x33, 0x89, 0x45, 0x66, 0x7f, 0xb3, 0x30, 0x56, + 0x0a, + 0x62, 0x86, 0x15, 0x16, 0x92, 0x42, 0x10, 0x9e, + 0x9e, 0x64, 0xd4, 0xcb, 0x28, 0x81, 0x60, 0x9c, + 0x24, 0xb9, 0x89, 0x51, 0x2a, 0xd9, 0x01, 0xae, + 0xff, 0x75, 0x64, 0x9c, 0x37, 0x5d, 0xbd, 0x79, + 0x0a, + 0xed, 0xe0, 0x6e, 0x07, 0x5e, 0x79, 0xd0, 0xf7, + 0x7b, 0x03, 0x3e, 0xb9, 0xa9, 0x21, 0xa4, 0x5b, + 0x99, 0xf3, 0x9b, 0xee, 0xfe, 0xa0, 0x37, 0xa2, + 0x1f, 0xe9, 0xd7, 0x4f, 0x95, 0x8b, 0x10, 0xe2, + }; + size_t len; + + len = 99; + CHECK(secp256k1_bulletproofs_generators_serialize(ctx, gens, gens_ser, &len)); + CHECK(memcmp(gens_ser, fixed_first_3, sizeof(fixed_first_3)) == 0); + + len = sizeof(gens_ser); + CHECK(secp256k1_bulletproofs_generators_serialize(ctx, gens, gens_ser, &len)); + CHECK(memcmp(gens_ser, fixed_first_3, sizeof(fixed_first_3)) == 0); + + secp256k1_bulletproofs_generators_destroy(ctx, gens); +} + void run_bulletproofs_tests(void) { - /* TODO */ + test_bulletproofs_generators_api(); + test_bulletproofs_generators_fixed(); } #endif