Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

secp256k1 jets #18

Closed
wants to merge 2 commits into from
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
10 changes: 5 additions & 5 deletions C/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -15,17 +15,17 @@ jetTable.c: jetTable.gperf
jetTable.o: jetTable.c
$(CC) -c $(CFLAGS) $(CPPFLAGS) -o $@ $<

# In some cases jets may not use their 'src' or 'env' parameters.
jets.o: jets.c
$(CC) -c $(CFLAGS) $(CWARN) -Wno-unused-parameter $(CPPFLAGS) -o $@ $<
# libsecp256k1 is full of conversion warnings, so we compile jets-secp256k1.c separately.
jets-secp256k1.o: jets-secp256k1.c
$(CC) -c $(CFLAGS) $(CWARN) -Wno-conversion $(CPPFLAGS) -o $@ $<

primitive/elements/jets.o: primitive/elements/jets.c
$(CC) -c $(CFLAGS) $(CWARN) -Wno-unused-parameter -Wno-switch-enum -Wswitch $(CPPFLAGS) -o $@ $<
$(CC) -c $(CFLAGS) $(CWARN) -Wno-switch-enum -Wswitch $(CPPFLAGS) -o $@ $<

%.o: %.c
$(CC) -c $(CFLAGS) $(CWARN) $(CPPFLAGS) -o $@ $<

libElementsSimplicity.a: bitstream.o dag.o deserialize.o eval.o frame.o jets.o jetTable.o sha256.o type.o typeInference.o primitive/elements.o primitive/elements/jets.o primitive/elements/primitive.o
libElementsSimplicity.a: bitstream.o dag.o deserialize.o eval.o frame.o jets.o jets-secp256k1.o jetTable.o sha256.o type.o typeInference.o primitive/elements.o primitive/elements/jets.o primitive/elements/primitive.o
ar rcs $@ $^

test: test.o hashBlock.o schnorr0.o schnorr6.o primitive/elements/checkSigHashAllTx1.o libElementsSimplicity.a
Expand Down
4 changes: 4 additions & 0 deletions C/jetTable.gperf
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,10 @@ struct jetEntry { const char* witnessMerkleRoot; jet_ptr jet; JET_FLAG flag;};
"\x40\x59\x14\xc9\x52\x4c\x48\x73\xce\x5d\xdb\x06\xfd\x30\xd6\xd5\xfc\x4a\xc1\xfa\xc0\xee\xf8\xd8\x2d\xe6\xc6\x22\x7f\xb2\xd2\xcd", &multiplier32 , JET_WORD
"\x89\xa0\xae\x09\x8a\xff\x5e\x9c\x40\x90\x74\x47\x91\xff\x5c\x8e\xe1\x7a\x8c\xeb\x9e\x49\x42\x24\xe9\x19\xde\xb1\x1c\x5b\x8a\xf4", &fullMultiplier32, JET_WORD
"\xee\xae\x47\xe2\xf7\x87\x6c\x3b\x9c\xbc\xd4\x04\xa3\x38\xb0\x89\xfd\xea\xdf\x1b\x9b\xb3\x82\xec\x6e\x69\x71\x9d\x31\xba\xec\x9a", &sha256_hashBlock, JET_HASH
"\x51\x95\xc2\x93\x6a\xcc\x80\x4d\x9a\x34\x02\x19\x15\xd0\x26\x20\x91\x0e\x64\x17\x3d\x31\x39\xe2\x4e\xd6\x48\x13\x3e\xcc\x6f\x53", &fe_sqrt, JET_EC
"\xe2\xe9\x9c\x7f\xf9\x1a\xbd\x84\x0e\xd5\x73\x08\xf3\x9c\xb0\xb0\x56\xff\xcb\xf7\x4a\xc7\xb6\xa6\xb0\x16\x20\x45\xcb\x90\x90\x5f", &offsetPoint, JET_EC
"\x78\xba\x07\x81\x15\x41\xdc\x7c\xcd\x9d\x6e\x2b\xba\xd2\x5d\x59\xd2\x29\x7e\x6a\xc5\x3a\x8d\xf7\x2b\x86\x0d\x0a\xcc\xa7\x71\x03", &ecmult, JET_EC
"\xec\xbf\x4f\x4a\xb1\x48\xd2\xf5\xff\x1d\x90\x16\x2b\xa0\xfc\x99\x52\xf7\xef\x5a\x96\x1b\x1a\x8b\x0f\xfb\x0f\x4a\x69\xa2\xa2\x8e", &schnorrVerify, JET_EC
%%
/* Given a witness Merkle root for some Simplicity expression, find a jet that simulates it.
* If such a jet is found, and that jet's type matches the 'filter' then the jet is returned.
Expand Down
218 changes: 218 additions & 0 deletions C/jets-secp256k1.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,218 @@
#include "jets.h"

#include "sha256.h"
#include "secp256k1/secp256k1_impl.h"

/* Copy an array of 'uint32_t's into an array of 'unsigned char' by transforming each 32 bit values into four 8-bit values
* written in big endian order.
*
* Precondition: unsigned char out[4*len];
* uint32_t in[len]
*/
static inline void unpack32s(unsigned char* out, const uint32_t* in, size_t len) {
while (len) {
WriteBE32(out, *in);
out += 4;
++in;
--len;
}
}

/* Read a secp256k1 10x32 field element value from the 'src' frame, advancing the cursor 320 cells.
*
* Precondition: '*src' is a valid read frame for 320 more cells;
* NULL != r;
*/
static inline void read_fe(secp256k1_fe* r, frameItem* src) {
read32s(r->n, 10, src);
}

/* Write a secp256k1 10x32 field element value to the 'dst' frame, advancing the cursor 320 cells.
*
* Precondition: '*dst' is a valid write frame for 320 more cells;
* NULL != r;
*/
static inline void write_fe(frameItem* dst, const secp256k1_fe* r) {
write32s(dst, r->n, 10);
}

/* Skip 320 cells, the size of a secp256k1 10x32 field element value, in the 'dst' frame.
*
* Precondition: '*dst' is a valid write frame for 320 more cells;
*/
static inline void skip_fe(frameItem* dst) {
skipBits(dst, 32*10);
}

/* Read a (non-infinity) secp256k1 affine group element value from the 'src' frame, advancing the cursor 640 cells.
*
* Precondition: '*src' is a valid read frame for 640 more cells;
* NULL != r;
*/
static inline void read_ge(secp256k1_ge* r, frameItem* src) {
read_fe(&r->x, src);
read_fe(&r->y, src);
r->infinity = 0;
}

/* Read a secp256k1 jacobian group element value from the 'src' frame, advancing the cursor 960 cells.
*
* Precondition: '*src' is a valid read frame for 960 more cells;
* NULL != r;
*/
static inline void read_gej(secp256k1_gej* r, frameItem* src) {
read_fe(&r->x, src);
read_fe(&r->y, src);
read_fe(&r->z, src);
r->infinity = secp256k1_fe_is_zero(&r->z);
}

/* Write a secp256k1 jacobian group element value to the 'dst' frame, advancing the cursor 960 cells.
* If 'r->infinity' then we write an fe_zero value to the 'z' coordinate in the 'dst' frame instead of 'r->z'.
*
* Precondition: '*dst' is a valid write frame for 960 more cells;
* NULL != r;
*/
static inline void write_gej(frameItem* dst, const secp256k1_gej* r) {
write_fe(dst, &r->x);
write_fe(dst, &r->y);
if (r->infinity) {
write32s(dst, (uint32_t[10]){0}, 10);
} else {
write_fe(dst, &r->z);
}
}

/* Read a secp256k1 scalar element value from the 'src' frame, advancing the cursor 256 cells.
* The secp256k1 8x32 scalar representation puts the 32 least significant bytes into the first array element;
* However Simplicity uses a standard big-endian 256-bit word to represent scalar values.
* Thus it is necessary to fill the secp256k1 scalar array in reverse order.
*
* Precondition: '*src' is a valid read frame for 256 more cells;
* NULL != r;
*/
static inline void read_scalar(secp256k1_scalar* r, frameItem* src) {
r->d[7] = (uint32_t)read32(src);
r->d[6] = (uint32_t)read32(src);
r->d[5] = (uint32_t)read32(src);
r->d[4] = (uint32_t)read32(src);
r->d[3] = (uint32_t)read32(src);
r->d[2] = (uint32_t)read32(src);
r->d[1] = (uint32_t)read32(src);
r->d[0] = (uint32_t)read32(src);
}

/* Write a secp256k1 scalar element value to the 'dst' frame, advancing the cursor 256 cells.
* The secp256k1 8x32 scalar representation puts the 32 least significant bytes into the first array element;
* However Simplicity uses a standard big-endian 256-bit word to represent scalar values.
* Thus it is necessary to read from the secp256k1 scalar array in reverse order.
*
* Precondition: '*dst' is a valid write frame for 256 more cells;
* NULL != r;
*/
static inline void write_scalar(frameItem* dst, const secp256k1_scalar* r) {
write32(dst, r->d[7]);
write32(dst, r->d[6]);
write32(dst, r->d[5]);
write32(dst, r->d[4]);
write32(dst, r->d[3]);
write32(dst, r->d[2]);
write32(dst, r->d[1]);
write32(dst, r->d[0]);
}

bool fe_sqrt(frameItem* dst, frameItem src, const txEnv* env) {
(void) env; // env is unused;

secp256k1_fe r, a;
read_fe(&a, &src);
int result = secp256k1_fe_sqrt_var(&r, &a);
if (writeBit(dst, result)) {
write_fe(dst, &r);
} else {
skip_fe(dst);
}
return true;
}

bool offsetPoint(frameItem* dst, frameItem src, const txEnv* env) {
(void) env; // env is unused;

secp256k1_gej r, a;
secp256k1_ge b;
secp256k1_fe rzr;
read_gej(&a, &src);
read_ge(&b, &src);
secp256k1_gej_add_ge_var(&r, &a, &b, &rzr);
write_fe(dst, &rzr);
write_gej(dst, &r);
return true;
}

static struct {
secp256k1_ecmult_context ctx;
char alloc[SECP256K1_ECMULT_CONTEXT_PREALLOCATED_SIZE];
bool initialized;
} ecmult_static;

/* This function will initialize the 'ecmult_static' global variable if it hasn't already been initialized. */
static void ensure_ecmult_static(void) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The thread-unsafety of this really makes me uncomfortable, though I can't think of any other way to do this. Maybe putting a secp context into txEnv?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is a more general problem with the entire library. I've created a separate issue to track it .

if (!ecmult_static.initialized) {
void *prealloc = ecmult_static.alloc;
secp256k1_ecmult_context_init(&ecmult_static.ctx);
secp256k1_ecmult_context_build(&ecmult_static.ctx, &prealloc);
ecmult_static.initialized = secp256k1_ecmult_context_is_built(&ecmult_static.ctx);
}
assert(ecmult_static.initialized);
}

bool ecmult(frameItem* dst, frameItem src, const txEnv* env) {
(void) env; // env is unused;

secp256k1_gej r, a;
secp256k1_scalar na, ng;

ensure_ecmult_static();
read_gej(&a, &src);
read_scalar(&na, &src);
read_scalar(&ng, &src);
secp256k1_ecmult(&ecmult_static.ctx, &r, &a, &na, &ng);

/* This jet's implementation of ecmult is defined to always outputs the jacobian coordinate (1, 1, 0)
* if the result is the point at infinity.
*/
if (r.infinity) {
secp256k1_fe_set_int(&r.x, 1);
secp256k1_fe_set_int(&r.y, 1);
}
write_gej(dst, &r);
return true;
}

bool schnorrVerify(frameItem* dst, frameItem src, const txEnv* env) {
(void) env; // env is unused;

uint32_t buf[16];
unsigned char buf_char[64];
secp256k1_xonly_pubkey pubkey;
unsigned char msg[32];
secp256k1_schnorrsig sig;

ensure_ecmult_static();
read32s(buf, 8, &src);
unpack32s(buf_char, buf, 8);
if (!secp256k1_xonly_pubkey_parse(&pubkey, buf_char)) {
writeBit(dst, 0);
return true;
}

read32s(buf, 8, &src);
unpack32s(msg, buf, 8);

read32s(buf, 16, &src);
unpack32s(buf_char, buf, 16);
secp256k1_schnorrsig_parse(&sig, buf_char);

writeBit(dst, secp256k1_schnorrsig_verify(&ecmult_static.ctx, &sig, msg, &pubkey));
return true;
}
7 changes: 7 additions & 0 deletions C/jets.c
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
#include "sha256/compression.h"

bool adder32(frameItem* dst, frameItem src, const txEnv* env) {
(void) env; // env is unused;
uint_fast32_t x = read32(&src);
uint_fast32_t y = read32(&src);
writeBit(dst, 0xFFFFFFFF - y < x);
Expand All @@ -17,6 +18,7 @@ bool adder32(frameItem* dst, frameItem src, const txEnv* env) {
}

bool fullAdder32(frameItem* dst, frameItem src, const txEnv* env) {
(void) env; // env is unused;
/* :TODO: rewrite full adder so the carry bit comes first for better bit allignment */
uint_fast32_t x = read32(&src);
uint_fast32_t y = read32(&src);
Expand All @@ -33,6 +35,7 @@ bool fullAdder32(frameItem* dst, frameItem src, const txEnv* env) {
}

bool subtractor32(frameItem* dst, frameItem src, const txEnv* env) {
(void) env; // env is unused;
uint_fast32_t x = read32(&src);
uint_fast32_t y = read32(&src);
writeBit(dst, x < y);
Expand All @@ -41,6 +44,7 @@ bool subtractor32(frameItem* dst, frameItem src, const txEnv* env) {
}

bool fullSubtractor32(frameItem* dst, frameItem src, const txEnv* env) {
(void) env; // env is unused;
/* :TODO: rewrite full subtractor so the borrow bit comes first for better bit allignment */
uint_fast32_t x = read32(&src);
uint_fast32_t y = read32(&src);
Expand All @@ -51,13 +55,15 @@ bool fullSubtractor32(frameItem* dst, frameItem src, const txEnv* env) {
}

bool multiplier32(frameItem* dst, frameItem src, const txEnv* env) {
(void) env; // env is unused;
uint_fast64_t x = read32(&src);
uint_fast64_t y = read32(&src);
write64(dst, x * y);
return true;
}

bool fullMultiplier32(frameItem* dst, frameItem src, const txEnv* env) {
(void) env; // env is unused;
uint_fast64_t x = read32(&src);
uint_fast64_t y = read32(&src);
uint_fast64_t z = read32(&src);
Expand All @@ -67,6 +73,7 @@ bool fullMultiplier32(frameItem* dst, frameItem src, const txEnv* env) {
}

bool sha256_hashBlock(frameItem* dst, frameItem src, const txEnv* env) {
(void) env; // env is unused;
uint32_t h[8];
uint32_t block[16];
read32s(h, 8, &src);
Expand Down
5 changes: 5 additions & 0 deletions C/jets.h
Original file line number Diff line number Diff line change
Expand Up @@ -16,4 +16,9 @@ bool multiplier32(frameItem* dst, frameItem src, const txEnv* env);
bool fullMultiplier32(frameItem* dst, frameItem src, const txEnv* env);
bool sha256_hashBlock(frameItem* dst, frameItem src, const txEnv* env);

bool fe_sqrt(frameItem* dst, frameItem src, const txEnv* env);
bool offsetPoint(frameItem* dst, frameItem src, const txEnv* env);
bool ecmult(frameItem* dst, frameItem src, const txEnv* env);
bool schnorrVerify(frameItem* dst, frameItem src, const txEnv* env);

#endif
Loading