From 71e65db233bdd988aec646dfea8a32cf6e964155 Mon Sep 17 00:00:00 2001 From: xjd Date: Thu, 28 Mar 2024 15:03:35 +0800 Subject: [PATCH 1/8] Solana (phantom wallet) draft support * make tx possible without radomness * add ed25519 library --- .gitmodules | 3 ++ Makefile | 17 ++++++++-- c/ckb_identity.h | 47 +++++++++++++++++++++++--- c/omni_lock.c | 1 - deps/ed25519 | 1 + tests/omni_lock_rust/tests/misc.rs | 53 +++++++++++++++++------------- 6 files changed, 92 insertions(+), 30 deletions(-) create mode 160000 deps/ed25519 diff --git a/.gitmodules b/.gitmodules index 6d068a4..2c177d5 100644 --- a/.gitmodules +++ b/.gitmodules @@ -8,3 +8,6 @@ [submodule "deps/secp256k1"] path = deps/secp256k1 url = https://github.com/nervosnetwork/secp256k1.git +[submodule "deps/ed25519"] + path = deps/ed25519 + url = https://github.com/nervosnetwork/ed25519.git diff --git a/Makefile b/Makefile index 9fedd3a..5bb1158 100644 --- a/Makefile +++ b/Makefile @@ -24,7 +24,7 @@ CLANG_FORMAT_DOCKER := xujiandong/ckb-riscv-llvm-toolchain@sha256:6409ab0d3e335c all: build/omni_lock build/always_success all-via-docker: - docker run --rm -v `pwd`:/code ${BUILDER_DOCKER} bash -c "cd /code && make" + docker run -u $(shell id -u):$(shell id -g) --rm -v `pwd`:/code ${BUILDER_DOCKER} bash -c "cd /code && make" build/always_success: c/always_success.c $(CC) $(CFLAGS) $(LDFLAGS) -o $@ $< @@ -45,6 +45,15 @@ $(SECP256K1_SRC): CC=$(CC) LD=$(LD) ./configure --enable-ecmult-static-precomputation --with-ecmult-window=6 --enable-module-recovery --host=$(TARGET) && \ make src/ecmult_static_pre_context.h src/ecmult_static_context.h +build/ed25519/%.o: deps/ed25519/src/%.c + mkdir -p build/ed25519 + $(CC) -c -DCKB_DECLARATION_ONLY -I deps/ed25519/src $(CFLAGS) -o $@ $^ + +build/libed25519.a: build/ed25519/sign.o build/ed25519/verify.o build/ed25519/sha512.o build/ed25519/sc.o build/ed25519/keypair.o \ + build/ed25519/key_exchange.o build/ed25519/ge.o build/ed25519/fe.o build/ed25519/add_scalar.o + $(AR) cr $@ $^ + + 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\ c/cobuild.h c/molecule2_verify.h mol2_utils.h) @@ -72,8 +81,8 @@ omni_lock_mol: build/omni_lock: c/omni_lock.c c/omni_lock_supply.h c/omni_lock_acp.h build/secp256k1_data_info.h $(SECP256K1_SRC) \ c/ckb_identity.h c/mol2_utils.h c/cobuild_basic_mol2.h c/molecule2_verify.h \ - c/cobuild.h c/mol2_utils.h c/molecule2_verify.h - $(CC) $(CFLAGS) $(LDFLAGS) -o $@ $< + c/cobuild.h c/mol2_utils.h c/molecule2_verify.h build/libed25519.a + $(CC) $(CFLAGS) $(LDFLAGS) -o $@ $< build/libed25519.a cp $@ $@.debug $(OBJCOPY) --strip-debug --strip-all $@ @@ -88,8 +97,10 @@ cobuild_mol: clean: clean2 rm -rf build/secp256k1_data_info.h build/dump_secp256k1_data rm -f build/secp256k1_data + rm -rf build/ed25519 build/libed25519.a cd deps/secp256k1 && [ -f "Makefile" ] && make clean +# not clean libraries, e.g. secp256k1 clean2: rm -rf build/*.debug rm -f build/omni_lock diff --git a/c/ckb_identity.h b/c/ckb_identity.h index f56c541..daef488 100644 --- a/c/ckb_identity.h +++ b/c/ckb_identity.h @@ -26,14 +26,16 @@ #define SECP256K1_MESSAGE_SIZE 32 #define MAX_PREIMAGE_SIZE 1024 #define MESSAGE_HEX_LEN 64 +#define ED25519_SIGNATURE_SIZE 64 +#define ED25519_PUBKEY_SIZE 32 const char BTC_PREFIX[] = "CKB (Bitcoin Layer) transaction: 0x"; // BTC_PREFIX_LEN = 35 -const size_t BTC_PREFIX_LEN = sizeof(BTC_PREFIX) - 1; +#define BTC_PREFIX_LEN (sizeof(BTC_PREFIX) - 1) const char COMMON_PREFIX[] = "CKB transaction: 0x"; -// COMMON_PREFIX_LEN = 17 -const size_t COMMON_PREFIX_LEN = sizeof(COMMON_PREFIX) - 1; +// COMMON_PREFIX_LEN = 19 +#define COMMON_PREFIX_LEN (sizeof(COMMON_PREFIX) - 1) enum CkbIdentityErrorCode { ERROR_IDENTITY_ARGUMENTS_LEN = -1, @@ -63,7 +65,6 @@ typedef struct CkbIdentityType { enum IdentityFlagsType { IdentityFlagsCkb = 0, - // values 1~5 are used by pw-lock IdentityFlagsEthereum = 1, IdentityFlagsEos = 2, IdentityFlagsTron = 3, @@ -72,6 +73,7 @@ enum IdentityFlagsType { IdentityCkbMultisig = 6, IdentityFlagsEthereumDisplaying = 18, + IdentityFlagsSolana = 19, IdentityFlagsOwnerLock = 0xFC, IdentityFlagsExec = 0xFD, IdentityFlagsDl = 0xFE, @@ -374,6 +376,37 @@ int validate_signature_eos(void *prefilled_data, const uint8_t *sig, return err; } +int ed25519_verify(const unsigned char *signature, const unsigned char *message, size_t message_len, const unsigned char *public_key); +int validate_signature_solana(void *prefilled_data, const uint8_t *sig, + size_t sig_len, const uint8_t *msg, size_t msg_len, + uint8_t *output, size_t *output_len) { + if (*output_len < AUTH160_SIZE || msg_len != SHA256_SIZE) { + return ERROR_INVALID_ARG; + } + + // CKB transaction: 0x + uint8_t displaying_msg[COMMON_PREFIX_LEN + MESSAGE_HEX_LEN] = {0}; + memcpy(displaying_msg, COMMON_PREFIX, COMMON_PREFIX_LEN); + bin_to_hex(msg, displaying_msg + COMMON_PREFIX_LEN, msg_len); + + // Unlike secp256k1, Ed25519 cannot recover the public key from the signature alone. + // The public key is located immediately after the signature. + const uint8_t* pubkey = sig + ED25519_SIGNATURE_SIZE; + int success = ed25519_verify(sig, displaying_msg, sizeof(displaying_msg), pubkey); + if (!success) { + return ERROR_MISMATCHED; + } + + uint8_t hash[SHA256_SIZE] = {0}; + blake2b_state ctx; + blake2b_init(&ctx, BLAKE2B_BLOCK_SIZE); + blake2b_update(&ctx, pubkey, ED25519_PUBKEY_SIZE); + blake2b_final(&ctx, hash, BLAKE2B_BLOCK_SIZE); + memcpy(output, hash, AUTH160_SIZE); + *output_len = AUTH160_SIZE; + return 0; +} + int generate_sighash_all(uint8_t *msg, size_t msg_len) { int ret; uint64_t len = 0; @@ -941,6 +974,12 @@ int ckb_verify_identity(CkbIdentityType *id, uint8_t *sig, uint32_t sig_size, } return verify_sighash_all(id->id, sig, sig_size, validate_signature_btc, convert_doge_message, signing_message_hash); + } else if (id->flags == IdentityFlagsSolana) { + if (sig == NULL || sig_size != (ED25519_SIGNATURE_SIZE + ED25519_PUBKEY_SIZE)) { + return ERROR_IDENTITY_WRONG_ARGS; + } + return verify_sighash_all(id->id, sig, sig_size, validate_signature_solana, + convert_copy, signing_message_hash); } else if (id->flags == IdentityCkbMultisig) { return verify_multisig(sig, sig_size, signing_message_hash, id->id); } else if (id->flags == IdentityFlagsOwnerLock) { diff --git a/c/omni_lock.c b/c/omni_lock.c index 04acdf8..c519e3e 100644 --- a/c/omni_lock.c +++ b/c/omni_lock.c @@ -136,7 +136,6 @@ bool is_memory_enough(mol_seg_t seg, const uint8_t *cur, uint32_t len) { int parse_args(ScriptType script, ArgsType *args) { int err = 0; - // TODO: do we need to validate Script structure here? mol2_cursor_t script_args = script.t->args(&script); // parse flags diff --git a/deps/ed25519 b/deps/ed25519 new file mode 160000 index 0000000..34582a3 --- /dev/null +++ b/deps/ed25519 @@ -0,0 +1 @@ +Subproject commit 34582a3340312ca1ce8eeccb0b1eb1ced548bd3c diff --git a/tests/omni_lock_rust/tests/misc.rs b/tests/omni_lock_rust/tests/misc.rs index 1735567..9a356bc 100644 --- a/tests/omni_lock_rust/tests/misc.rs +++ b/tests/omni_lock_rust/tests/misc.rs @@ -25,7 +25,7 @@ use ckb_types::{ H256 as CkbH256, }; use lazy_static::lazy_static; -use rand::prelude::{thread_rng, ThreadRng}; +use rand::prelude::thread_rng; use rand::seq::SliceRandom; use rand::Rng; use rand::RngCore; @@ -155,15 +155,6 @@ pub fn new_smt(pairs: Vec<(H256, H256)>) -> SMT { smt } -pub fn gen_random_out_point(rng: &mut ThreadRng) -> OutPoint { - let hash = { - let mut buf = [0u8; 32]; - rng.fill(&mut buf); - Pack::pack(&buf) - }; - OutPoint::new(hash, 0) -} - // // deploy "bin" to cell, then build a script to point it. // @@ -437,7 +428,7 @@ pub fn append_rc( ) -> TransactionBuilder { let smt_key = config.id.to_smt_key(); let (proofs, rc_datas, proof_masks) = generate_proofs(config.scheme, &vec![smt_key]); - let (rc_root, b0) = generate_rce_cell(dummy, tx_builder, rc_datas, config.smt_in_input); + let (rc_root, b0) = generate_rce_cell(dummy, tx_builder, rc_datas, config.smt_in_input, config); config.proofs = proofs; config.proof_masks = proof_masks; @@ -454,11 +445,14 @@ pub fn append_rc( pub fn append_input_lock_script_hash( dummy: &mut DummyDataLoader, tx_builder: TransactionBuilder, + config: &TestConfig, ) -> (TransactionBuilder, Bytes) { let mut rng = thread_rng(); let previous_tx_hash = { - let mut buf = [0u8; 32]; - rng.fill(&mut buf); + let mut buf = [3u8; 32]; + if config.random_tx { + rng.fill(&mut buf); + } buf.pack() }; let previous_out_point = OutPoint::new(previous_tx_hash, 0); @@ -686,8 +680,10 @@ pub fn gen_tx_with_grouped_args( // setup sighash_all dep let sighash_all_out_point = { let contract_tx_hash = { - let mut buf = [0u8; 32]; - rng.fill(&mut buf); + let mut buf = [1u8; 32]; + if config.random_tx { + rng.fill(&mut buf); + } buf.pack() }; OutPoint::new(contract_tx_hash.clone(), 0) @@ -700,8 +696,10 @@ pub fn gen_tx_with_grouped_args( // always success let always_success_out_point = { let contract_tx_hash = { - let mut buf = [0u8; 32]; - rng.fill(&mut buf); + let mut buf = [2u8; 32]; + if config.random_tx { + rng.fill(&mut buf); + } buf.pack() }; OutPoint::new(contract_tx_hash.clone(), 0) @@ -725,7 +723,7 @@ pub fn gen_tx_with_grouped_args( if config.is_owner_lock() { // insert an "always success" script as first input script. - let (b0, blake160) = append_input_lock_script_hash(dummy, tx_builder); + let (b0, blake160) = append_input_lock_script_hash(dummy, tx_builder, &config); tx_builder = b0; config.id.blake160 = blake160; } @@ -738,7 +736,9 @@ pub fn gen_tx_with_grouped_args( for _ in 0..inputs_size { let previous_tx_hash = { let mut buf = [0u8; 32]; - rng.fill(&mut buf); + if config.random_tx { + rng.fill(&mut buf); + } buf.pack() }; args = if config.is_owner_lock() { @@ -771,7 +771,9 @@ pub fn gen_tx_with_grouped_args( let mut random_extra_witness = Vec::::new(); let witness_len = if config.scheme == TestScheme::LongWitness { 40000 } else { 32 }; random_extra_witness.resize(witness_len, 0); - rng.fill(&mut random_extra_witness[..]); + if config.random_tx { + rng.fill(&mut random_extra_witness[..]); + } let witness_args = WitnessArgsBuilder::default() .input_type(Some(Bytes::copy_from_slice(&random_extra_witness[..])).pack()) @@ -1257,6 +1259,7 @@ pub struct TestConfig { pub cobuild_message: Option, pub custom_extension_witnesses: Option>, pub custom_extension_witnesses_beginning: Option>, + pub random_tx: bool, } #[derive(Copy, Clone, PartialEq)] @@ -1355,6 +1358,7 @@ impl TestConfig { cobuild_message: Some(Message::default()), custom_extension_witnesses: None, custom_extension_witnesses_beginning: None, + random_tx: true, } } @@ -1636,13 +1640,16 @@ pub fn generate_rce_cell( mut tx_builder: TransactionBuilder, rc_data: Vec, smt_in_input: bool, + config: &TestConfig, ) -> (Byte32, TransactionBuilder) { let mut rng = thread_rng(); let mut cell_vec_builder = RCCellVecBuilder::default(); for rc_rule in rc_data { let mut random_args: [u8; 32] = Default::default(); - rng.fill(&mut random_args[..]); + if config.random_tx { + rng.fill(&mut random_args[..]); + } // let's first build the RCE cell which contains the RCData(RCRule/RCCellVec). let (b0, rce_script) = build_script(dummy, tx_builder, true, smt_in_input, &rc_rule, Bytes::copy_from_slice(random_args.as_ref())); @@ -1658,7 +1665,9 @@ pub fn generate_rce_cell( let rce_cell_content = RCDataBuilder::default().set(RCDataUnion::RCCellVec(cell_vec)).build(); let mut random_args: [u8; 32] = Default::default(); - rng.fill(&mut random_args[..]); + if config.random_tx { + rng.fill(&mut random_args[..]); + } let bin = rce_cell_content.as_slice(); From e1b5fe50de1f33a3579089f2fcf2ea5b7ff75ed9 Mon Sep 17 00:00:00 2001 From: xjd Date: Thu, 28 Mar 2024 15:11:06 +0800 Subject: [PATCH 2/8] Fix simulator error --- tests/omni_lock/omni_lock_sim.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/tests/omni_lock/omni_lock_sim.c b/tests/omni_lock/omni_lock_sim.c index 9d3d8bf..c6a5162 100644 --- a/tests/omni_lock/omni_lock_sim.c +++ b/tests/omni_lock/omni_lock_sim.c @@ -38,6 +38,11 @@ int hex2bin(uint8_t* buf, const char* src) { return length; } +int ed25519_verify(const unsigned char* signature, const unsigned char* message, + size_t message_len, const unsigned char* public_key) { + return 0; +} + UTEST(pubkey_hash, pass) { init_input(&g_setting); g_setting.flags = IdentityFlagsCkb; From 79c35150b8ac8977bd20954218e001fa8b96a6ab Mon Sep 17 00:00:00 2001 From: xjd Date: Fri, 29 Mar 2024 09:45:42 +0800 Subject: [PATCH 3/8] Add test case for solana --- tests/omni_lock_rust/Cargo.lock | 160 ++++++++++++++++++- tests/omni_lock_rust/Cargo.toml | 1 + tests/omni_lock_rust/tests/misc.rs | 31 +++- tests/omni_lock_rust/tests/test_omni_lock.rs | 25 +++ 4 files changed, 211 insertions(+), 6 deletions(-) diff --git a/tests/omni_lock_rust/Cargo.lock b/tests/omni_lock_rust/Cargo.lock index 288c233..feb3908 100644 --- a/tests/omni_lock_rust/Cargo.lock +++ b/tests/omni_lock_rust/Cargo.lock @@ -59,6 +59,12 @@ version = "0.21.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9d297deb1925b89f2ccc13d7635fa0714f12c87adce1c75356b39ca9b7178567" +[[package]] +name = "base64ct" +version = "1.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8c3c1a368f70d6cf7302d78f8f7093da241fb8e8807c05cc9e51a125895a6d5b" + [[package]] name = "bit-vec" version = "0.6.3" @@ -524,6 +530,12 @@ dependencies = [ "bitflags 1.3.2", ] +[[package]] +name = "const-oid" +version = "0.9.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c2459377285ad874054d797f3ccebf984978aa39129f6eafde5cdc8315b612f8" + [[package]] name = "convert_case" version = "0.4.0" @@ -579,6 +591,44 @@ version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b365fabc795046672053e29c954733ec3b05e4be654ab130fe8f1f94d7051f35" +[[package]] +name = "curve25519-dalek" +version = "4.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0a677b8922c94e01bdbb12126b0bc852f00447528dee1782229af9c720c3f348" +dependencies = [ + "cfg-if 1.0.0", + "cpufeatures", + "curve25519-dalek-derive", + "digest 0.10.7", + "fiat-crypto", + "platforms", + "rustc_version", + "subtle", + "zeroize", +] + +[[package]] +name = "curve25519-dalek-derive" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f46882e17999c6cc590af592290432be3bce0428cb0d5f8b6715e4dc7b383eb3" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.48", +] + +[[package]] +name = "der" +version = "0.7.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fffa369a668c8af7dbf8b5e56c9f744fbd399949ed171606040001947de40b1c" +dependencies = [ + "const-oid", + "zeroize", +] + [[package]] name = "derive_more" version = "0.99.17" @@ -617,6 +667,30 @@ version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8d978bd5d343e8ab9b5c0fc8d93ff9c602fdc96616ffff9c05ac7a155419b824" +[[package]] +name = "ed25519" +version = "2.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "115531babc129696a58c64a4fef0a8bf9e9698629fb97e9e40767d235cfbcd53" +dependencies = [ + "pkcs8", + "signature", +] + +[[package]] +name = "ed25519-dalek" +version = "2.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4a3daa8e81a3963a60642bcc1f90a670680bd4a77535faa384e9d1c79d620871" +dependencies = [ + "curve25519-dalek", + "ed25519", + "serde", + "sha2", + "subtle", + "zeroize", +] + [[package]] name = "either" version = "1.9.0" @@ -654,6 +728,12 @@ version = "2.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "25cbce373ec4653f1a01a31e8a5e5ec0c622dc27ff9c4e6606eefef5cbbed4a5" +[[package]] +name = "fiat-crypto" +version = "0.2.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c007b1ae3abe1cb6f85a16305acd418b7ca6343b953633fee2b76d8f108b830f" + [[package]] name = "flate2" version = "1.0.28" @@ -792,7 +872,18 @@ checksum = "8fc3cb4d91f53b50155bdcfd23f6a4c39ae1969c2ae85982b135750cccaf5fce" dependencies = [ "cfg-if 1.0.0", "libc", - "wasi", + "wasi 0.9.0+wasi-snapshot-preview1", +] + +[[package]] +name = "getrandom" +version = "0.2.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "190092ea657667030ac6a35e305e62fc4dd69fd98ac98631e5d3a2b1575a12b5" +dependencies = [ + "cfg-if 1.0.0", + "libc", + "wasi 0.11.0+wasi-snapshot-preview1", ] [[package]] @@ -1044,6 +1135,7 @@ dependencies = [ "ckb-traits", "ckb-types", "ckb-vm", + "ed25519-dalek", "faster-hex 0.9.0", "hex", "lazy_static", @@ -1164,6 +1256,16 @@ version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" +[[package]] +name = "pkcs8" +version = "0.10.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f950b2377845cebe5cf8b5165cb3cc1a5e0fa5cfa3e1f7f55707d8fd82e0a7b7" +dependencies = [ + "der", + "spki", +] + [[package]] name = "pkg-config" version = "0.3.28" @@ -1176,6 +1278,12 @@ version = "0.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b4596b6d070b27117e987119b4dac604f3c58cfb0b191112e24771b2faeac1a6" +[[package]] +name = "platforms" +version = "3.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "db23d408679286588f4d4644f965003d056e3dd5abcaaa938116871d7ce2fee7" + [[package]] name = "ppv-lite86" version = "0.2.17" @@ -1225,7 +1333,7 @@ version = "0.7.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6a6b1679d49b24bbfe0c803429aa1874472f50d9b363131f0e89fc356b544d03" dependencies = [ - "getrandom", + "getrandom 0.1.16", "libc", "rand_chacha 0.2.2", "rand_core 0.5.1", @@ -1274,7 +1382,16 @@ version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "90bde5296fc891b0cef12a6d03ddccc162ce7b2aff54160af9338f8d40df6d19" dependencies = [ - "getrandom", + "getrandom 0.1.16", +] + +[[package]] +name = "rand_core" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" +dependencies = [ + "getrandom 0.2.12", ] [[package]] @@ -1558,6 +1675,15 @@ dependencies = [ "opaque-debug", ] +[[package]] +name = "signature" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77549399552de45a898a580c1b41d445bf730df867cc44e6c0233bbc4b8329de" +dependencies = [ + "rand_core 0.6.4", +] + [[package]] name = "siphasher" version = "0.3.11" @@ -1582,6 +1708,16 @@ dependencies = [ "cfg-if 0.1.10", ] +[[package]] +name = "spki" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d91ed6c858b01f942cd56b37a94b3e0a1798290327d1236e4d9cf4eaca44d29d" +dependencies = [ + "base64ct", + "der", +] + [[package]] name = "ssri" version = "9.2.0" @@ -1599,6 +1735,12 @@ dependencies = [ "xxhash-rust", ] +[[package]] +name = "subtle" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "81cdd64d312baedb58e21336b31bc043b77e01cc99033ce76ef539f78e965ebc" + [[package]] name = "syn" version = "1.0.109" @@ -1743,6 +1885,12 @@ version = "0.9.0+wasi-snapshot-preview1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cccddf32554fecc6acb585f82a32a72e28b48f8c4c1883ddfeeeaa96f7d8e519" +[[package]] +name = "wasi" +version = "0.11.0+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" + [[package]] name = "winapi" version = "0.3.9" @@ -1864,3 +2012,9 @@ name = "xxhash-rust" version = "0.8.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "53be06678ed9e83edb1745eb72efc0bbcd7b5c3c35711a860906aed827a13d61" + +[[package]] +name = "zeroize" +version = "1.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "525b4ec142c6b68a2d10f01f7bbf6755599ca3f81ea53b8431b7dd348f5fdb2d" diff --git a/tests/omni_lock_rust/Cargo.toml b/tests/omni_lock_rust/Cargo.toml index d88f404..91a1981 100644 --- a/tests/omni_lock_rust/Cargo.toml +++ b/tests/omni_lock_rust/Cargo.toml @@ -31,3 +31,4 @@ molecule = "0.7.5" blake2b-ref = "0.3.1" serde = "1.0" serde_json = "1.0" +ed25519-dalek = "2.1.1" \ No newline at end of file diff --git a/tests/omni_lock_rust/tests/misc.rs b/tests/omni_lock_rust/tests/misc.rs index 9a356bc..c95653a 100644 --- a/tests/omni_lock_rust/tests/misc.rs +++ b/tests/omni_lock_rust/tests/misc.rs @@ -37,6 +37,7 @@ use sparse_merkle_tree::{SparseMerkleTree, H256}; use ckb_chain_spec::consensus::ConsensusBuilder; use ckb_script::TransactionScriptsVerifier; use ckb_types::core::hardfork::HardForks; +use ed25519_dalek::SigningKey; use omni_lock_test::omni_lock; use omni_lock_test::omni_lock::OmniLockWitnessLock; use omni_lock_test::schemas::basic::{Message, SighashAll, SighashAllOnly}; @@ -551,7 +552,7 @@ pub fn sign_tx_by_input_group( .enumerate() .map(|(i, _)| { if i == begin_index { - let message = if use_chain_confg(config.id.flags) { + let message = if use_chain_config(config.id.flags) { assert!(config.chain_config.is_some()); config.chain_config.as_ref().unwrap().convert_message(&message) } else { @@ -590,7 +591,28 @@ pub fn sign_tx_by_input_group( } else if config.id.flags == IDENTITY_FLAGS_MULTISIG { let sig = config.multisig.sign(&message.into()); gen_witness_lock(sig, config.use_rc, config.use_rc_identity, &proof_vec, &identity, None) - } else if use_chain_confg(config.id.flags) { + } else if config.id.flags == IDENTITY_FLAGS_SOLANA { + // name conflicted + use ed25519_dalek::Signer; + // solana has different signing process and algorithm + let signing_key = SigningKey::from_bytes(&config.solana_secret_key); + let msg = String::from("CKB transaction: 0x") + &hex::encode(message); + println!("message to be signed by ed25519: {}", msg); + let sig = signing_key.sign(msg.as_bytes()); + let verifying_key = signing_key.verifying_key(); + + let mut sig_plus_pubkey = sig.to_vec(); + sig_plus_pubkey.extend(verifying_key.to_bytes()); + let sig_plus_pubkey: Bytes = sig_plus_pubkey.into(); + gen_witness_lock( + sig_plus_pubkey, + config.use_rc, + config.use_rc_identity, + &proof_vec, + &identity, + None, + ) + } else if use_chain_config(config.id.flags) { let sig_bytes = config.chain_config.as_ref().unwrap().sign(&config.private_key, message); println!("bitcoin sign(size: {}): {:02x?}", sig_bytes.len(), sig_bytes.to_vec()); gen_witness_lock(sig_bytes, config.use_rc, config.use_rc_identity, &proof_vec, &identity, None) @@ -858,6 +880,7 @@ pub const IDENTITY_FLAGS_BITCOIN: u8 = 4; pub const IDENTITY_FLAGS_DOGECOIN: u8 = 5; pub const IDENTITY_FLAGS_MULTISIG: u8 = 6; pub const IDENTITY_FLAGS_ETHEREUM_DISPLAYING: u8 = 18; +pub const IDENTITY_FLAGS_SOLANA: u8 = 19; pub const IDENTITY_FLAGS_OWNER_LOCK: u8 = 0xFC; pub const IDENTITY_FLAGS_EXEC: u8 = 0xFD; @@ -975,7 +998,7 @@ pub trait ChainConfig { fn sign(&self, privkey: &Privkey, message: CkbH256) -> Bytes; } -pub fn use_chain_confg(flags: u8) -> bool { +pub fn use_chain_config(flags: u8) -> bool { flags == IDENTITY_FLAGS_ETHEREUM || flags == IDENTITY_FLAGS_EOS || flags == IDENTITY_FLAGS_TRON @@ -1260,6 +1283,7 @@ pub struct TestConfig { pub custom_extension_witnesses: Option>, pub custom_extension_witnesses_beginning: Option>, pub random_tx: bool, + pub solana_secret_key: [u8; 32], } #[derive(Copy, Clone, PartialEq)] @@ -1359,6 +1383,7 @@ impl TestConfig { custom_extension_witnesses: None, custom_extension_witnesses_beginning: None, random_tx: true, + solana_secret_key: [0u8; 32], } } diff --git a/tests/omni_lock_rust/tests/test_omni_lock.rs b/tests/omni_lock_rust/tests/test_omni_lock.rs index 8203f46..fb5fc25 100644 --- a/tests/omni_lock_rust/tests/test_omni_lock.rs +++ b/tests/omni_lock_rust/tests/test_omni_lock.rs @@ -22,6 +22,7 @@ use ckb_types::{ prelude::*, H256, }; +use ed25519_dalek::SigningKey; use lazy_static::lazy_static; use misc::*; use omni_lock_test::schemas::{basic::*, blockchain::WitnessArgsBuilder, top_level::*}; @@ -550,6 +551,30 @@ fn test_eth_displaying_unlock() { verify_result.expect("pass verification"); } +#[test] +fn test_solana_unlock() { + let mut data_loader = DummyDataLoader::new(); + + let mut config = TestConfig::new(IDENTITY_FLAGS_SOLANA, false); + config.solana_secret_key = [0x01u8; 32]; + config.sig_len = 96; + + let signing_key = SigningKey::from_bytes(&config.solana_secret_key); + let verifying_key = signing_key.verifying_key(); + let blake160 = blake160(&verifying_key.to_bytes()); + let auth = Identity { flags: IDENTITY_FLAGS_SOLANA, blake160 }; + config.id = auth; + + let tx = gen_tx(&mut data_loader, &mut config); + let tx = sign_tx(&mut data_loader, tx, &mut config); + let resolved_tx = build_resolved_tx(&data_loader, &tx); + + let mut verifier = verify_tx(resolved_tx, data_loader); + verifier.set_debug_printer(debug_printer); + let verify_result = verifier.verify(MAX_CYCLES); + verify_result.expect("pass verification"); +} + // this test can fail during development // TODO: enable it when ready #[test] From 7e00f07ef69968a0b197ab1f6636a9158e1ccad8 Mon Sep 17 00:00:00 2001 From: xjd Date: Fri, 29 Mar 2024 15:17:42 +0800 Subject: [PATCH 4/8] Test case with phantom wallet --- tests/omni_lock_rust/Cargo.lock | 25 +++++++++ tests/omni_lock_rust/Cargo.toml | 3 +- tests/omni_lock_rust/tests/misc.rs | 54 ++++++++++++-------- tests/omni_lock_rust/tests/test_omni_lock.rs | 52 +++++++++++++++++++ 4 files changed, 113 insertions(+), 21 deletions(-) diff --git a/tests/omni_lock_rust/Cargo.lock b/tests/omni_lock_rust/Cargo.lock index feb3908..3cae86c 100644 --- a/tests/omni_lock_rust/Cargo.lock +++ b/tests/omni_lock_rust/Cargo.lock @@ -133,6 +133,15 @@ version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8d696c370c750c948ada61c69a0ee2cbbb9c50b1019ddb86d9317157a99c2cae" +[[package]] +name = "bs58" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bf88ba1141d185c399bee5288d850d63b8369520c1eafc32a0430b5b6c287bf4" +dependencies = [ + "tinyvec", +] + [[package]] name = "byteorder" version = "1.5.0" @@ -1126,6 +1135,7 @@ version = "0.1.0" dependencies = [ "blake2b-ref", "blake2b-rs 0.1.5", + "bs58", "ckb-chain-spec", "ckb-crypto", "ckb-error", @@ -1796,6 +1806,21 @@ dependencies = [ "syn 2.0.48", ] +[[package]] +name = "tinyvec" +version = "1.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "87cc5ceb3875bb20c2890005a4e226a4651264a5c75edb2421b52861a0a0cb50" +dependencies = [ + "tinyvec_macros", +] + +[[package]] +name = "tinyvec_macros" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" + [[package]] name = "tokio" version = "1.35.1" diff --git a/tests/omni_lock_rust/Cargo.toml b/tests/omni_lock_rust/Cargo.toml index 91a1981..6d20c47 100644 --- a/tests/omni_lock_rust/Cargo.toml +++ b/tests/omni_lock_rust/Cargo.toml @@ -31,4 +31,5 @@ molecule = "0.7.5" blake2b-ref = "0.3.1" serde = "1.0" serde_json = "1.0" -ed25519-dalek = "2.1.1" \ No newline at end of file +ed25519-dalek = "2.1.1" +bs58 = "0.5.1" diff --git a/tests/omni_lock_rust/tests/misc.rs b/tests/omni_lock_rust/tests/misc.rs index c95653a..dd1be60 100644 --- a/tests/omni_lock_rust/tests/misc.rs +++ b/tests/omni_lock_rust/tests/misc.rs @@ -592,26 +592,38 @@ pub fn sign_tx_by_input_group( let sig = config.multisig.sign(&message.into()); gen_witness_lock(sig, config.use_rc, config.use_rc_identity, &proof_vec, &identity, None) } else if config.id.flags == IDENTITY_FLAGS_SOLANA { - // name conflicted - use ed25519_dalek::Signer; - // solana has different signing process and algorithm - let signing_key = SigningKey::from_bytes(&config.solana_secret_key); - let msg = String::from("CKB transaction: 0x") + &hex::encode(message); - println!("message to be signed by ed25519: {}", msg); - let sig = signing_key.sign(msg.as_bytes()); - let verifying_key = signing_key.verifying_key(); - - let mut sig_plus_pubkey = sig.to_vec(); - sig_plus_pubkey.extend(verifying_key.to_bytes()); - let sig_plus_pubkey: Bytes = sig_plus_pubkey.into(); - gen_witness_lock( - sig_plus_pubkey, - config.use_rc, - config.use_rc_identity, - &proof_vec, - &identity, - None, - ) + if let Some(sig) = config.solana_phantom_sig.clone() { + let sig_plus_pubkey: Bytes = sig.into(); + gen_witness_lock( + sig_plus_pubkey, + config.use_rc, + config.use_rc_identity, + &proof_vec, + &identity, + None, + ) + } else { + // name conflicted + use ed25519_dalek::Signer; + // solana has different signing process and algorithm + let signing_key = SigningKey::from_bytes(&config.solana_secret_key); + let msg = String::from("CKB transaction: 0x") + &hex::encode(message); + println!("message to be signed by ed25519: {}", msg); + let sig = signing_key.sign(msg.as_bytes()); + let verifying_key = signing_key.verifying_key(); + + let mut sig_plus_pubkey = sig.to_vec(); + sig_plus_pubkey.extend(verifying_key.to_bytes()); + let sig_plus_pubkey: Bytes = sig_plus_pubkey.into(); + gen_witness_lock( + sig_plus_pubkey, + config.use_rc, + config.use_rc_identity, + &proof_vec, + &identity, + None, + ) + } } else if use_chain_config(config.id.flags) { let sig_bytes = config.chain_config.as_ref().unwrap().sign(&config.private_key, message); println!("bitcoin sign(size: {}): {:02x?}", sig_bytes.len(), sig_bytes.to_vec()); @@ -1284,6 +1296,7 @@ pub struct TestConfig { pub custom_extension_witnesses_beginning: Option>, pub random_tx: bool, pub solana_secret_key: [u8; 32], + pub solana_phantom_sig: Option>, } #[derive(Copy, Clone, PartialEq)] @@ -1384,6 +1397,7 @@ impl TestConfig { custom_extension_witnesses_beginning: None, random_tx: true, solana_secret_key: [0u8; 32], + solana_phantom_sig: None, } } diff --git a/tests/omni_lock_rust/tests/test_omni_lock.rs b/tests/omni_lock_rust/tests/test_omni_lock.rs index fb5fc25..a71e59d 100644 --- a/tests/omni_lock_rust/tests/test_omni_lock.rs +++ b/tests/omni_lock_rust/tests/test_omni_lock.rs @@ -575,6 +575,58 @@ fn test_solana_unlock() { verify_result.expect("pass verification"); } +/// Steps to update this test case: +/// +/// 1. Install Phantom wallet from: [Phantom Wallet](https://phantom.app/) +/// 2. Create an account on the wallet and obtain the Solana address. Update it +/// to the variable `address`. +/// 3. Run `cargo test test_solana_phantom_wallet -- --nocapture`. Find the +/// message to sign, for example: +/// ``` +/// Message to be signed by ed25519: CKB transaction: +/// 0x761f6986168340c33dfe016c7274fc30b3339d6d29dacc594f93addc700704fe +/// ``` +/// 4. Sign the message using [Phantom's message signing functionality](https://docs.phantom.app/solana/signing-a-message), e.g.: +/// ``` +/// provider.signMessage(new TextEncoder().encode("CKB transaction: +/// 0x761f6986168340c33dfe016c7274fc30b3339d6d29dacc594f93addc700704fe"), +/// "utf8") +/// ``` +/// 5. Update the variable `sig` with the obtained signature. +/// +#[test] +fn test_solana_phantom_wallet() { + let mut data_loader = DummyDataLoader::new(); + let address = "FK577f9qN4jiUJkQoiXvjuCcwmwLmB3sWwzBzX3ij8wG"; + let mut sig = vec![ + 139, 30, 199, 50, 16, 72, 145, 222, 75, 218, 182, 90, 47, 14, 110, 181, 226, 204, 15, 118, 122, 239, 221, 181, + 120, 164, 215, 252, 0, 72, 232, 235, 80, 74, 74, 107, 48, 10, 90, 145, 212, 44, 198, 233, 76, 253, 51, 91, 235, + 252, 117, 77, 242, 40, 68, 155, 143, 28, 252, 98, 94, 179, 6, 0, + ]; + + let verifying_key = bs58::decode(address).into_vec().unwrap(); + sig.extend(verifying_key.clone()); + + let mut config = TestConfig::new(IDENTITY_FLAGS_SOLANA, false); + config.random_tx = false; + config.sig_len = 96; + + let blake160 = blake160(&verifying_key); + let auth = Identity { flags: IDENTITY_FLAGS_SOLANA, blake160 }; + config.id = auth; + assert_eq!(sig.len(), 96); + config.solana_phantom_sig = Some(sig); + + let tx = gen_tx(&mut data_loader, &mut config); + let tx = sign_tx(&mut data_loader, tx, &mut config); + let resolved_tx = build_resolved_tx(&data_loader, &tx); + + let mut verifier = verify_tx(resolved_tx, data_loader); + verifier.set_debug_printer(debug_printer); + let verify_result = verifier.verify(MAX_CYCLES); + verify_result.expect("pass verification"); +} + // this test can fail during development // TODO: enable it when ready #[test] From 7aa854f015440a61d6874bfb880c46b1756d94ac Mon Sep 17 00:00:00 2001 From: xjd Date: Mon, 1 Apr 2024 10:44:18 +0800 Subject: [PATCH 5/8] Check consumed cycles --- tests/omni_lock_rust/tests/misc.rs | 24 ++++++++++---------- tests/omni_lock_rust/tests/test_omni_lock.rs | 16 ++++++++++--- tests/omni_lock_rust/tests/test_otx.rs | 10 ++++++-- 3 files changed, 33 insertions(+), 17 deletions(-) diff --git a/tests/omni_lock_rust/tests/misc.rs b/tests/omni_lock_rust/tests/misc.rs index dd1be60..c1c678f 100644 --- a/tests/omni_lock_rust/tests/misc.rs +++ b/tests/omni_lock_rust/tests/misc.rs @@ -645,13 +645,13 @@ pub fn sign_tx_by_input_group( .build(); let sighash_all = WitnessLayout::new_builder().set(sighash_all).build(); let sighash_all = sighash_all.as_bytes(); - println!( - "sighash_all with enum id(size: {}): {:02x?}", - sighash_all.len(), - sighash_all.as_ref() - ); + // println!( + // "sighash_all with enum id(size: {}): {:02x?}", + // sighash_all.len(), + // sighash_all.as_ref() + // ); let res = sighash_all.pack(); - println!("res(size: {}): {:02x?}", res.len(), res.as_bytes().as_ref()); + // println!("res(size: {}): {:02x?}", res.len(), res.as_bytes().as_ref()); res } None => { @@ -660,13 +660,13 @@ pub fn sign_tx_by_input_group( .build(); let sighash_all_only = WitnessLayout::new_builder().set(sighash_all_only).build(); let sighash_all_only = sighash_all_only.as_bytes(); - println!( - "sighash_all_only with enum id(size: {}): {:02x?}", - sighash_all_only.len(), - sighash_all_only.as_ref() - ); + // println!( + // "sighash_all_only with enum id(size: {}): {:02x?}", + // sighash_all_only.len(), + // sighash_all_only.as_ref() + // ); let res = sighash_all_only.pack(); - println!("res(size: {}): {:02x?}", res.len(), res.as_bytes().as_ref()); + // println!("res(size: {}): {:02x?}", res.len(), res.as_bytes().as_ref()); res } } diff --git a/tests/omni_lock_rust/tests/test_omni_lock.rs b/tests/omni_lock_rust/tests/test_omni_lock.rs index a71e59d..8d6a1c5 100644 --- a/tests/omni_lock_rust/tests/test_omni_lock.rs +++ b/tests/omni_lock_rust/tests/test_omni_lock.rs @@ -374,6 +374,9 @@ fn test_cobuild_btc_success(vtype: u8) { let mut verifier = verify_tx(resolved_tx, data_loader); verifier.set_debug_printer(debug_printer); let verify_result = verifier.verify(MAX_CYCLES); + let cycles = verify_result.clone().unwrap(); + // about ~1429386 + assert!(cycles < 1500000); verify_result.expect("pass verification"); } @@ -874,6 +877,10 @@ fn test_cobuild_sighash_all_only() { let mut verifier = verify_tx(resolved_tx, data_loader); verifier.set_debug_printer(debug_printer); let verify_result = verifier.verify(MAX_CYCLES); + let cycles = (&verify_result).as_ref().unwrap(); + println!("cycles = {}", *cycles); + // about ~1397402 + assert!(*cycles < 1410000); verify_result.expect("pass verification"); } @@ -1009,18 +1016,18 @@ fn test_cobuild_big_message() { let always_success_script_opt = ScriptOpt::new_builder().set(Some(always_success_script)).build(); let mut action_vec = Vec::::new(); - for _ in 0..3072 { + for _ in 0..12 { let action_builder = Action::new_builder(); let action_builder = action_builder.script_info_hash(ckb_types::packed::Byte32::from_slice(&[0x00; 32]).unwrap()); let action_builder = action_builder.script_hash(always_success_script_hash.clone()); - let action_builder = action_builder.data(vec![0x42; 128].pack()); + let action_builder = action_builder.data(vec![0x42; 1024 * 40].pack()); let action = action_builder.build(); action_vec.push(action); } let action_vec = ActionVec::new_builder().extend(action_vec).build(); let message = Message::new_builder().actions(action_vec).build(); - config.cobuild_message = Some(message); // Message is 651300 bytes in molecule type. + config.cobuild_message = Some(message); // Message is 500K bytes in molecule type. config.set_chain_config(Box::new(BitcoinConfig { sign_vtype: BITCOIN_V_TYPE_P2PKHCOMPRESSED, pubkey_err: false })); @@ -1045,6 +1052,9 @@ fn test_cobuild_big_message() { let mut verifier = verify_tx(resolved_tx, data_loader); verifier.set_debug_printer(debug_printer); let verify_result = verifier.verify(MAX_CYCLES); + let cycles = verify_result.as_ref().unwrap(); + println!("cycles = {}", *cycles); + assert!(*cycles < 16_000_000); verify_result.expect("pass verification"); } diff --git a/tests/omni_lock_rust/tests/test_otx.rs b/tests/omni_lock_rust/tests/test_otx.rs index 37d4d06..4257f89 100644 --- a/tests/omni_lock_rust/tests/test_otx.rs +++ b/tests/omni_lock_rust/tests/test_otx.rs @@ -86,7 +86,12 @@ impl Verifier { let str = format!("Script({})", hex::encode(&script.as_slice()[..4])); println!("{}: {}", str, msg); }); - verifier.verify(u64::MAX) + let result = verifier.verify(u64::MAX); + if result.is_ok() { + let cycles = (*result.as_ref().unwrap() as f64) / 1024.0 / 1024.0; + println!("cycles = {:.1} M ", cycles); + } + result } } @@ -1202,7 +1207,8 @@ fn test_cobuild_otx_prefix_and_suffix() { let tx = tx_builder.build(); let tx = ckb_types::core::cell::resolve_transaction(tx, &mut HashSet::new(), &dl, &dl).unwrap(); let verifier = Verifier::default(); - verifier.verify(&tx, &dl).unwrap(); + let cycles = verifier.verify(&tx, &dl).unwrap(); + assert!(cycles < 5_000_000); } #[test] From b87d9f788b4877b93b90e38244d0780cd90f1c4b Mon Sep 17 00:00:00 2001 From: xjd Date: Mon, 1 Apr 2024 13:14:15 +0800 Subject: [PATCH 6/8] Optimize get_witness_layout * avoid duplicated verifications on witnesses * printf with format %zu for size_t --- c/cobuild.h | 28 +++++++------ c/molecule2_verify.h | 42 ++++++++++++++------ tests/omni_lock_rust/tests/test_omni_lock.rs | 4 +- 3 files changed, 46 insertions(+), 28 deletions(-) diff --git a/c/cobuild.h b/c/cobuild.h index 83eb03a..f9742b6 100644 --- a/c/cobuild.h +++ b/c/cobuild.h @@ -209,7 +209,8 @@ int new_otx_blake2b(blake2b_state *S) { } static inline int get_witness_layout(BytesVecType witnesses, uint32_t index, - WitnessLayoutType *witness_layout) { + WitnessLayoutType *witness_layout, + bool verify_recursively) { bool existing = false; mol2_cursor_t witness = witnesses.t->get(&witnesses, index, &existing); if (!existing) { @@ -217,7 +218,7 @@ static inline int get_witness_layout(BytesVecType witnesses, uint32_t index, } WitnessLayoutType witness_layout2 = make_WitnessLayout(&witness); - if (verify_WitnessLayout(&witness_layout2)) { + if (verify_WitnessLayout(&witness_layout2, verify_recursively)) { return COBUILD_ERROR_GENERAL; } if (witness_layout != NULL) { @@ -248,7 +249,7 @@ int ckb_fetch_sighash_message(BytesVecType witnesses, MessageType *message) { uint32_t witness_len = witnesses.t->len(&witnesses); for (uint32_t index = 0; index < witness_len; index++) { WitnessLayoutType witness_layout = {0}; - if (get_witness_layout(witnesses, index, &witness_layout) == 0) { + if (get_witness_layout(witnesses, index, &witness_layout, false) == 0) { uint32_t id = witness_layout.t->item_id(&witness_layout); if (id == WitnessLayoutSighashAll) { // tested by: @@ -280,7 +281,7 @@ static inline int ckb_fetch_otx_start(BytesVecType witnesses, bool *has_otx, uint32_t witness_len = witnesses.t->len(&witnesses); for (uint32_t index = 0; index < witness_len; index++) { WitnessLayoutType witness_layout = {0}; - err = get_witness_layout(witnesses, index, &witness_layout); + err = get_witness_layout(witnesses, index, &witness_layout, false); if (err == 0) { uint32_t id = witness_layout.t->item_id(&witness_layout); if (id == WitnessLayoutOtxStart) { @@ -398,7 +399,7 @@ int ckb_generate_smh(const Env *env, mol2_cursor_t message_cursor, CKB_COBUILD_CHECK(err); } blake2b_final(&ctx, smh, BLAKE2B_BLOCK_SIZE); - printf("ckb_generate_smh total hashed %d bytes", count); + printf("ckb_generate_smh total hashed %zu bytes", count); exit: return err; @@ -532,7 +533,7 @@ int ckb_cobuild_normal_entry(const Env *env, ScriptEntryType callback) { CKB_SOURCE_GROUP_INPUT); CKB_COBUILD_CHECK(err); WitnessLayoutType witness_layout = make_WitnessLayout(&witness); - CKB_COBUILD_CHECK2(!verify_WitnessLayout(&witness_layout), + CKB_COBUILD_CHECK2(!verify_WitnessLayout(&witness_layout, false), COBUILD_ERROR_SIGHASHALL_NOSEAL); uint32_t id = witness_layout.t->item_id(&witness_layout); @@ -691,7 +692,7 @@ int ckb_generate_otx_smh(const Env *env, mol2_cursor_t message_cursor, err = ckb_hash_cursor(&ctx, header_dep_cursor); count += header_dep_cursor.size; } - printf("ckb_generate_otx_smh totally hashed %d bytes", count); + printf("ckb_generate_otx_smh totally hashed %zu bytes", count); blake2b_final(&ctx, smh, BLAKE2B_BLOCK_SIZE); exit: return err; @@ -712,9 +713,10 @@ int ckb_cobuild_entry(const Env *env, ScriptEntryType callback, // Legacy Flow Handling *cobuild_enabled = false; for (uint32_t i = 0; i < witness_len; i++) { - if (get_witness_layout(witnesses, i, NULL) == 0) { + if (get_witness_layout(witnesses, i, NULL, true) == 0) { *cobuild_enabled = true; - break; + // Do not break here. All witnesses are recursively verified at this + // point. Subsequent witnesses will not be recursively verified. } } if (!*cobuild_enabled) { @@ -749,7 +751,7 @@ int ckb_cobuild_entry(const Env *env, ScriptEntryType callback, printf("Otx starts at index %d(inclusive)", index); for (; index < witness_len; index++) { WitnessLayoutType witness_layout = {0}; - err = get_witness_layout(witnesses, index, &witness_layout); + err = get_witness_layout(witnesses, index, &witness_layout, false); if (err != 0) { // step 6, not WitnessLayoutOtx break; @@ -861,7 +863,7 @@ int ckb_cobuild_entry(const Env *env, ScriptEntryType callback, // [0, i) [j, +infinity) if (index < i || index >= j) { WitnessLayoutType witness_layout = {0}; - err = get_witness_layout(witnesses, index, &witness_layout); + err = get_witness_layout(witnesses, index, &witness_layout, false); if (err == 0) { // test_cobuild_otx_noexistent_otx_id uint32_t id = witness_layout.t->item_id(&witness_layout); @@ -883,7 +885,7 @@ int ckb_cobuild_entry(const Env *env, ScriptEntryType callback, CKB_COBUILD_CHECK_LOOP(err); if (memcmp(hash, env->current_script_hash, sizeof(hash)) == 0) { printf( - "Same lock script found beyond otx, at index %d. " + "Same lock script found beyond otx, at index %zu. " "ckb_cobuild_normal_entry called.", index); found = true; @@ -898,7 +900,7 @@ int ckb_cobuild_entry(const Env *env, ScriptEntryType callback, CKB_COBUILD_CHECK(err); } CKB_COBUILD_CHECK2(execution_count > 0, COBUILD_ERROR_NO_CALLBACK); - printf("execution_count = %d", execution_count); + printf("execution_count = %zu", execution_count); exit: return err; } diff --git a/c/molecule2_verify.h b/c/molecule2_verify.h index 96c1b55..fec7ac9 100644 --- a/c/molecule2_verify.h +++ b/c/molecule2_verify.h @@ -17,7 +17,7 @@ typedef enum WitnessLayoutId { } WitnessLayoutId; int verify_WitnessArgs(WitnessArgsType *witness); -int verify_WitnessLayout(WitnessLayoutType *witness); +int verify_WitnessLayout(WitnessLayoutType *witness, bool recursive); #ifndef MOLECULEC_C2_DECLARATION_ONLY @@ -177,28 +177,44 @@ int get_union_id(mol2_cursor_t *cur, uint32_t *union_id) { return MOL2_OK; } -int verify_WitnessLayout(WitnessLayoutType *witness) { +int verify_WitnessLayout(WitnessLayoutType *witness, bool recursive) { int err = MOL2_OK; uint32_t union_id = 0; - CHECK(get_union_id(&witness->cur, &union_id)); - + err = get_union_id(&witness->cur, &union_id); + CHECK(err); switch (union_id) { case WitnessLayoutSighashAll: { - SighashAllType sighash_all = witness->t->as_SighashAll(witness); - return verify_SighashAll(&sighash_all); + if (recursive) { + SighashAllType sighash_all = witness->t->as_SighashAll(witness); + err = verify_SighashAll(&sighash_all); + CHECK(err); + } + break; } case WitnessLayoutSighashAllOnly: { - SighashAllOnlyType sighash_all_only = - witness->t->as_SighashAllOnly(witness); - return verify_SighashAllOnly(&sighash_all_only); + if (recursive) { + SighashAllOnlyType sighash_all_only = + witness->t->as_SighashAllOnly(witness); + err = verify_SighashAllOnly(&sighash_all_only); + CHECK(err); + } + break; } case WitnessLayoutOtx: { - OtxType otx = witness->t->as_Otx(witness); - return verify_Otx(&otx); + if (recursive) { + OtxType otx = witness->t->as_Otx(witness); + err = verify_Otx(&otx); + CHECK(err); + } + break; } case WitnessLayoutOtxStart: { - OtxStartType otx_start = witness->t->as_OtxStart(witness); - return verify_OtxStart(&otx_start); + if (recursive) { + OtxStartType otx_start = witness->t->as_OtxStart(witness); + err = verify_OtxStart(&otx_start); + CHECK(err); + } + break; } default: { return MOL2_ERR_UNKNOWN_ITEM; diff --git a/tests/omni_lock_rust/tests/test_omni_lock.rs b/tests/omni_lock_rust/tests/test_omni_lock.rs index 8d6a1c5..8f42db6 100644 --- a/tests/omni_lock_rust/tests/test_omni_lock.rs +++ b/tests/omni_lock_rust/tests/test_omni_lock.rs @@ -879,8 +879,8 @@ fn test_cobuild_sighash_all_only() { let verify_result = verifier.verify(MAX_CYCLES); let cycles = (&verify_result).as_ref().unwrap(); println!("cycles = {}", *cycles); - // about ~1397402 - assert!(*cycles < 1410000); + // about ~1419872 + assert!(*cycles < 1430000); verify_result.expect("pass verification"); } From 5c25d6a1a48f7d5984170aa501d5567281ba1a4c Mon Sep 17 00:00:00 2001 From: xjd Date: Tue, 2 Apr 2024 09:12:47 +0800 Subject: [PATCH 7/8] Add solana test cases --- tests/omni_lock_rust/tests/misc.rs | 15 ++++++ tests/omni_lock_rust/tests/test_omni_lock.rs | 50 ++++++++++++++++++++ 2 files changed, 65 insertions(+) diff --git a/tests/omni_lock_rust/tests/misc.rs b/tests/omni_lock_rust/tests/misc.rs index c1c678f..2052b3a 100644 --- a/tests/omni_lock_rust/tests/misc.rs +++ b/tests/omni_lock_rust/tests/misc.rs @@ -91,6 +91,7 @@ pub const ERROR_SIGHASHALL_DUP: i8 = 113; pub const MOL2_ERR_OVERFLOW: i8 = 8; // parse witnesses error pub const ERROR_IDENTITY_WRONG_ARGS: i8 = 71; +pub const ERROR_MISMATCHED: i8 = 73; pub const ERROR_ARGS_FORMAT: i8 = 87; // https://github.com/bitcoin-core/secp256k1/blob/d373bf6d08c82ac5496bf8103698c9f54d8d99d2/include/secp256k1.h#L219 @@ -613,7 +614,19 @@ pub fn sign_tx_by_input_group( let verifying_key = signing_key.verifying_key(); let mut sig_plus_pubkey = sig.to_vec(); + match config.scheme { + TestScheme::SolanaWrongSignature => { + sig_plus_pubkey[0] ^= 1; + } + _ => {} + } sig_plus_pubkey.extend(verifying_key.to_bytes()); + match config.scheme { + TestScheme::SolanaWrongPubkey => { + sig_plus_pubkey[64] ^= 1; + } + _ => {} + } let sig_plus_pubkey: Bytes = sig_plus_pubkey.into(); gen_witness_lock( sig_plus_pubkey, @@ -1318,6 +1331,8 @@ pub enum TestScheme { OwnerLockWithoutWitness, RsaWrongSignature, + SolanaWrongSignature, + SolanaWrongPubkey, } #[derive(Copy, Clone, PartialEq)] diff --git a/tests/omni_lock_rust/tests/test_omni_lock.rs b/tests/omni_lock_rust/tests/test_omni_lock.rs index 8f42db6..bd2ccf9 100644 --- a/tests/omni_lock_rust/tests/test_omni_lock.rs +++ b/tests/omni_lock_rust/tests/test_omni_lock.rs @@ -578,6 +578,56 @@ fn test_solana_unlock() { verify_result.expect("pass verification"); } +#[test] +fn test_solana_wrong_pubkey() { + let mut data_loader = DummyDataLoader::new(); + + let mut config = TestConfig::new(IDENTITY_FLAGS_SOLANA, false); + config.solana_secret_key = [0x01u8; 32]; + config.sig_len = 96; + config.scheme = TestScheme::SolanaWrongPubkey; + + let signing_key = SigningKey::from_bytes(&config.solana_secret_key); + let verifying_key = signing_key.verifying_key(); + let blake160 = blake160(&verifying_key.to_bytes()); + let auth = Identity { flags: IDENTITY_FLAGS_SOLANA, blake160 }; + config.id = auth; + + let tx = gen_tx(&mut data_loader, &mut config); + let tx = sign_tx(&mut data_loader, tx, &mut config); + let resolved_tx = build_resolved_tx(&data_loader, &tx); + + let mut verifier = verify_tx(resolved_tx, data_loader); + verifier.set_debug_printer(debug_printer); + let verify_result = verifier.verify(MAX_CYCLES); + assert_script_error(verify_result.unwrap_err(), ERROR_MISMATCHED); +} + +#[test] +fn test_solana_wrong_signature() { + let mut data_loader = DummyDataLoader::new(); + + let mut config = TestConfig::new(IDENTITY_FLAGS_SOLANA, false); + config.solana_secret_key = [0x01u8; 32]; + config.sig_len = 96; + config.scheme = TestScheme::SolanaWrongSignature; + + let signing_key = SigningKey::from_bytes(&config.solana_secret_key); + let verifying_key = signing_key.verifying_key(); + let blake160 = blake160(&verifying_key.to_bytes()); + let auth = Identity { flags: IDENTITY_FLAGS_SOLANA, blake160 }; + config.id = auth; + + let tx = gen_tx(&mut data_loader, &mut config); + let tx = sign_tx(&mut data_loader, tx, &mut config); + let resolved_tx = build_resolved_tx(&data_loader, &tx); + + let mut verifier = verify_tx(resolved_tx, data_loader); + verifier.set_debug_printer(debug_printer); + let verify_result = verifier.verify(MAX_CYCLES); + assert_script_error(verify_result.unwrap_err(), ERROR_MISMATCHED); +} + /// Steps to update this test case: /// /// 1. Install Phantom wallet from: [Phantom Wallet](https://phantom.app/) From 60b7c5b52829810eaa62a6bc97d354cb329911a7 Mon Sep 17 00:00:00 2001 From: xjd Date: Sun, 7 Apr 2024 15:07:10 +0800 Subject: [PATCH 8/8] Add macro DISABLE_MESSAGE_CALCULATION_FLOW message calculation flow is not part of cobuild protocol. Some of lock scripts may need this. --- c/cobuild.h | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/c/cobuild.h b/c/cobuild.h index f9742b6..afe29c5 100644 --- a/c/cobuild.h +++ b/c/cobuild.h @@ -491,6 +491,14 @@ static int check_type_script_existing(MessageType msg) { static int parse_seal(const mol2_cursor_t original_seal, mol2_cursor_t *seal, uint8_t *message_calculation_flow) { int err = 0; + // message calculation flow is not part of cobuild protocol. + // Some of lock scripts may need this. +#ifdef DISABLE_MESSAGE_CALCULATION_FLOW + *seal = original_seal; + *message_calculation_flow = 0; + return 0; +#endif + uint32_t prefix_length = 1; uint8_t prefix[1] = {0};