Skip to content

Commit

Permalink
Merge pull request #24 from jaspervdm/bp_multisig
Browse files Browse the repository at this point in the history
Multi-party bulletproof
  • Loading branch information
yeastplume committed Sep 16, 2018
2 parents e6f1191 + 883d223 commit f1b4f08
Show file tree
Hide file tree
Showing 4 changed files with 257 additions and 76 deletions.
22 changes: 16 additions & 6 deletions include/secp256k1_bulletproofs.h
Expand Up @@ -142,36 +142,46 @@ SECP256K1_WARN_UNUSED_RESULT SECP256K1_API int secp256k1_bulletproof_rangeproof_
* Args: ctx: pointer to a context object initialized for signing and verification (cannot be NULL)
* scratch: scratch space with enough memory for verification (cannot be NULL)
* gens: generator set with at least 2*nbits*n_commits many generators (cannot be NULL)
* Out: proof: byte-serialized rangeproof (cannot be NULL)
* In/out: plen: pointer to size of `proof`, to be replaced with actual length of proof (cannot be NULL)
* Out: proof: byte-serialized rangeproof
* In/out: plen: pointer to size of `proof`, to be replaced with actual length of proof
* tau_x: only for multi-party; 32-byte, output in second step or input in final step
* t_one: only for multi-party; public key, output in first step or input for the others
* t_two: only for multi-party; public key, output in first step or input for the others
* In: value: array of values committed by the Pedersen commitments (cannot be NULL)
* min_value: array of minimum values to prove ranges above, or NULL for all-zeroes
* blind: array of blinding factors of the Pedersen commitments (cannot be NULL)
* commits: only for multi-party; array of pointers to commitments
* n_commits: number of entries in the `value` and `blind` arrays
* value_gen: generator multiplied by value in pedersen commitments (cannot be NULL)
* nbits: number of bits proven for each range
* nonce: random 32-byte seed used to derive blinding factors (cannot be NULL)
* private_nonce: only for multi-party; random 32-byte seed used to derive private blinding factors
* extra_commit: additonal data committed to by the rangeproof
* extra_commit_len: length of additional data
* message: optional 16 bytes of message that can be recovered by rewinding with the correct nonce
*/
SECP256K1_WARN_UNUSED_RESULT SECP256K1_API int secp256k1_bulletproof_rangeproof_prove(
const secp256k1_context* ctx,
secp256k1_scratch_space* scratch,
const secp256k1_bulletproof_generators *gens,
const secp256k1_bulletproof_generators* gens,
unsigned char* proof,
size_t* plen,
const uint64_t *value,
const uint64_t *min_value,
unsigned char* tau_x,
secp256k1_pubkey* t_one,
secp256k1_pubkey* t_two,
const uint64_t* value,
const uint64_t* min_value,
const unsigned char* const* blind,
const secp256k1_pedersen_commitment* const* commits,
size_t n_commits,
const secp256k1_generator* value_gen,
size_t nbits,
const unsigned char* nonce,
const unsigned char* private_nonce,
const unsigned char* extra_commit,
size_t extra_commit_len,
const unsigned char* message
) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3) SECP256K1_ARG_NONNULL(4) SECP256K1_ARG_NONNULL(5) SECP256K1_ARG_NONNULL(6) SECP256K1_ARG_NONNULL(8) SECP256K1_ARG_NONNULL(10) SECP256K1_ARG_NONNULL(12);
) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3) SECP256K1_ARG_NONNULL(9) SECP256K1_ARG_NONNULL(11) SECP256K1_ARG_NONNULL(14) SECP256K1_ARG_NONNULL(16);

# ifdef __cplusplus
}
Expand Down
62 changes: 55 additions & 7 deletions src/modules/bulletproofs/main_impl.h
Expand Up @@ -182,19 +182,34 @@ int secp256k1_bulletproof_rangeproof_rewind(const secp256k1_context* ctx, const
return ret;
}

int secp256k1_bulletproof_rangeproof_prove(const secp256k1_context* ctx, secp256k1_scratch_space *scratch, const secp256k1_bulletproof_generators *gens, unsigned char *proof, size_t *plen, const uint64_t *value, const uint64_t *min_value, const unsigned char* const* blind, size_t n_commits, const secp256k1_generator *value_gen, size_t nbits, const unsigned char *nonce, const unsigned char *extra_commit, size_t extra_commit_len, const unsigned char *message) {
int secp256k1_bulletproof_rangeproof_prove(
const secp256k1_context* ctx, secp256k1_scratch_space* scratch, const secp256k1_bulletproof_generators* gens,
unsigned char* proof, size_t* plen,
unsigned char* tau_x, secp256k1_pubkey* t_one, secp256k1_pubkey* t_two,
const uint64_t* value, const uint64_t* min_value,
const unsigned char* const* blind, const secp256k1_pedersen_commitment* const* commits, size_t n_commits,
const secp256k1_generator* value_gen, size_t nbits,
const unsigned char* nonce, const unsigned char* private_nonce,
const unsigned char* extra_commit, size_t extra_commit_len, const unsigned char* message
) {
int ret;
secp256k1_ge *commitp;
secp256k1_scalar *blinds;
secp256k1_ge value_genp;
size_t i;
const unsigned char *secondary_nonce;
secp256k1_ge *tge = NULL;

VERIFY_CHECK(ctx != NULL);
ARG_CHECK(scratch != NULL);
ARG_CHECK(gens != NULL);
ARG_CHECK(gens->n >= 2 * nbits * n_commits);
ARG_CHECK(proof != NULL);
ARG_CHECK(plen != NULL);
ARG_CHECK(
(proof != NULL && plen != NULL && tau_x == NULL && t_one == NULL && t_two == NULL && commits == NULL && private_nonce == NULL) ||
(proof == NULL && plen == NULL && tau_x == NULL && t_one != NULL && t_two != NULL && commits != NULL && private_nonce != NULL) ||
(proof == NULL && plen == NULL && tau_x != NULL && t_one != NULL && t_two != NULL && commits != NULL && private_nonce != NULL) ||
(proof != NULL && plen != NULL && tau_x != NULL && t_one != NULL && t_two != NULL && commits != NULL && private_nonce != NULL)
); /* 1) normal BP, 2) multi-party BP step 1, 3) multi-party BP step 2, 4) multi-party BP step 3 */
ARG_CHECK(value != NULL);
ARG_CHECK(blind != NULL);
ARG_CHECK(value_gen != NULL);
Expand All @@ -220,16 +235,49 @@ int secp256k1_bulletproof_rangeproof_prove(const secp256k1_context* ctx, secp256
secp256k1_generator_load(&value_genp, value_gen);
for (i = 0; i < n_commits; i++) {
int overflow;
secp256k1_gej commitj;
secp256k1_scalar_set_b32(&blinds[i], blind[i], &overflow);
if (overflow || secp256k1_scalar_is_zero(&blinds[i])) {
return 0;
}
secp256k1_pedersen_ecmult(&commitj, &blinds[i], value[i], &value_genp, &gens->blinding_gen[0]);
secp256k1_ge_set_gej(&commitp[i], &commitj);

if (commits == NULL) {
/* Calculate commitment from blinding factor */
secp256k1_gej commitj;
secp256k1_pedersen_ecmult(&commitj, &blinds[i], value[i], &value_genp, &gens->blinding_gen[0]);
secp256k1_ge_set_gej(&commitp[i], &commitj);
}
else {
/* Multi-party bulletproof: total blinding factor unknown. Input commitment(s) */
secp256k1_pedersen_commitment_load(&commitp[i], commits[i]);
}
}

if (private_nonce == NULL) {
secondary_nonce = nonce;
}
else {
secondary_nonce = private_nonce;
}

ret = secp256k1_bulletproof_rangeproof_prove_impl(&ctx->ecmult_ctx, scratch, proof, plen, nbits, value, min_value, blinds, commitp, n_commits, &value_genp, gens, nonce, extra_commit, extra_commit_len, message);
if (t_one != NULL) {
tge = malloc(2*sizeof(secp256k1_ge));
if (tau_x != NULL) {
if (!secp256k1_pubkey_load(ctx, &tge[0], t_one)) {
return 0;
}
if (!secp256k1_pubkey_load(ctx, &tge[1], t_two)) {
return 0;
}
}
}

ret = secp256k1_bulletproof_rangeproof_prove_impl(&ctx->ecmult_ctx, scratch, proof, plen, tau_x, tge, nbits, value, min_value, blinds, commitp, n_commits, &value_genp, gens, nonce, secondary_nonce, extra_commit, extra_commit_len, message);

if (t_one != NULL && tau_x == NULL) {
secp256k1_pubkey_save(t_one, &tge[0]);
secp256k1_pubkey_save(t_two, &tge[1]);
}

secp256k1_scratch_deallocate_frame(scratch);
return ret;
}
Expand Down
91 changes: 70 additions & 21 deletions src/modules/bulletproofs/rangeproof_impl.h
Expand Up @@ -434,7 +434,16 @@ static int secp256k1_bulletproof_abgh_callback(secp256k1_scalar *sc, secp256k1_g
* The non-bold `h` in the Bulletproofs paper corresponds to our gens->blinding_gen
* while the non-bold `g` corresponds to the asset type `value_gen`.
*/
static int secp256k1_bulletproof_rangeproof_prove_impl(const secp256k1_ecmult_context *ecmult_ctx, secp256k1_scratch *scratch, unsigned char *proof, size_t *plen, const size_t nbits, const uint64_t *value, const uint64_t *min_value, const secp256k1_scalar *blind, const secp256k1_ge *commitp, size_t n_commits, const secp256k1_ge *value_gen, const secp256k1_bulletproof_generators *gens, const unsigned char *nonce, const unsigned char *extra_commit, size_t extra_commit_len, const unsigned char *message) {
static int secp256k1_bulletproof_rangeproof_prove_impl(
const secp256k1_ecmult_context *ecmult_ctx, secp256k1_scratch *scratch,
unsigned char *proof, size_t *plen,
unsigned char *tauxc, secp256k1_ge *tge,
const size_t nbits, const uint64_t *value, const uint64_t *min_value,
const secp256k1_scalar *blind, const secp256k1_ge *commitp, size_t n_commits,
const secp256k1_ge *value_gen, const secp256k1_bulletproof_generators *gens,
const unsigned char *nonce, const unsigned char *private_nonce,
const unsigned char *extra_commit, size_t extra_commit_len, const unsigned char *message
) {
secp256k1_bulletproof_lr_generator lr_gen;
secp256k1_bulletproof_abgh_data abgh_data;
secp256k1_scalar zero;
Expand All @@ -451,7 +460,7 @@ static int secp256k1_bulletproof_rangeproof_prove_impl(const secp256k1_ecmult_co
secp256k1_gej tmpj;
size_t i, j;
int overflow;
/* inner product proof variables */
/* Inner product proof variables */
secp256k1_ge out_pt[4];

if (POPCOUNT(nbits) != 1 || nbits > MAX_NBITS) {
Expand All @@ -466,7 +475,7 @@ static int secp256k1_bulletproof_rangeproof_prove_impl(const secp256k1_ecmult_co
return 0;
}
}
if (*plen < 128 + 64 + 1) { /* inner product argument will check and assign plen */
if (plen != NULL && *plen < 128 + 64 + 1) { /* Inner product argument will check and assign plen */
return 0;
}

Expand Down Expand Up @@ -516,14 +525,24 @@ static int secp256k1_bulletproof_rangeproof_prove_impl(const secp256k1_ecmult_co
}

secp256k1_scalar_chacha20(&alpha, &rho, nonce, 0);
secp256k1_scalar_chacha20(&tau1, &tau2, nonce, 1);
secp256k1_scalar_chacha20(&tau1, &tau2, private_nonce, 1);

if (proof == NULL && tauxc == NULL && tge != NULL) {
/* Multi-party bulletproof: export tau1j*G and tau2j*G */
secp256k1_ecmult_const(&tmpj, &gens->blinding_gen[0], &tau1, 256);
secp256k1_ge_set_gej(&tge[0], &tmpj);

secp256k1_ecmult_const(&tmpj, &gens->blinding_gen[0], &tau2, 256);
secp256k1_ge_set_gej(&tge[1], &tmpj);

return 1;
}

/* Encrypt value and optional message into alpha, so it will be recoverable from -mu by someone who knows `nonce` */
if (n_commits == 1) {
secp256k1_scalar vals;
secp256k1_scalar_set_u64(&vals, value[0]);
unsigned char vals_bytes[32];
int overflow;
if (message != NULL) {
/* Combine value with 16 bytes of optional message */
secp256k1_scalar_get_b32(&vals_bytes, &vals);
Expand Down Expand Up @@ -565,7 +584,7 @@ static int secp256k1_bulletproof_rangeproof_prove_impl(const secp256k1_ecmult_co
}
}

/* get challenges y and z */
/* Get challenges y and z */
secp256k1_ge_set_gej(&out_pt[0], &aj);
secp256k1_ge_set_gej(&out_pt[1], &sj);

Expand Down Expand Up @@ -629,35 +648,65 @@ static int secp256k1_bulletproof_rangeproof_prove_impl(const secp256k1_ecmult_co
secp256k1_scalar_negate(&t2, &t2);
secp256k1_scalar_add(&t2, &t2, &t1);

/* Compute Ti = t_i*A + tau_i*G for i = 1,2 */
/* Compute Ti for i = 1,2 */
secp256k1_ecmult_const(&tmpj, value_gen, &t1, 256);
secp256k1_ge_set_gej(&out_pt[2], &tmpj);
secp256k1_ecmult_const(&tmpj, &gens->blinding_gen[0], &tau1, 256);
secp256k1_gej_add_ge(&tmpj, &tmpj, &out_pt[2]);
if (tge == NULL) {
/* Normal bulletproof: T1=t1*A + tau1*G */
secp256k1_ge_set_gej(&out_pt[2], &tmpj);
secp256k1_ecmult_const(&tmpj, &gens->blinding_gen[0], &tau1, 256);
secp256k1_gej_add_ge(&tmpj, &tmpj, &out_pt[2]);
}
else {
/* Multi-party bulletproof: T1=t1*A + sumj tau1j*G */
secp256k1_gej_add_ge(&tmpj, &tmpj, &tge[0]);
}
secp256k1_ge_set_gej(&out_pt[2], &tmpj);

secp256k1_ecmult_const(&tmpj, value_gen, &t2, 256);
secp256k1_ge_set_gej(&out_pt[3], &tmpj);
secp256k1_ecmult_const(&tmpj, &gens->blinding_gen[0], &tau2, 256);
secp256k1_gej_add_ge(&tmpj, &tmpj, &out_pt[3]);
if (tge == NULL) {
/* Normal bulletproof: T1=t1*A + tau1*G */
secp256k1_ge_set_gej(&out_pt[3], &tmpj);
secp256k1_ecmult_const(&tmpj, &gens->blinding_gen[0], &tau2, 256);
secp256k1_gej_add_ge(&tmpj, &tmpj, &out_pt[3]);
}
else {
/* Multi-party bulletproof: T2=t2*A + sumj tau2j*G */
secp256k1_gej_add_ge(&tmpj, &tmpj, &tge[1]);
}
secp256k1_ge_set_gej(&out_pt[3], &tmpj);

/* get challenge x */
/* Get challenge x */
secp256k1_bulletproof_update_commit(commit, &out_pt[2], &out_pt[3]);
secp256k1_scalar_set_b32(&x, commit, &overflow);
if (overflow || secp256k1_scalar_is_zero(&x)) {
return 0;
}
secp256k1_scalar_sqr(&xsq, &x);

/* compute tau_x and mu */
secp256k1_scalar_mul(&taux, &tau1, &x);
secp256k1_scalar_mul(&tmps, &tau2, &xsq);
secp256k1_scalar_add(&taux, &taux, &tmps);
for (i = 0; i < n_commits; i++) {
secp256k1_scalar_mul(&tmps, &zsq, &blind[i]);
if (proof == NULL || tauxc == NULL) {
/* Compute taux and mu */
secp256k1_scalar_mul(&taux, &tau1, &x);
secp256k1_scalar_mul(&tmps, &tau2, &xsq);
secp256k1_scalar_add(&taux, &taux, &tmps);
secp256k1_scalar_mul(&zsq, &zsq, &z);
for (i = 0; i < n_commits; i++) {
secp256k1_scalar_mul(&tmps, &zsq, &blind[i]);
secp256k1_scalar_add(&taux, &taux, &tmps);
secp256k1_scalar_mul(&zsq, &zsq, &z);
}
}

if (proof == NULL) {
/* Multi-party bulletproof: export tauxj */
secp256k1_scalar_get_b32(tauxc, &taux);
return 1;
}

if (tauxc != NULL) {
/* Multi-party bulletproof: taux = sumj tauxj */
secp256k1_scalar_set_b32(&taux, tauxc, &overflow);
if (overflow || secp256k1_scalar_is_zero(&tmps)) {
return 0;
}
}

secp256k1_scalar_mul(&mu, &rho, &x);
Expand Down

0 comments on commit f1b4f08

Please sign in to comment.