ckb-script 1.1.0

CKB component to run the type/lock scripts
Documentation
#include <stdlib.h>

#include "sha3.h"

#define SHA3_BLOCK_SIZE 32

#define CUSTOM_ABORT 1
#define CUSTOM_PRINT_ERR 1

#include "syscall.h"
void custom_abort() { syscall_errno(93, 10, 0, 0, 0, 0, 0); }

int custom_print_err(const char* arg, ...) {
    (void)arg;
    return 0;
}

#include <secp256k1_static.h>
/*
 * We are including secp256k1 implementation directly so gcc can strip
 * unused functions. For some unknown reasons, if we link in libsecp256k1.a
 * directly, the final binary will include all functions rather than those used.
 */
#include <secp256k1.c>

int char_to_int(char ch) {
    if (ch >= '0' && ch <= '9') {
        return ch - '0';
    }
    if (ch >= 'a' && ch <= 'f') {
        return ch - 'a' + 10;
    }
    return -1;
}

int hex_to_bin(char* buf, size_t buf_len, const char* hex) {
    int i = 0;

    for (; i < buf_len && hex[i * 2] != '\0' && hex[i * 2 + 1] != '\0'; i++) {
        int a = char_to_int(hex[i * 2]);
        int b = char_to_int(hex[i * 2 + 1]);

        if (a < 0 || b < 0) {
            return -1;
        }

        buf[i] = ((a & 0xF) << 4) | (b & 0xF);
    }

    if (i == buf_len && hex[i * 2] != '\0') {
        return -1;
    }
    return i;
}

#define CHECK_LEN(x) \
    if ((x) <= 0) {  \
        return x;    \
    }

/*
 * Arguments are listed in the following order:
 * 0. Program name, ignored here, only preserved for compatibility reason
 * 1. Current script hash in hex format, which is 128 bytes. While this program
 * cannot verify the hash directly, this ensures the script is include in
 * signature calculation
 * 2. Other additional parameters that might be included. Notice only ASCII
 * characters are included, so binary should be passed as binary format.
 * 3. Pubkey in hex format, a maximum of 130 bytes will be processed
 * 4. Signature in hex format, a maximum of 512 bytes will be processed
 *
 * This program will run double sha256 on all arguments excluding pubkey and
 * signature(also for simplicity, we are running sha256 on ASCII chars directly,
 * not deserialized raw bytes), then it will use sha256 result calculated as the
 * message to verify the signature. It returns 0 if the signature works, and
 * a non-zero value otherwise.
 *
 * Note all hex values passed in as arguments must have lower case letters for
 * deterministic behavior.
 */
int main(int argc, char* argv[]) {
    char buf[256];
    int len;

    if (argc < 4) {
        return -1;
    }

    secp256k1_context context;
    int ret = secp256k1_context_initialize(&context, SECP256K1_CONTEXT_VERIFY);
    if (ret == 0) {
        return 4;
    }

    len = hex_to_bin(buf, 65, argv[argc - 2]);
    CHECK_LEN(len);
    secp256k1_pubkey pubkey;

    ret = secp256k1_ec_pubkey_parse(&context, &pubkey, buf, len);
    if (ret == 0) {
        return 1;
    }

    len = hex_to_bin(buf, 256, argv[argc - 1]);
    CHECK_LEN(len);
    secp256k1_ecdsa_signature signature;
    secp256k1_ecdsa_signature_parse_der(&context, &signature, buf, len);
    if (ret == 0) {
        return 3;
    }

    sha3_ctx_t sha3_ctx;
    unsigned char hash[SHA3_BLOCK_SIZE];
    sha3_init(&sha3_ctx, SHA3_BLOCK_SIZE);
    for (int i = 1; i < argc - 2; i++) {
        sha3_update(&sha3_ctx, argv[i], strlen(argv[i]));
    }
    sha3_final(hash, &sha3_ctx);

    sha3_init(&sha3_ctx, SHA3_BLOCK_SIZE);
    sha3_update(&sha3_ctx, hash, SHA3_BLOCK_SIZE);
    sha3_final(hash, &sha3_ctx);

    ret = secp256k1_ecdsa_verify(&context, &signature, hash, &pubkey);
    if (ret == 1) {
        ret = 0;
    } else {
        ret = 2;
    }

    return ret;
}