diff --git a/Makefile b/Makefile index 1c46576..f07853b 100644 --- a/Makefile +++ b/Makefile @@ -3,7 +3,7 @@ CC := $(TARGET)-gcc LD := $(TARGET)-gcc OBJCOPY := $(TARGET)-objcopy CFLAGS := -fPIC -O3 -fno-builtin-printf -fno-builtin-memcmp -nostdinc -nostdlib -nostartfiles -fvisibility=hidden -fdata-sections -ffunction-sections -I deps/secp256k1/src -I deps/secp256k1 -I deps/ckb-c-std-lib -I deps/ckb-c-std-lib/libc -I deps/ckb-c-std-lib/molecule -I c -I build -Wall -Werror -Wno-nonnull -Wno-nonnull-compare -Wno-unused-function -g -LDFLAGS := -Wl,-static -fdata-sections -ffunction-sections -Wl,--gc-sections +LDFLAGS := -nostdlib -nostartfiles -fno-builtin -Wl,-static -Wl,--gc-sections SECP256K1_SRC_20210801 := deps/secp256k1-20210801/src/ecmult_static_pre_context.h @@ -54,7 +54,8 @@ ${PROTOCOL_SCHEMA}: curl -L -o $@ ${PROTOCOL_URL} ALL_C_SOURCE := $(wildcard c/omni_lock.c c/omni_lock_acp.h c/omni_lock_time_lock.h \ - tests/omni_lock/omni_lock_sim.c tests/omni_lock/ckb_syscall_omni_lock_sim.h tests/omni_lock/omni_lock_supply.h) + tests/omni_lock/omni_lock_sim.c tests/omni_lock/ckb_syscall_omni_lock_sim.h tests/omni_lock/omni_lock_supply.h\ + c/blake2b_decl_only.h c/cobuild.h c/cobuild.c) fmt: docker run --rm -v `pwd`:/code ${CLANG_FORMAT_DOCKER} bash -c "cd code && clang-format -i -style=Google $(ALL_C_SOURCE)" @@ -76,17 +77,23 @@ omni_lock_mol: ${MOLC} --language - --schema-file c/omni_lock.mol --format json > build/omni_lock_mol2.json moleculec-c2 --input build/omni_lock_mol2.json | clang-format -style=Google > c/omni_lock_mol2.h -build/omni_lock: c/omni_lock.c c/omni_lock_supply.h c/omni_lock_acp.h c/secp256k1_lock.h build/secp256k1_data_info_20210801.h $(SECP256K1_SRC_20210801) c/ckb_identity.h - $(CC) $(OMNI_LOCK_CFLAGS) $(LDFLAGS) -o $@ $< +build/cobuild.o: c/cobuild.c c/cobuild.h + $(CC) -c $(OMNI_LOCK_CFLAGS) -o $@ $< + +build/omni_lock.o: c/omni_lock.c c/omni_lock_supply.h c/omni_lock_acp.h c/secp256k1_lock.h build/secp256k1_data_info_20210801.h $(SECP256K1_SRC_20210801) c/ckb_identity.h + $(CC) -c $(OMNI_LOCK_CFLAGS) -o $@ $< + +build/omni_lock: build/omni_lock.o build/cobuild.o + $(CC) $(LDFLAGS) -o $@ $^ cp $@ $@.debug $(OBJCOPY) --strip-debug --strip-all $@ - clean: rm -rf build/secp256k1_data_info_20210801.h build/dump_secp256k1_data_20210801 rm -rf build/secp256k1_data_20210801 rm -rf build/*.debug rm -f build/omni_lock + rm -f build/*.o cd deps/secp256k1-20210801 && [ -f "Makefile" ] && make clean install-tools: diff --git a/c/blake2b_decl_only.h b/c/blake2b_decl_only.h new file mode 100644 index 0000000..eb069e3 --- /dev/null +++ b/c/blake2b_decl_only.h @@ -0,0 +1,61 @@ + +#ifndef __BLAKE2B_DECL_ONLY_H__ +#define __BLAKE2B_DECL_ONLY_H__ + +#include +#include + +#define BLAKE2_PACKED(x) x __attribute__((packed)) + +enum blake2b_constant { + BLAKE2B_BLOCKBYTES = 128, + BLAKE2B_OUTBYTES = 64, + BLAKE2B_KEYBYTES = 64, + BLAKE2B_SALTBYTES = 16, + BLAKE2B_PERSONALBYTES = 16 +}; +BLAKE2_PACKED(struct blake2b_param__ { + uint8_t digest_length; /* 1 */ + uint8_t key_length; /* 2 */ + uint8_t fanout; /* 3 */ + uint8_t depth; /* 4 */ + uint32_t leaf_length; /* 8 */ + uint32_t node_offset; /* 12 */ + uint32_t xof_length; /* 16 */ + uint8_t node_depth; /* 17 */ + uint8_t inner_length; /* 18 */ + uint8_t reserved[14]; /* 32 */ + uint8_t salt[BLAKE2B_SALTBYTES]; /* 48 */ + uint8_t personal[BLAKE2B_PERSONALBYTES]; /* 64 */ +}); + +typedef struct blake2b_param__ blake2b_param; + +typedef struct blake2b_state__ { + uint64_t h[8]; + uint64_t t[2]; + uint64_t f[2]; + uint8_t buf[BLAKE2B_BLOCKBYTES]; + size_t buflen; + size_t outlen; + uint8_t last_node; +} blake2b_state; + +/* Streaming API */ +int ckb_blake2b_init(blake2b_state *S, size_t outlen); +int blake2b_init(blake2b_state *S, size_t outlen); +int blake2b_init_key(blake2b_state *S, size_t outlen, const void *key, + size_t keylen); +int blake2b_update(blake2b_state *S, const void *in, size_t inlen); +int blake2b_final(blake2b_state *S, void *out, size_t outlen); +/* Simple API */ +int blake2b(void *out, size_t outlen, const void *in, size_t inlen, + const void *key, size_t keylen); + +/* This is simply an alias for blake2b */ +int blake2(void *out, size_t outlen, const void *in, size_t inlen, + const void *key, size_t keylen); + +int blake2b_init_param(blake2b_state *S, const blake2b_param *P); + +#endif diff --git a/c/ckb_identity.h b/c/ckb_identity.h index 44f4086..0d1302c 100644 --- a/c/ckb_identity.h +++ b/c/ckb_identity.h @@ -83,6 +83,9 @@ typedef int (*validate_signature_t)(void *prefilled_data, const uint8_t *sig, typedef int (*convert_msg_t)(const uint8_t *msg, size_t msg_len, uint8_t *new_msg, size_t new_msg_len); +bool g_cobuild_enabled = false; +uint8_t g_cobuild_signing_message_hash[32]; + static void bin_to_hex(const uint8_t *source, uint8_t *dest, size_t len) { const static uint8_t HEX_TABLE[] = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'}; @@ -375,6 +378,11 @@ int validate_signature_eos(void *prefilled_data, const uint8_t *sig, } int generate_sighash_all(uint8_t *msg, size_t msg_len) { + if (g_cobuild_enabled) { + memcpy(msg, g_cobuild_signing_message_hash, BLAKE2B_BLOCK_SIZE); + return 0; + } + int ret; uint64_t len = 0; unsigned char temp[MAX_WITNESS_SIZE]; diff --git a/c/cobuild.c b/c/cobuild.c new file mode 100644 index 0000000..3378cfb --- /dev/null +++ b/c/cobuild.c @@ -0,0 +1,442 @@ +/** +This is an implementation in C of cobuild. See reference implementation in Rust: +https://github.com/cryptape/ckb-transaction-cobuild-poc/blob/main/ckb-transaction-cobuild/src/lib.rs +*/ +// clang-format off +#define CKB_DECLARATION_ONLY +#include +#include +#include +#include +#define MOLECULEC_C2_DECLARATION_ONLY +#define MOLECULEC2_VERSION 6001 +#define MOLECULE2_API_VERSION_MIN 5000 +#include "cobuild.h" +#include "molecule2_reader.h" + +#include "blake2b_decl_only.h" +#include "ckb_consts.h" +#include "ckb_syscall_apis.h" +// clang-format on +#ifndef MAX +#define MAX(a, b) ((a) > (b) ? (a) : (b)) +#endif + +#ifndef MIN +#define MIN(a, b) ((a) < (b) ? (a) : (b)) +#endif + +#define CHECK2(cond, code) \ + do { \ + if (!(cond)) { \ + printf("error at %s:%d, error code %d", __FILE__, __LINE__, code); \ + err = code; \ + ASSERT(0); \ + goto exit; \ + } \ + } while (0) + +#define CHECK(_code) \ + do { \ + int code = (_code); \ + if (code != 0) { \ + printf("error at %s:%d, error code %d", __FILE__, __LINE__, code); \ + err = code; \ + ASSERT(0); \ + goto exit; \ + } \ + } while (0) + +enum CobuildErrorCode { + // cobuild error code is from 110 + ERROR_GENERAL = 110, + ERROR_HASH, + ERROR_NONEMPTY_WITNESS, + ERROR_SIGHASHALL_DUP, + ERROR_SIGHASHALL_NOSEAL, +}; + +enum WitnessLayoutId { + WitnessLayoutSighashAll = 4278190081, + WitnessLayoutSighashAllOnly = 4278190082, + WitnessLayoutOtx = 4278190083, + WitnessLayoutOtxStart = 4278190084, +}; + +const char *PERSONAL_SIGHASH_ALL = "ckb-tcob-sighash"; +const char *PERSONAL_SIGHASH_ALL_ONLY = "ckb-tcob-sgohash"; +const char *PERSONAL_OTX = "ckb-tcob-otxhash"; + +/* + The seal cursor binds this data source. So the lifetime of data source should + be enough. + */ +static uint8_t g_cobuild_seal_data_source[DEFAULT_DATA_SOURCE_LENGTH]; + +#ifdef CKB_C_STDLIB_PRINTF + +static void bin_to_hex(const uint8_t *source, uint8_t *dest, size_t len) { + const static uint8_t HEX_TABLE[] = {'0', '1', '2', '3', '4', '5', '6', '7', + '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'}; + for (int i = 0; i < len; i++) { + dest[i * 2] = HEX_TABLE[source[i] >> 4]; + dest[i * 2 + 1] = HEX_TABLE[source[i] & 0x0F]; + } +} + +void print_raw_data(const char *name, uint8_t *data, size_t len) { + uint8_t str[924] = {0}; + const int limit = (sizeof(str) - 1) / 2; + if (len > limit) { + printf("The data length (%d) is too long, truncated to %d", len, limit); + len = limit; + } + bin_to_hex(data, str, len); + printf("%s: %s", name, str); +} + +void print_cursor(const char *name, mol2_cursor_t cursor) { + uint8_t data[256] = {0}; + uint32_t read_len = mol2_read_at(&cursor, data, sizeof(data)); + if (read_len >= sizeof(data)) { + printf("the cursor length (%d) is too long, truncated to %d", cursor.size, + read_len); + } + print_raw_data(name, data, MIN(read_len, sizeof(data))); +} + +#define BLAKE2B_UPDATE blake2b_update_debug +int blake2b_update_debug(blake2b_state *S, const void *pin, size_t inlen) { + blake2b_update(S, pin, inlen); + print_raw_data("blake2b_update: ", (uint8_t *)pin, inlen); + return 0; +} + +#else + +void print_raw_data(const char *name, uint8_t *data, size_t len); +void print_cursor(const char *name, mol2_cursor_t cursor); +#define BLAKE2B_UPDATE blake2b_update + +#endif + +static void store32(void *dst, uint32_t w) { + uint8_t *p = (uint8_t *)dst; + p[0] = (uint8_t)(w >> 0); + p[1] = (uint8_t)(w >> 8); + p[2] = (uint8_t)(w >> 16); + p[3] = (uint8_t)(w >> 24); +} + +int ckb_blake2b_init_personal(blake2b_state *S, size_t outlen, + const char *personal) { + blake2b_param P[1]; + + if ((!outlen) || (outlen > BLAKE2B_OUTBYTES)) return -1; + + P->digest_length = (uint8_t)outlen; + P->key_length = 0; + P->fanout = 1; + P->depth = 1; + store32(&P->leaf_length, 0); + store32(&P->node_offset, 0); + store32(&P->xof_length, 0); + P->node_depth = 0; + P->inner_length = 0; + memset(P->reserved, 0, sizeof(P->reserved)); + memset(P->salt, 0, sizeof(P->salt)); + memset(P->personal, 0, sizeof(P->personal)); + for (int i = 0; i < BLAKE2B_PERSONALBYTES; ++i) { + (P->personal)[i] = personal[i]; + } + return blake2b_init_param(S, P); +} + +int new_sighash_all_blake2b(blake2b_state *S) { + return ckb_blake2b_init_personal(S, 32, PERSONAL_SIGHASH_ALL); +} + +int new_sighash_all_only_blake2b(blake2b_state *S) { + return ckb_blake2b_init_personal(S, 32, PERSONAL_SIGHASH_ALL_ONLY); +} + +int new_otx_blake2b(blake2b_state *S) { + return ckb_blake2b_init_personal(S, 32, PERSONAL_OTX); +} + +// for lock script with message, the other witness in script group except first +// one should be empty +int ckb_check_others_in_group() { + int err = ERROR_GENERAL; + for (size_t index = 1;; index++) { + uint64_t witness_len = 0; + err = ckb_load_witness(0, &witness_len, 0, index, CKB_SOURCE_GROUP_INPUT); + if (err == CKB_INDEX_OUT_OF_BOUND) { + err = CKB_SUCCESS; + break; + } + CHECK(err); + CHECK2(witness_len > 0, ERROR_NONEMPTY_WITNESS); + } + +exit: + return err; +} + +typedef uint32_t(read_from_t)(uintptr_t arg[], uint8_t *ptr, uint32_t len, + uint32_t offset); + +static uint32_t read_from_witness(uintptr_t arg[], uint8_t *ptr, uint32_t len, + uint32_t offset) { + int err; + uint64_t output_len = len; + err = ckb_load_witness(ptr, &output_len, offset, arg[0], arg[1]); + if (err != 0) { + return 0; + } + if (output_len > len) { + return len; + } else { + return (uint32_t)output_len; + } +} + +static uint32_t read_from_cell_data(uintptr_t arg[], uint8_t *ptr, uint32_t len, + uint32_t offset) { + int err; + uint64_t output_len = len; + err = ckb_load_cell_data(ptr, &output_len, offset, arg[0], arg[1]); + if (err != 0) { + return 0; + } + if (output_len > len) { + return len; + } else { + return (uint32_t)output_len; + } +} + +void ckb_new_cursor(mol2_cursor_t *cursor, uint32_t total_len, + read_from_t read_from, uint8_t *data_source, + uint32_t cache_len, size_t index, size_t source) { + cursor->offset = 0; + cursor->size = (uint32_t)total_len; + + mol2_data_source_t *ptr = (mol2_data_source_t *)data_source; + + ptr->read = read_from; + ptr->total_size = (uint32_t)total_len; + ptr->args[0] = index; + ptr->args[1] = source; + + ptr->cache_size = 0; + ptr->start_point = 0; + ptr->max_cache_size = cache_len; + + cursor->data_source = ptr; +} + +int ckb_new_witness_cursor(mol2_cursor_t *cursor, uint8_t *data_source, + uint32_t cache_len, size_t index, size_t source) { + int err = 0; + uint64_t len = 0; + err = ckb_load_witness(0, &len, 0, index, source); + CHECK(err); + ckb_new_cursor(cursor, len, read_from_witness, data_source, cache_len, index, + source); + +exit: + return err; +} + +int ckb_hash_cursor(blake2b_state *ctx, mol2_cursor_t cursor) { + uint8_t batch[1024]; + while (true) { + uint32_t read_len = mol2_read_at(&cursor, batch, sizeof(batch)); + BLAKE2B_UPDATE(ctx, batch, read_len); + // adjust cursor + mol2_add_offset(&cursor, read_len); + mol2_sub_size(&cursor, read_len); + mol2_validate(&cursor); + if (cursor.size == 0) { + break; + } + } + return 0; +} + +static uint32_t try_union_unpack_id(const mol2_cursor_t *cursor, uint32_t *id) { + uint32_t len = mol2_read_at(cursor, (uint8_t *)id, 4); + if (len != 4) { + return MOL2_ERR_DATA; + } + return CKB_SUCCESS; +} + +int ckb_fetch_message(bool *has_message, mol2_cursor_t *message_cursor, + uint8_t *data_source, size_t cache_len) { + int err = 0; + int message_count = 0; + for (size_t index = 0;; index++) { + mol2_cursor_t cursor; + /** + We need the cursor valid after function returns. So we receive a memory + with longer lifetime. + */ + err = ckb_new_witness_cursor(&cursor, data_source, cache_len, index, + CKB_SOURCE_INPUT); + if (err == CKB_INDEX_OUT_OF_BOUND) { + err = 0; + break; + } + CHECK(err); + uint32_t id = 0; + err = try_union_unpack_id(&cursor, &id); + if (err) { + // it might be WitnessArgs layout and it is allowed in cobuild + err = 0; // reset error + continue; + } else { + if (id == WitnessLayoutSighashAll) { + mol2_union_t uni = mol2_union_unpack(&cursor); + /* See molecule defintion, the index is 1: + table SighashAll { + seal: Bytes, + message: Message, + } + */ + *message_cursor = mol2_table_slice_by_index(&uni.cursor, 1); + message_count++; + CHECK2(message_count <= 1, ERROR_SIGHASHALL_DUP); + } + } + } + *has_message = message_count > 0; + +exit: + return err; +} + +int ckb_fetch_seal(mol2_cursor_t *seal_cursor) { + int err = 0; + mol2_cursor_t cursor; + err = ckb_new_witness_cursor(&cursor, g_cobuild_seal_data_source, + MAX_CACHE_SIZE, 0, CKB_SOURCE_GROUP_INPUT); + CHECK(err); + uint32_t id = 0; + err = try_union_unpack_id(&cursor, &id); + // when error occurs here, it might be a WitnessArgs layout. It shouldn't be + // cobuild. + CHECK(err); + if (id == WitnessLayoutSighashAll || id == WitnessLayoutSighashAllOnly) { + mol2_union_t uni = mol2_union_unpack(&cursor); + /* See molecule defintion, the index is 0: + table SighashAll { + seal: Bytes, + message: Message, + } + table SighashAllOnly { + seal: Bytes, + } + */ + *seal_cursor = mol2_table_slice_by_index(&uni.cursor, 0); + } else { + CHECK2(false, ERROR_SIGHASHALL_NOSEAL); + } + +exit: + return err; +} + +int ckb_calculate_inputs_len(); + +int ckb_generate_signing_message_hash(bool has_message, + mol2_cursor_t message_cursor, + uint8_t *signing_message_hash) { + int err = 0; + // this data source is on stack. When this function returns, all cursors bound + // to this buffer become invalid. + uint8_t data_source[DEFAULT_DATA_SOURCE_LENGTH]; + + blake2b_state ctx; + // use different hash based on message + if (has_message) { + new_sighash_all_blake2b(&ctx); + ckb_hash_cursor(&ctx, message_cursor); + } else { + new_sighash_all_only_blake2b(&ctx); + } + + // hash tx hash + uint8_t tx_hash[32]; + uint64_t tx_hash_len = 32; + err = ckb_load_tx_hash(tx_hash, &tx_hash_len, 0); + CHECK(err); + BLAKE2B_UPDATE(&ctx, tx_hash, sizeof(tx_hash)); + + // hash input cell and data + int input_len = ckb_calculate_inputs_len(); + for (size_t index = 0; index < input_len; index++) { + uint8_t cell[128]; + uint64_t cell_len = sizeof(cell); + err = ckb_load_cell(cell, &cell_len, 0, index, CKB_SOURCE_INPUT); + CHECK(err); + BLAKE2B_UPDATE(&ctx, cell, cell_len); + + uint64_t cell_data_len = 0; + err = ckb_load_cell_data(0, &cell_data_len, 0, index, CKB_SOURCE_INPUT); + CHECK(err); + mol2_cursor_t cell_data_cursor; + ckb_new_cursor(&cell_data_cursor, cell_data_len, read_from_cell_data, + data_source, MAX_CACHE_SIZE, index, CKB_SOURCE_INPUT); + uint32_t cell_data_len2 = (uint32_t)cell_data_len; + BLAKE2B_UPDATE(&ctx, &cell_data_len2, 4); + err = ckb_hash_cursor(&ctx, cell_data_cursor); + CHECK(err); + } + + // hash remaining witness + for (size_t index = input_len;; index++) { + uint64_t witness_len = 0; + err = ckb_load_witness(0, &witness_len, 0, index, CKB_SOURCE_INPUT); + if (err == CKB_INDEX_OUT_OF_BOUND) { + break; + } + CHECK(err); + mol2_cursor_t witness_cursor; + ckb_new_cursor(&witness_cursor, witness_len, read_from_cell_data, + data_source, MAX_CACHE_SIZE, index, CKB_SOURCE_INPUT); + uint32_t witness_len2 = (uint32_t)witness_len; + BLAKE2B_UPDATE(&ctx, &witness_len2, 4); + err = ckb_hash_cursor(&ctx, witness_cursor); + CHECK(err); + } + blake2b_final(&ctx, signing_message_hash, 32); +exit: + return err; +} + +int ckb_parse_message(uint8_t *signing_message_hash, mol2_cursor_t *seal) { + int err = 0; + + err = ckb_check_others_in_group(); + CHECK(err); + bool has_message = false; + mol2_cursor_t message; + // the message cursor requires lifetime of data_source + uint8_t data_source[DEFAULT_DATA_SOURCE_LENGTH]; + err = ckb_fetch_message(&has_message, &message, data_source, MAX_CACHE_SIZE); + CHECK(err); + + err = ckb_generate_signing_message_hash(has_message, message, + signing_message_hash); + CHECK(err); + + err = ckb_fetch_seal(seal); + CHECK(err); + print_raw_data("signing_message_hash", signing_message_hash, 32); + print_cursor("seal", *seal); + +exit: + return err; +} diff --git a/c/cobuild.h b/c/cobuild.h new file mode 100644 index 0000000..6e223fd --- /dev/null +++ b/c/cobuild.h @@ -0,0 +1,16 @@ +#ifndef __COBUILD_H__ +#define __COBUILD_H__ + +#include +#include +#include + +#include "molecule2_reader.h" + +/** + * return non-zero if the transaction doesn't have cobuild support. + * signing_message_hash: signing message hash + * seal: the seal. Same as WitnessArgs.lock with new name. + */ +int ckb_parse_message(uint8_t *signing_message_hash, mol2_cursor_t *seal); +#endif diff --git a/c/omni_lock.c b/c/omni_lock.c index 1e092b7..3f63346 100644 --- a/c/omni_lock.c +++ b/c/omni_lock.c @@ -1,5 +1,6 @@ // uncomment to enable printf in CKB-VM -// #define CKB_C_STDLIB_PRINTF +#define CKB_C_STDLIB_PRINTF +#define CKB_C_STDLIB_PRINTF_BUFFER_SIZE 1024 // it's used by blockchain-api2.h, the behavior when panic #ifndef MOL2_EXIT @@ -39,6 +40,7 @@ int ckb_exit(signed char); #include "omni_lock_acp.h" #include "omni_lock_time_lock.h" #include "omni_lock_supply.h" +#include "cobuild.h" // clang-format on @@ -313,7 +315,7 @@ int smt_verify_identity(CkbIdentityType *id, SmtProofEntryVecType *proofs, return err; } -int parse_witness_lock(WitnessLockType *witness_lock) { +int parse_witness_lock(WitnessLockType *witness_lock, mol2_cursor_t *seal) { int err = 0; witness_lock->has_signature = false; witness_lock->has_identity = false; @@ -332,7 +334,12 @@ int parse_witness_lock(WitnessLockType *witness_lock) { BytesOptType mol_lock = witness_args.t->lock(&witness_args); if (mol_lock.t->is_none(&mol_lock)) return err; - mol2_cursor_t mol_lock_bytes = mol_lock.t->unwrap(&mol_lock); + mol2_cursor_t mol_lock_bytes = {0}; + if (seal) { + mol_lock_bytes = *seal; + } else { + mol_lock_bytes = mol_lock.t->unwrap(&mol_lock); + } // convert Bytes to OmniLockWitnessLock OmniLockWitnessLockType mol_witness_lock = make_OmniLockWitnessLock(&mol_lock_bytes); @@ -396,7 +403,18 @@ int main() { // args (args.id) CkbIdentityType identity = {0}; - err = parse_witness_lock(&witness_lock); + mol2_cursor_t seal = {0}; + /* + * When it fails, WitnessArgs is used. No cobuild enabled. + */ + err = ckb_parse_message(g_cobuild_signing_message_hash, &seal); + if (err) { + g_cobuild_enabled = false; + err = parse_witness_lock(&witness_lock, NULL); + } else { + g_cobuild_enabled = true; + err = parse_witness_lock(&witness_lock, &seal); + } CHECK(err); err = parse_args(&args); diff --git a/tests/omni_lock/omni_lock_sim.c b/tests/omni_lock/omni_lock_sim.c index 42c9770..344a68d 100644 --- a/tests/omni_lock/omni_lock_sim.c +++ b/tests/omni_lock/omni_lock_sim.c @@ -16,6 +16,11 @@ void debug_print_hex(const char* prefix, const uint8_t* buf, size_t length) { printf("\n"); } +int ckb_parse_message(uint8_t *signing_message_hash, mol2_cursor_t *seal) { + // not cobuild + return 1; +} + /* hex2bin modified from * https://chromium.googlesource.com/chromium/deps/xz/+/77022065014d48cf51d83322264ab4836fd175ec/debug/hex2bin.c */ diff --git a/tests/omni_lock_rust/Cargo.lock b/tests/omni_lock_rust/Cargo.lock index 00d109c..f74a536 100644 --- a/tests/omni_lock_rust/Cargo.lock +++ b/tests/omni_lock_rust/Cargo.lock @@ -868,7 +868,6 @@ dependencies = [ "faster-hex 0.3.1", "gdb-remote-protocol", "hex", - "includedir_codegen", "lazy_static", "openssl", "rand 0.6.5", diff --git a/tests/omni_lock_rust/Cargo.toml b/tests/omni_lock_rust/Cargo.toml index 48ca362..102b8aa 100644 --- a/tests/omni_lock_rust/Cargo.toml +++ b/tests/omni_lock_rust/Cargo.toml @@ -33,8 +33,4 @@ ckb-vm = { version = "=0.20.0-rc2", features = ["detect-asm"] } ripemd = "0.1.3" sha2 = "0.10.6" hex = "0.4.3" - -[build-dependencies] -includedir_codegen = "0.5.0" -blake2b-rs = "0.1.5" faster-hex = "0.3" diff --git a/tests/omni_lock_rust/build.rs b/tests/omni_lock_rust/build.rs deleted file mode 100644 index e6f0946..0000000 --- a/tests/omni_lock_rust/build.rs +++ /dev/null @@ -1,82 +0,0 @@ -pub use blake2b_rs::{Blake2b, Blake2bBuilder}; -use includedir_codegen::Compression; - -use std::{ - env, - fs::File, - io::{BufWriter, Read, Write}, - path::Path, -}; - -const PATH_PREFIX: &str = "../../build/"; -const BUF_SIZE: usize = 8 * 1024; -const CKB_HASH_PERSONALIZATION: &[u8] = b"ckb-default-hash"; - -const BINARIES: &[(&str, &str)] = &[ - ( - "omni_lock", - "d83e7ec3ed31b24f7627af8d05d8da7a87fdf1afa120ef75a5c487464e8472aa", - ), -]; - -fn main() { - let mut bundled = includedir_codegen::start("BUNDLED_CELL"); - - let out_path = Path::new(&env::var("OUT_DIR").unwrap()).join("code_hashes.rs"); - let mut out_file = BufWriter::new(File::create(&out_path).expect("create code_hashes.rs")); - - let mut errors = Vec::new(); - - for (name, expected_hash) in BINARIES { - let path = format!("{}{}", PATH_PREFIX, name); - - let mut buf = [0u8; BUF_SIZE]; - bundled - .add_file(&path, Compression::Gzip) - .expect("add files to resource bundle"); - - // build hash - let mut blake2b = new_blake2b(); - let mut fd = File::open(&path).expect("open file"); - loop { - let read_bytes = fd.read(&mut buf).expect("read file"); - if read_bytes > 0 { - blake2b.update(&buf[..read_bytes]); - } else { - break; - } - } - - let mut hash = [0u8; 32]; - blake2b.finalize(&mut hash); - - let actual_hash = faster_hex::hex_string(&hash).unwrap(); - if expected_hash != &actual_hash { - errors.push((name, expected_hash, actual_hash)); - continue; - } - - write!( - &mut out_file, - "pub const {}: [u8; 32] = {:?};\n", - format!("CODE_HASH_{}", name.to_uppercase()), - hash - ) - .expect("write to code_hashes.rs"); - } - - if !errors.is_empty() { - for (name, expected, actual) in errors.into_iter() { - eprintln!("{}: expect {}, actual {}", name, expected, actual); - } - panic!("not all hashes are right"); - } - - bundled.build("bundled.rs").expect("build resource bundle"); -} - -pub fn new_blake2b() -> Blake2b { - Blake2bBuilder::new(32) - .personal(CKB_HASH_PERSONALIZATION) - .build() -} diff --git a/tests/omni_lock_rust/tests/test_omni_lock.rs b/tests/omni_lock_rust/tests/test_omni_lock.rs index d420bfb..41380df 100644 --- a/tests/omni_lock_rust/tests/test_omni_lock.rs +++ b/tests/omni_lock_rust/tests/test_omni_lock.rs @@ -3,6 +3,10 @@ mod misc; +use std::fs::File; +use std::io::Read; + +use blake2b_rs::{Blake2b, Blake2bBuilder}; use ckb_chain_spec::consensus::ConsensusBuilder; use ckb_crypto::secp::Generator; use ckb_error::assert_error_eq; @@ -730,3 +734,32 @@ fn test_eth_displaying_unlock() { let verify_result = verifier.verify(MAX_CYCLES); verify_result.expect("pass verification"); } + +// this test can fail during development +#[test] +fn test_binary_unchanged() { + let mut buf = [0u8; 8 * 1024]; + // build hash + let mut blake2b = Blake2bBuilder::new(32) + .personal(b"ckb-default-hash") + .build(); + + let mut fd = File::open("../../build/omni_lock").expect("open file"); + loop { + let read_bytes = fd.read(&mut buf).expect("read file"); + if read_bytes > 0 { + blake2b.update(&buf[..read_bytes]); + } else { + break; + } + } + + let mut hash = [0u8; 32]; + blake2b.finalize(&mut hash); + + let actual_hash = faster_hex::hex_string(&hash).unwrap(); + assert_eq!( + "1a3fe8b555626d09a031e8c027d4d4c4231d8c46ca8fe2cc644b3341a26f311d", + &actual_hash + ); +}