You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

768 lines
28 KiB

/******************************************************************************
* Copyright © 2014-2016 The SuperNET Developers. *
* *
* See the AUTHORS, DEVELOPER-AGREEMENT and LICENSE files at *
* the top-level directory of this distribution for the individual copyright *
* holder information and the developer policies on copyright and licensing. *
* *
* Unless otherwise agreed in a custom licensing agreement, no part of the *
* SuperNET software, including this file may be copied, modified, propagated *
* or distributed except according to the terms contained in the LICENSE file *
* *
* Removal or modification of this copyright notice is prohibited. *
* *
******************************************************************************/
#include <ctype.h>
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include "../includes/curve25519.h"
#include "secp256k1/include/secp256k1.h"
#include "secp256k1/include/secp256k1_ecdh.h"
#include "secp256k1/include/secp256k1_schnorr.h"
#include "secp256k1/include/secp256k1_rangeproof.h"
#include "secp256k1/include/secp256k1_recovery.h"
SECP256K1_API extern const secp256k1_nonce_function secp256k1_nonce_function_rfc6979;
#define bits256_nonz(a) (((a).ulongs[0] | (a).ulongs[1] | (a).ulongs[2] | (a).ulongs[3]) != 0)
#define SECP_ENSURE_CTX int32_t flag = 0; if ( ctx == 0 ) { ctx = secp256k1_context_create(SECP256K1_CONTEXT_SIGN | SECP256K1_CONTEXT_VERIFY); secp256k1_pedersen_context_initialize(ctx); secp256k1_rangeproof_context_initialize(ctx); flag++; } else flag = 0; if ( ctx != 0 )
#define ENDSECP_ENSURE_CTX if ( flag != 0 ) secp256k1_context_destroy(ctx);
int32_t bitcoin_pubkeylen(const uint8_t *pubkey)
{
if ( pubkey[0] == 2 || pubkey[0] == 3 )
return(33);
else if ( pubkey[0] == 4 )
return(65);
else
{
printf("illegal pubkey.[%02x] %llx\n",pubkey[0],*(long long *)pubkey);
return(-1);
}
}
bits256 bitcoin_randkey(secp256k1_context *ctx)
{
int32_t i; bits256 privkey;
SECP_ENSURE_CTX
{
for (i=0; i<100; i++)
{
privkey = rand256(0);
if ( secp256k1_ec_seckey_verify(ctx,privkey.bytes) != 0 )
{
ENDSECP_ENSURE_CTX
return(privkey);
}
}
ENDSECP_ENSURE_CTX
}
fprintf(stderr,"couldnt generate valid bitcoin privkey. something is REALLY wrong. exiting\n");
exit(-1);
}
bits256 bitcoin_pubkey33(secp256k1_context *ctx,uint8_t *data,bits256 privkey)
{
size_t plen; bits256 pubkey; secp256k1_pubkey secppub;
memset(pubkey.bytes,0,sizeof(pubkey));
SECP_ENSURE_CTX
{
if ( secp256k1_ec_seckey_verify(ctx,privkey.bytes) == 0 )
{
//printf("bitcoin_sign illegal privkey\n");
return(pubkey);
}
if ( secp256k1_ec_pubkey_create(ctx,&secppub,privkey.bytes) != 0 )
{
plen = 33;
secp256k1_ec_pubkey_serialize(ctx,data,&plen,&secppub,SECP256K1_EC_COMPRESSED);
if ( plen == 33 )
memcpy(pubkey.bytes,data+1,sizeof(pubkey));
}
ENDSECP_ENSURE_CTX
}
return(pubkey);
}
bits256 bitcoin_pub256(void *ctx,bits256 *privkeyp,uint8_t odd_even)
{
bits256 pub256; uint8_t pubkey[33]; int32_t i;
for (i=0; i<100; i++)
{
*privkeyp = rand256(0);
pub256 = bitcoin_pubkey33(ctx,pubkey,*privkeyp);
if ( pubkey[0] == odd_even+2 )
return(pub256);
}
printf("bitcoin_pub256 couldnt generate pubkey.%d\n",odd_even+2);
memset(pub256.bytes,0,sizeof(pub256));
return(pub256);
}
int32_t bitcoin_sign(void *ctx,char *symbol,uint8_t *sig,bits256 txhash2,bits256 privkey,int32_t recoverflag)
{
int32_t fCompressed = 1;
secp256k1_ecdsa_signature SIG; secp256k1_ecdsa_recoverable_signature rSIG; bits256 extra_entropy,seed; int32_t recid,retval = -1; size_t siglen = 72; secp256k1_pubkey SECPUB,CHECKPUB;
seed = rand256(0);
extra_entropy = rand256(0);
SECP_ENSURE_CTX
{
if ( secp256k1_ec_seckey_verify(ctx,privkey.bytes) == 0 )
{
//printf("bitcoin_sign illegal privkey\n");
return(-1);
}
if ( secp256k1_context_randomize(ctx,seed.bytes) != 0 )
{
if ( recoverflag != 0 )
{
if ( secp256k1_ecdsa_sign_recoverable(ctx,&rSIG,txhash2.bytes,privkey.bytes,secp256k1_nonce_function_rfc6979,extra_entropy.bytes) != 0 )
{
recid = -1;
secp256k1_ecdsa_recoverable_signature_serialize_compact(ctx,sig+1,&recid,&rSIG);
if ( secp256k1_ecdsa_recover(ctx,&SECPUB,&rSIG,txhash2.bytes) != 0 )
{
if ( secp256k1_ec_pubkey_create(ctx,&CHECKPUB,privkey.bytes) != 0 )
{
if ( memcmp(&SECPUB,&CHECKPUB,sizeof(SECPUB)) == 0 )
{
sig[0] = 27 + recid + (fCompressed != 0 ? 4 : 0);
retval = 64 + 1;
}
else printf("secpub mismatch\n");
} else printf("pubkey create error\n");
} else printf("recover error\n");
} else printf("secp256k1_ecdsa_sign_recoverable error\n");
}
else
{
if ( secp256k1_ecdsa_sign(ctx,&SIG,txhash2.bytes,privkey.bytes,secp256k1_nonce_function_rfc6979,extra_entropy.bytes) != 0 )
{
if ( secp256k1_ecdsa_signature_serialize_der(ctx,sig,&siglen,&SIG) != 0 )
retval = (int32_t)siglen;
}
}
}
ENDSECP_ENSURE_CTX
}
return(retval);
}
int32_t bitcoin_recoververify(void *ctx,char *symbol,uint8_t *sig65,bits256 messagehash2,uint8_t *pubkey)
{
int32_t retval = -1; size_t plen; secp256k1_pubkey PUB; secp256k1_ecdsa_signature SIG; secp256k1_ecdsa_recoverable_signature rSIG;
pubkey[0] = 0;
SECP_ENSURE_CTX
{
plen = (sig65[0] <= 31) ? 65 : 33;
secp256k1_ecdsa_recoverable_signature_parse_compact(ctx,&rSIG,sig65 + 1,0);
secp256k1_ecdsa_recoverable_signature_convert(ctx,&SIG,&rSIG);
if ( secp256k1_ecdsa_recover(ctx,&PUB,&rSIG,messagehash2.bytes) != 0 )
{
plen = 33;
secp256k1_ec_pubkey_serialize(ctx,pubkey,&plen,&PUB,SECP256K1_EC_COMPRESSED);//plen == 65 ? SECP256K1_EC_UNCOMPRESSED : SECP256K1_EC_COMPRESSED);
if ( secp256k1_ecdsa_verify(ctx,&SIG,messagehash2.bytes,&PUB) != 0 )
{
retval = 0;
/*if ( pubkey[0] == 4 ) // experimentally looks like 04 is set
pubkey[0] = 2;
else if ( pubkey[0] != 2 )
pubkey[0] = 3;*/
}
else printf("secp256k1_ecdsa_verify error\n");
} else printf("secp256k1_ecdsa_recover error\n");
ENDSECP_ENSURE_CTX
}
return(retval);
}
int32_t bitcoin_verify(void *ctx,uint8_t *sig,int32_t siglen,bits256 txhash2,uint8_t *pubkey,int32_t plen)
{
int32_t retval = -1; secp256k1_pubkey PUB; secp256k1_ecdsa_signature SIG;
SECP_ENSURE_CTX
{
if ( secp256k1_ec_pubkey_parse(ctx,&PUB,pubkey,plen) != 0 )
{
secp256k1_ecdsa_signature_parse_der(ctx,&SIG,sig,siglen);
if ( secp256k1_ecdsa_verify(ctx,&SIG,txhash2.bytes,&PUB) != 0 )
retval = 0;
}
ENDSECP_ENSURE_CTX
}
return(retval);
}
bits256 bitcoin_sharedsecret(void *ctx,bits256 privkey,uint8_t *pubkey,int32_t plen)
{
int32_t retval = -1; bits256 shared; secp256k1_pubkey PUB;
memset(shared.bytes,0,sizeof(shared));
SECP_ENSURE_CTX
{
if ( secp256k1_ec_pubkey_parse(ctx,&PUB,pubkey,plen) != 0 )
{
if ( secp256k1_ecdh(ctx,shared.bytes,&PUB,privkey.bytes) != 0 )
retval = 0;
else memset(shared.bytes,0,sizeof(shared));
}
ENDSECP_ENSURE_CTX
}
return(shared);
}
int32_t bitcoin_schnorr_sign(void *ctx,uint8_t *sig64,bits256 txhash2,bits256 privkey)
{
int32_t retval = -1; bits256 seed;
SECP_ENSURE_CTX
{
seed = rand256(0);
if ( secp256k1_schnorr_sign(ctx,sig64,txhash2.bytes,privkey.bytes,secp256k1_nonce_function_rfc6979,seed.bytes) != 0 )
retval = 0;
ENDSECP_ENSURE_CTX
}
return(retval);
}
int32_t bitcoin_schnorr_verify(void *ctx,uint8_t *sig64,bits256 txhash2,uint8_t *pubkey,int32_t plen)
{
int32_t retval = -1; secp256k1_pubkey PUB;
SECP_ENSURE_CTX
{
if ( secp256k1_ec_pubkey_parse(ctx,&PUB,pubkey,plen) != 0 )
{
if ( secp256k1_schnorr_verify(ctx,sig64,txhash2.bytes,&PUB) != 0 )
retval = 0;
}
ENDSECP_ENSURE_CTX
}
return(retval);
}
int32_t bitcoin_schnorr_recover(void *ctx,uint8_t *pubkey,uint8_t *sig64,bits256 txhash2)
{
int32_t retval = -1; secp256k1_pubkey PUB; size_t plen;
SECP_ENSURE_CTX
{
if ( secp256k1_schnorr_recover(ctx,&PUB,sig64,txhash2.bytes) != 0 )
{
plen = 33;
secp256k1_ec_pubkey_serialize(ctx,pubkey,&plen,&PUB,SECP256K1_EC_COMPRESSED);
retval = 0;
}
ENDSECP_ENSURE_CTX
}
return(retval);
}
bits256 bitcoin_schnorr_noncepair(void *ctx,uint8_t *pubnonce,bits256 txhash2,bits256 privkey) //exchange
{
int32_t retval = -1; size_t plen; secp256k1_pubkey PUB; bits256 privnonce,seed;
memset(privnonce.bytes,0,sizeof(privnonce));
pubnonce[0] = 0;
SECP_ENSURE_CTX
{
seed = rand256(0);
if ( secp256k1_schnorr_generate_nonce_pair(ctx,&PUB,privnonce.bytes,txhash2.bytes,privkey.bytes,secp256k1_nonce_function_rfc6979,seed.bytes) != 0 )
{
plen = 33;
secp256k1_ec_pubkey_serialize(ctx,pubnonce,&plen,&PUB,SECP256K1_EC_COMPRESSED);
retval = 0;
}
ENDSECP_ENSURE_CTX
}
return(privnonce);
}
int32_t bitcoin_pubkey_combine(void *ctx,uint8_t *combined_pub,uint8_t *skipkey,bits256 *evenkeys,int32_t n,bits256 *oddkeys,int32_t m)
{
int32_t i,num,iter,max,retval = -1; uint8_t pubkey[33]; size_t plen; secp256k1_pubkey PUBall,*PUBptrs[256],PUBkeys[256];
SECP_ENSURE_CTX
{
if ( n+m > 0 && n+m < sizeof(PUBptrs)/sizeof(*PUBptrs) )
{
for (iter=num=0; iter<2; iter++)
{
if ( (max= (iter == 0) ? n : m) != 0 )
{
for (i=0; i<max; i++)
{
PUBptrs[num] = &PUBkeys[num];
pubkey[0] = 2 + iter;
memcpy(pubkey+1,((iter == 0) ? evenkeys : oddkeys)[i].bytes,32);
if ( skipkey != 0 && memcmp(pubkey,skipkey,33) == 0 )
{
//printf("skipkey.%d\n",i);
continue;
}
if ( secp256k1_ec_pubkey_parse(ctx,PUBptrs[num],pubkey,33) == 0 )
{
int32_t j; for (j=0; j<33; j++)
printf("%02x",pubkey[j]);
printf(" error parsing pubkey iter.%d num.%d i.%d\n",iter,num,i);
break;
}
num++;
}
}
}
if ( secp256k1_ec_pubkey_combine(ctx,&PUBall,(void *)PUBptrs,num) != 0 )
{
plen = 33;
secp256k1_ec_pubkey_serialize(ctx,combined_pub,&plen,&PUBall,SECP256K1_EC_COMPRESSED);
retval = 0;
}
}
ENDSECP_ENSURE_CTX
}
return(retval);
}
int32_t bitcoin_schnorr_partialsign(void *ctx,uint8_t *sig64,uint8_t *combined_pub,bits256 txhash2,bits256 privkey,bits256 privnonce,uint8_t *pubptrs[],int32_t n) // generate and exchange
{
int32_t bitcoin_pubkeylen(const uint8_t *pubkey);
int32_t i,retval = -1; secp256k1_pubkey PUBall,**PUBptrs; size_t plen;
SECP_ENSURE_CTX
{
PUBptrs = calloc(n,sizeof(*PUBptrs));
for (i=0; i<n; i++)
{
PUBptrs[i] = calloc(1,sizeof(secp256k1_pubkey));
if ( secp256k1_ec_pubkey_parse(ctx,PUBptrs[i],pubptrs[i],bitcoin_pubkeylen(pubptrs[i])) != 0 )
break;
}
if ( n > 0 && secp256k1_ec_pubkey_combine(ctx,&PUBall,(void *)PUBptrs,n) != 0 )
{
plen = 33;
if ( secp256k1_schnorr_partial_sign(ctx,sig64,txhash2.bytes,privkey.bytes,&PUBall,privnonce.bytes) != 0 )
{
secp256k1_ec_pubkey_serialize(ctx,combined_pub,&plen,&PUBall,SECP256K1_EC_COMPRESSED);
retval = 0;
}
}
free(PUBptrs);
ENDSECP_ENSURE_CTX
}
return(retval);
}
int32_t bitcoin_schnorr_combine(void *ctx,uint8_t *sig64,uint8_t *allpub,uint8_t **sigs,int32_t n,bits256 txhash2)
{
int32_t rc,retval = -1;
SECP_ENSURE_CTX
{
if ( (rc= secp256k1_schnorr_partial_combine(ctx,sig64,(void *)sigs,n)) != 0 )
{
if ( bitcoin_schnorr_recover(ctx,allpub,sig64,txhash2) == 0 )
{
if ( bitcoin_schnorr_verify(ctx,sig64,txhash2,allpub,33) == 0 )
retval = 0;
}
}
ENDSECP_ENSURE_CTX
}
return(retval);
}
int32_t bitcoin_pederson_commit(void *ctx,uint8_t *commit,bits256 blind,uint64_t value)
{
int32_t retval = -1;
SECP_ENSURE_CTX
{
if ( secp256k1_pedersen_commit(ctx,commit,blind.bytes,value) != 0 )
retval = 0;
ENDSECP_ENSURE_CTX
}
return(retval);
}
bits256 bitcoin_pederson_blindsum(void *ctx,bits256 **blindptrs,int32_t n,int32_t numpos)
{
bits256 blind_out;
memset(blind_out.bytes,0,sizeof(blind_out));
SECP_ENSURE_CTX
{
if ( secp256k1_pedersen_blind_sum(ctx,blind_out.bytes,(void *)blindptrs,n,numpos) == 0 )
memset(blind_out.bytes,0,sizeof(blind_out));
ENDSECP_ENSURE_CTX
}
return(blind_out);
}
int32_t bitcoin_pederson_tally(void *ctx,uint8_t **commits,int32_t n,int32_t numpos,int64_t excess)
{
int32_t retval = -1;
SECP_ENSURE_CTX
{
printf("bitcoin_pederson_tally: n.%d numpos.%d excess %lld\n",n,numpos,(long long)excess);
if ( secp256k1_pedersen_verify_tally(ctx,(void *)commits,numpos,(void *)&commits[numpos],n - numpos,excess) != 0 )
retval = 0;
ENDSECP_ENSURE_CTX
}
return(retval);
}
int32_t bitcoin_rangeproof_message(void *ctx,uint8_t *blind_out,uint8_t *message,uint64_t *valuep,bits256 nonce,uint64_t *min_valuep,uint64_t *max_valuep,uint8_t *commit,uint8_t *proof,int32_t prooflen)
{
int32_t outlen = 0,retval = -1;
SECP_ENSURE_CTX
{
if ( secp256k1_rangeproof_rewind(ctx,blind_out,valuep,message,&outlen,nonce.bytes,min_valuep,max_valuep,commit,proof,prooflen) != 0 )
retval = outlen;
ENDSECP_ENSURE_CTX
}
return(retval);
}
uint64_t bitcoin_rangeverify(void *ctx,int32_t *exponentp,int32_t *mantissap,uint64_t *min_valuep,uint8_t *commit,uint8_t *proof,int32_t prooflen)
{
uint64_t max_value,retval = 0;
max_value = *min_valuep = *exponentp = *mantissap = 0;
if ( secp256k1_rangeproof_info(ctx,exponentp,mantissap,min_valuep,&max_value,proof,prooflen) != 0 )
{
if ( commit != 0 )
{
if ( secp256k1_rangeproof_verify(ctx,min_valuep,&max_value,commit,proof,prooflen) != 0 )
retval = max_value;
} else retval = max_value;
}
return(retval);
}
int32_t bitcoin_rangeproof(void *ctx,uint8_t *proof,uint8_t *commit,bits256 blind,bits256 nonce,uint64_t value,uint64_t min_value,int32_t exponent,int32_t min_bits)
{
int32_t prooflen=0 ,retval = -1;
SECP_ENSURE_CTX
{
if ( secp256k1_rangeproof_sign(ctx,proof,&prooflen,min_value,commit,blind.bytes,nonce.bytes,exponent,min_bits,value) != 0 )
retval = prooflen;
ENDSECP_ENSURE_CTX
}
return(retval);
}
/*
* The intended procedure for creating a multiparty signature is:
* - Each signer S[i] with private key x[i] and public key Q[i] runs
* secp256k1_schnorr_generate_nonce_pair to produce a pair (k[i],R[i]) of private/public nonces.
* - All signers communicate their public nonces to each other (revealing your
* private nonce can lead to discovery of your private key, so it should be considered secret).
* - All signers combine all the public nonces they received (excluding their
* own) using secp256k1_ec_pubkey_combine to obtain an Rall[i] = sum(R[0..i-1,i+1..n]).
* - All signers produce a partial signature using
* secp256k1_schnorr_partial_sign, passing in their own private key x[i],
* their own private nonce k[i], and the sum of the others' public nonces Rall[i].
* - All signers communicate their partial signatures to each other.
* - Someone combines all partial signatures using secp256k1_schnorr_partial_combine, to obtain a full signature.
* - The resulting signature is validatable using secp256k1_schnorr_verify, with
* public key equal to the result of secp256k1_ec_pubkey_combine of the signers' public keys (sum(Q[0..n])).
*
* Note that secp256k1_schnorr_partial_combine and secp256k1_ec_pubkey_combine
* function take their arguments in any order, and it is possible to
* pre-combine several inputs already with one call, and add more inputs later
* by calling the function again (they are commutative and associative).
*/
#ifdef test_schnorr
#include "secp256k1/src/util.h"
#include "secp256k1/src/hash_impl.h"
#include "secp256k1/src/testrand_impl.h"
void test_schnorr_threshold(void *ctx) {
unsigned char msg[32];
unsigned char sec[5][32];
secp256k1_pubkey pub[5];
unsigned char nonce[5][32];
secp256k1_pubkey pubnonce[5];
unsigned char sig[5][64];
const unsigned char* sigs[5];
unsigned char allsig[64];
const secp256k1_pubkey* pubs[5];
secp256k1_pubkey allpub;
int n, i;
int damage;
int ret = 0;
damage = secp256k1_rand_bits(1) ? (1 + secp256k1_rand_int(4)) : 0;
secp256k1_rand256_test(msg);
n = 2 + secp256k1_rand_int(4);
for (i = 0; i < n; i++) {
do {
secp256k1_rand256_test(sec[i]);
} while (!secp256k1_ec_seckey_verify(ctx, sec[i]));
CHECK(secp256k1_ec_pubkey_create(ctx, &pub[i], sec[i]));
CHECK(secp256k1_schnorr_generate_nonce_pair(ctx, &pubnonce[i], nonce[i], msg, sec[i], NULL, NULL));
pubs[i] = &pub[i];
}
if (damage == 1) {
nonce[secp256k1_rand_int(n)][secp256k1_rand_int(32)] ^= 1 + secp256k1_rand_int(255);
} else if (damage == 2) {
sec[secp256k1_rand_int(n)][secp256k1_rand_int(32)] ^= 1 + secp256k1_rand_int(255);
}
for (i = 0; i < n; i++) {
secp256k1_pubkey allpubnonce;
const secp256k1_pubkey *pubnonces[4];
int j;
for (j = 0; j < i; j++) {
pubnonces[j] = &pubnonce[j];
}
for (j = i + 1; j < n; j++) {
pubnonces[j - 1] = &pubnonce[j];
}
CHECK(secp256k1_ec_pubkey_combine(ctx, &allpubnonce, pubnonces, n - 1));
ret |= (secp256k1_schnorr_partial_sign(ctx, sig[i], msg, sec[i], &allpubnonce, nonce[i]) != 1) * 1;
sigs[i] = sig[i];
}
if (damage == 3) {
sig[secp256k1_rand_int(n)][secp256k1_rand_bits(6)] ^= 1 + secp256k1_rand_int(255);
}
ret |= (secp256k1_ec_pubkey_combine(ctx, &allpub, pubs, n) != 1) * 2;
if ((ret & 1) == 0) {
ret |= (secp256k1_schnorr_partial_combine(ctx, allsig, sigs, n) != 1) * 4;
}
if (damage == 4) {
allsig[secp256k1_rand_int(32)] ^= 1 + secp256k1_rand_int(255);
}
if ((ret & 7) == 0) {
ret |= (secp256k1_schnorr_verify(ctx, allsig, msg, &allpub) != 1) * 8;
}
CHECK((ret == 0) == (damage == 0));
}
#endif
int32_t iguana_pederson_test(void *ctx)
{
uint8_t commits[100][33],*commitptrs[100],proofs[100][5138]; uint16_t vouts[100]; int64_t min_value,values[100],totalpos,totalneg; bits256 txids[100],nonces[100],blinds[100],*blindptrs[100],blindsum; int32_t prooflens[100],i,r,pos,neg,numpos,exponent,min_bits,n,N = 100;
srand(100);
for (i=numpos=n=0; i<N; i++)
{
values[i] = rand();
vouts[i] = (rand() % 300);
txids[i] = rand256(0);
nonces[i] = rand256(0);
blinds[i] = rand256(0);
if ( bitcoin_pederson_commit(ctx,commits[i],blinds[i],values[i]) < 0 )
break;
if ( ((r= rand()) % 2) == 0 )
values[i] = -values[i];
else
{
exponent = 0;
min_bits = 64;
min_value = 0;
prooflens[i] = bitcoin_rangeproof(ctx,proofs[i],commits[i],blinds[i],nonces[i],values[i],min_value,exponent,min_bits);
printf("%d ",prooflens[i]);
numpos++;
}
n++;
}
if ( i != N )
{
printf("commit failure i.%d of N.%d\n",i,N);
return(-1);
}
for (totalpos=i=pos=0; i<N; i++)
{
if ( values[i] > 0 )
{
commitptrs[pos] = commits[i];
blindptrs[pos] = &blinds[i];
totalpos += values[i];
pos++;
}
}
if ( pos != numpos )
{
printf("pos.%d != numpos.%d\n",pos,numpos);
return(-1);
}
for (totalneg=i=neg=0; i<N; i++)
{
if ( values[i] < 0 )
{
commitptrs[numpos + neg] = commits[i];
blindptrs[numpos + neg] = &blinds[i];
totalneg -= values[i];
neg++;
}
}
if ( numpos+neg != N )
{
printf("numpos.%d + neg.%d != N.%d\n",numpos,neg,N);
return(-1);
}
blindsum = bitcoin_pederson_blindsum(ctx,blindptrs,N,numpos);
if ( bits256_nonz(blindsum) == 0 )
{
printf("error doing blindsum numpos.%d N.%d\n",numpos,N);
return(-2);
}
if ( bitcoin_pederson_tally(ctx,commitptrs,N,numpos,totalneg - totalpos) == 0 )
{
printf("error doing pederson tally\n");
return(-3);
} else printf("pederson tally matches\n");
return(0);
}
int32_t iguana_schnorr_test(void *ctx)
{
bits256 privnonces[100],privkeys[100],txhash2; uint8_t *sigs[100],allpub[100][33],sig64[100][64],allsig64[100][64],combined_pub[100][33],pubnonces[100][33],*pubptrs[100]; int32_t i,j,N,n,errs = 0;
iguana_pederson_test(ctx);
SECP_ENSURE_CTX
{
N = 100;
txhash2 = rand256(0);
for (i=0; i<N; i++)
{
privkeys[i] = bitcoin_randkey(ctx);
privnonces[i] = bitcoin_schnorr_noncepair(ctx,pubnonces[i],txhash2,privkeys[i]);
}
if ( i != N )
{
printf("error getting nonce pair\n");
exit(-1);
}
for (i=0; i<N; i++)
{
for (j=n=0; j<N; j++)
if ( j != i )
pubptrs[n++] = pubnonces[j];
if ( N > 1 )
{
if ( bitcoin_schnorr_partialsign(ctx,sig64[i],combined_pub[i],txhash2,privkeys[i],privnonces[i],pubptrs,N-1) < 0 )
errs++;
}
else
{
if ( bitcoin_schnorr_sign(ctx,sig64[0],txhash2,privkeys[0]) < 0 )
errs++;
}
}
if ( errs != 0 )
printf("partialsign errs.%d\n",errs);
for (i=0; i<N; i++)
{
sigs[i] = sig64[i];
continue;
for (j=0; j<64; j++)
printf("%02x",sig64[i][j]);
printf(" sig[%d]\n",i);
}
for (i=0; i<N; i++)
{
if ( bitcoin_schnorr_combine(ctx,allsig64[i],allpub[i],sigs,N,txhash2) < 0 )
errs++;
else if ( memcmp(allpub[i],allpub[0],33) != 0 )
errs++;
else if ( memcmp(allsig64[i],allsig64[0],33) != 0 )
errs++;
}
if ( errs != 0 )
printf("combine errs.%d\n",errs);
if ( bitcoin_schnorr_verify(ctx,allsig64[0],txhash2,allpub[0],33) < 0 )
errs++;
printf("schnorr errs.%d\n",errs);
ENDSECP_ENSURE_CTX
}
return(errs);
}
/*
We start by reminding the reader how confidential transactions work. First, the
amounts are coded by the following equation:
C = r*G + v*H
where C is a Pedersen commitment, G and H are fixed nothing-up-my-sleeve elliptic
curve group generators, v is the amount, and r is a secret random blinding key.
Attached to this output is a rangeproof which proves that v is in [0, 2^64], so
that user cannot exploit the blinding to produce overflow attacks, etc.
To validate a transaction, the verifer will add commitments for all outputs, plus
f*H (f here is the transaction fee which is given explicitly) and subtracts all
input commitments. The result must be 0, which proves that no amount was created
or destroyed overall.
We note that to create such a transaction, the user must know the sum of all the
values of r for commitments entries. Therefore, the r-values (and their sums) act
as secret keys. If we can make the r output values known only to the recipient,
then we have an authentication system! Unfortunately, if we keep the rule that
commits all add to 0, this is impossible, because the sender knows the sum of
all _his_ r values, and therefore knows the receipient's r values sum to the
negative of that. So instead, we allow the transaction to sum to a nonzero value
k*G, and require a signature of an empty string with this as key, to prove its
amount component is zero.
We let transactions have as many k*G values as they want, each with a signature,
and sum them during verification.
To create transactions sender and recipient do following ritual:
1. Sender and recipient agree on amount to be sent. Call this b.
2. Sender creates transaction with all inputs and change output(s), and gives
recipient the total blinding factor (r-value of change minus r-values of
inputs) along with this transaction. So the commitments sum to r*G - b*H.
3. Recipient chooses random r-values for his outputs, and values that sum
to b minus fee, and adds these to transaction (including range proof).
Now the commitments sum to k*G - fee*H for some k that only recipient
knows.
4. Recipient attaches signature with k to the transaction, and the explicit
fee. It has done.
*/
void test_mimblewimble(void *ctx)
{
uint8_t commits[100][33],*commitptrs[100]; int64_t inputs[8],inputsum,amount,change,txfee,totalpos,totalneg; bits256 nonces[100],blinds[100],*blindptrs[100],blindsum; int32_t i,r,numinputs;
OS_randombytes((void *)&r,sizeof(r));
srand(r);
inputs[0] = 100000000;
numinputs = 1;
inputsum = 0;
for (i=0; i<numinputs; i++)
inputsum += inputs[i];
txfee = 10000;
amount = 100000000 / 10;
change = inputsum - txfee - amount;
totalpos = change;
totalneg = inputsum;
for (i=0; i<numinputs+2; i++)
{
nonces[i] = rand256(0);
blinds[i] = rand256(0);
commitptrs[i] = commits[i];
blindptrs[i] = &blinds[i];
}
if ( bitcoin_pederson_commit(ctx,commits[0],blinds[0],change) < 0 )
{
printf("error getting change commit\n");
return;
}
for (i=1; i<=numinputs; i++)
{
if ( bitcoin_pederson_commit(ctx,commits[i],blinds[i],-inputs[i]) < 0 )
{
printf("error getting input.(%d) commit\n",i);
return;
}
}
blindsum = bitcoin_pederson_blindsum(ctx,blindptrs,numinputs+1,1);
if ( bits256_nonz(blindsum) == 0 )
{
printf("error doing blindsum\n");
return;
}
if ( bitcoin_pederson_tally(ctx,commitptrs,numinputs+1,1,totalneg - totalpos) == 0 )
{
printf("error doing pederson tally\n");
return;
} else printf("pederson tally matches\n");
getchar();
}