Skip to content

Commit

Permalink
feat: add more bindings
Browse files Browse the repository at this point in the history
  • Loading branch information
XuJiandong committed Jan 6, 2025
1 parent f307ad9 commit 30e22a7
Show file tree
Hide file tree
Showing 2 changed files with 111 additions and 14 deletions.
2 changes: 0 additions & 2 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -68,8 +68,6 @@ CFLAGS_BASE_SECP256k1 = $(CFLAGS_BASE) \
LDFLAGS := -static --gc-sections
LDFLAGS += -Ldeps/compiler-rt-builtins-riscv/build -lcompiler-rt

SECP256K1_SRC := deps/secp256k1/src/precomputed_ecmult.c

all: out build/ckb-js-vm

out:
Expand Down
123 changes: 111 additions & 12 deletions src/secp256k1_module.c
Original file line number Diff line number Diff line change
@@ -1,22 +1,23 @@
#include <stdbool.h>
#include "cutils.h"
#include "secp256k1_module.h"
#include "secp256k1.h"
#include "secp256k1_recovery.h"
#include "group.h"
#undef CHECK
#undef CHECK2
#include "qjs.h"

#define COMPRESSED_PUBKEY_LENGTH 33 // 1 byte prefix + 32 bytes x coordinate
#define UNCOMPRESSED_PUBKEY_LENGTH 65 // 1 byte prefix + 32 bytes x + 32 bytes y

// from the secp256k1 library. The original table is used for signing operations and
// public key generation, and consumes significant memory. Since we only need verification
// functionality in our on-chain scripts (not signing or key generation), we can safely
// use this minimal placeholder to reduce memory usage.
const secp256k1_ge_storage secp256k1_ecmult_gen_prec_table[1][1];
const secp256k1_ge_storage secp256k1_ecmult_gen_prec_table[0][0];
const secp256k1_context *g_secp256k1_context = NULL;

static void free_array_buffer(JSRuntime *rt, void *_opaque, void *ptr) { js_free_rt(rt, ptr); }

static JSValue recover(JSContext *ctx, JSValueConst this_val, int argc, JSValueConst *argv) {
int err = 0;
JSValue ret = JS_EXCEPTION;
size_t msg_len, sig_len;
secp256k1_pubkey pubkey;
secp256k1_ecdsa_recoverable_signature signature;
Expand Down Expand Up @@ -49,18 +50,116 @@ static JSValue recover(JSContext *ctx, JSValueConst this_val, int argc, JSValueC

// Perform the recovery
int success = secp256k1_ecdsa_recover(g_secp256k1_context, &pubkey, &signature, msg);
CHECK2(success, -1);

if (!success) {
return JS_ThrowInternalError(ctx, "invalid signature");
}
// Return the public key with 64-bytes
ret = JS_NewArrayBufferCopy(ctx, pubkey.data, sizeof(pubkey));
return JS_NewArrayBufferCopy(ctx, pubkey.data, sizeof(pubkey));
}

static JSValue serialize_pubkey(JSContext *ctx, JSValueConst this_val, int argc, JSValueConst *argv) {
size_t pubkey_len;
uint8_t *pubkey_data;
secp256k1_pubkey pubkey;
unsigned int flags = SECP256K1_EC_COMPRESSED;

// Check argument count
if (argc != 2) return JS_ThrowTypeError(ctx, "wrong number of arguments");

// Get public key from first argument
pubkey_data = JS_GetArrayBuffer(ctx, &pubkey_len, argv[0]);
if (!pubkey_data || pubkey_len != sizeof(secp256k1_pubkey)) {
return JS_ThrowTypeError(ctx, "invalid public key format");
}

// Copy the public key data
memcpy(&pubkey, pubkey_data, sizeof(secp256k1_pubkey));

// Get compression flag from second argument if provided
flags = JS_ToBool(ctx, argv[1]) ? SECP256K1_EC_COMPRESSED : SECP256K1_EC_UNCOMPRESSED;

// Allocate output buffer (33 bytes for compressed, 65 for uncompressed)
size_t output_len = flags == SECP256K1_EC_COMPRESSED ? COMPRESSED_PUBKEY_LENGTH : UNCOMPRESSED_PUBKEY_LENGTH;
uint8_t *output = js_malloc(ctx, output_len);
if (!output) {
return JS_ThrowOutOfMemory(ctx);
}

// Serialize the public key
size_t serialized_len = output_len;
if (!secp256k1_ec_pubkey_serialize(g_secp256k1_context, output, &serialized_len, &pubkey, flags)) {
js_free(ctx, output);
return JS_ThrowInternalError(ctx, "serialization failed");
}

return JS_NewArrayBuffer(ctx, output, serialized_len, free_array_buffer, ctx, false);
}

static JSValue parse_pubkey(JSContext *ctx, JSValueConst this_val, int argc, JSValueConst *argv) {
size_t input_len;
uint8_t *input;
secp256k1_pubkey pubkey;

// Check argument count
if (argc != 1) return JS_ThrowTypeError(ctx, "wrong number of arguments");

// Get serialized public key from argument
input = JS_GetArrayBuffer(ctx, &input_len, argv[0]);
if (!input || (input_len != COMPRESSED_PUBKEY_LENGTH && input_len != UNCOMPRESSED_PUBKEY_LENGTH)) {
return JS_ThrowTypeError(ctx, "invalid public key format");
}

// Parse the public key
if (!secp256k1_ec_pubkey_parse(g_secp256k1_context, &pubkey, input, input_len)) {
return JS_ThrowTypeError(ctx, "invalid public key");
}

// Return the parsed public key
return JS_NewArrayBufferCopy(ctx, pubkey.data, sizeof(pubkey));
}

static JSValue verify(JSContext *ctx, JSValueConst this_val, int argc, JSValueConst *argv) {
size_t msg_len, sig_len, pubkey_len;
uint8_t *msg = NULL, *sig = NULL, *pubkey_data = NULL;
secp256k1_pubkey pubkey;
secp256k1_ecdsa_signature signature;

// Check argument count
if (argc != 3) return JS_ThrowTypeError(ctx, "wrong number of arguments");

// Get signature from first argument
sig = JS_GetArrayBuffer(ctx, &sig_len, argv[0]);
if (!sig || sig_len != 64) return JS_ThrowTypeError(ctx, "invalid signature format");

// Parse the signature
if (!secp256k1_ecdsa_signature_parse_compact(g_secp256k1_context, &signature, sig)) {
return JS_ThrowTypeError(ctx, "invalid signature");
}

// Get public key from second argument
pubkey_data = JS_GetArrayBuffer(ctx, &pubkey_len, argv[1]);
if (!pubkey_data || pubkey_len != sizeof(secp256k1_pubkey)) {
return JS_ThrowTypeError(ctx, "invalid public key format");
}

// Copy the public key data
memcpy(&pubkey, pubkey_data, sizeof(secp256k1_pubkey));

// Get message hash from third argument
msg = JS_GetArrayBuffer(ctx, &msg_len, argv[2]);
if (!msg || msg_len != 32) return JS_ThrowTypeError(ctx, "message must be 32 bytes");

// Perform the verification
int result = secp256k1_ecdsa_verify(g_secp256k1_context, &signature, msg, &pubkey);

exit:
if (err) return JS_EXCEPTION;
return ret;
return JS_NewBool(ctx, result == 1);
}

static const JSCFunctionListEntry js_secp256k1_funcs[] = {
JS_CFUNC_DEF("recover", 3, recover),
JS_CFUNC_DEF("serializePubkey", 2, serialize_pubkey),
JS_CFUNC_DEF("parsePubkey", 1, parse_pubkey),
JS_CFUNC_DEF("verify", 3, verify),
};

static int js_secp256k1_init(JSContext *ctx, JSModuleDef *m) {
Expand Down

0 comments on commit 30e22a7

Please sign in to comment.