From 1a09a4208b732a1937708c362c3d6d2d07372706 Mon Sep 17 00:00:00 2001 From: xjd Date: Thu, 11 Apr 2024 10:37:18 +0800 Subject: [PATCH 1/2] Solana (phantom wallet) support --- .gitmodules | 3 + Makefile | 16 +- c/ckb_identity.h | 50 ++++- deps/ed25519 | 1 + tests/omni_lock/omni_lock_sim.c | 5 + tests/omni_lock_rust/Cargo.lock | 185 ++++++++++++++++++- tests/omni_lock_rust/Cargo.toml | 2 + tests/omni_lock_rust/build.rs | 10 +- tests/omni_lock_rust/tests/misc.rs | 116 +++++++++--- tests/omni_lock_rust/tests/test_omni_lock.rs | 156 ++++++++++++++++ 10 files changed, 502 insertions(+), 42 deletions(-) create mode 160000 deps/ed25519 diff --git a/.gitmodules b/.gitmodules index b0c8ac4..f749147 100644 --- a/.gitmodules +++ b/.gitmodules @@ -10,3 +10,6 @@ path = deps/secp256k1-20210801 url = https://github.com/nervosnetwork/secp256k1.git branch = schnorr +[submodule "deps/ed25519"] + path = deps/ed25519 + url = https://github.com/nervosnetwork/ed25519.git diff --git a/Makefile b/Makefile index 1c46576..41a8b11 100644 --- a/Makefile +++ b/Makefile @@ -24,7 +24,7 @@ CLANG_FORMAT_DOCKER := kason223/clang-format@sha256:3cce35b0400a7d420ec8504558a0 all: build/omni_lock build/always_success all-via-docker: ${PROTOCOL_HEADER} - 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 @@ -47,6 +47,15 @@ $(SECP256K1_SRC_20210801): 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 $(OMNI_LOCK_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 $@ $^ + + build/impl.o: deps/ckb-c-std-lib/libc/src/impl.c $(CC) -c $(filter-out -DCKB_DECLARATION_ONLY, $(CFLAGS_MBEDTLS)) $(LDFLAGS_MBEDTLS) -o $@ $^ @@ -76,8 +85,8 @@ 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/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 build/libed25519.a + $(CC) $(OMNI_LOCK_CFLAGS) $(LDFLAGS) -o $@ $< build/libed25519.a cp $@ $@.debug $(OBJCOPY) --strip-debug --strip-all $@ @@ -88,6 +97,7 @@ clean: rm -rf build/*.debug rm -f build/omni_lock cd deps/secp256k1-20210801 && [ -f "Makefile" ] && make clean + rm -rf build/ed25519 build/libed25519.a install-tools: if [ ! -x "$$(command -v "${MOLC}")" ] \ diff --git a/c/ckb_identity.h b/c/ckb_identity.h index dbdb1c1..e7d8ef1 100644 --- a/c/ckb_identity.h +++ b/c/ckb_identity.h @@ -24,14 +24,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, @@ -61,7 +63,6 @@ typedef struct CkbIdentityType { enum IdentityFlagsType { IdentityFlagsCkb = 0, - // values 1~5 are used by pw-lock IdentityFlagsEthereum = 1, IdentityFlagsEos = 2, IdentityFlagsTron = 3, @@ -70,6 +71,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; @@ -953,8 +986,13 @@ int ckb_verify_identity(CkbIdentityType *id, uint8_t *sig, uint32_t sig_size, if (sig == NULL || sig_size != SECP256K1_SIGNATURE_SIZE) { return ERROR_IDENTITY_WRONG_ARGS; } - return verify_sighash_all(id->id, sig, sig_size, validate_signature_btc, - convert_doge_message); + return verify_sighash_all(id->id, sig, sig_size, validate_signature_btc, convert_doge_message); + } 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); } else if (id->flags == IdentityCkbMultisig) { uint8_t msg[BLAKE2B_BLOCK_SIZE]; int ret = generate_sighash_all(msg, sizeof(msg)); 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/omni_lock_sim.c b/tests/omni_lock/omni_lock_sim.c index 42c9770..4772d4d 100644 --- a/tests/omni_lock/omni_lock_sim.c +++ b/tests/omni_lock/omni_lock_sim.c @@ -37,6 +37,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; diff --git a/tests/omni_lock_rust/Cargo.lock b/tests/omni_lock_rust/Cargo.lock index 00d109c..65d7e65 100644 --- a/tests/omni_lock_rust/Cargo.lock +++ b/tests/omni_lock_rust/Cargo.lock @@ -38,6 +38,12 @@ version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" +[[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.5.1" @@ -96,6 +102,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" @@ -453,6 +468,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" @@ -506,6 +527,44 @@ dependencies = [ "typenum", ] +[[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 1.0.35", + "syn 2.0.48", +] + +[[package]] +name = "der" +version = "0.7.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f55bf8e7b65898637379c1b74eb1551107c8294ed26d855ceb9fd1a09cfc9bc0" +dependencies = [ + "const-oid", + "zeroize", +] + [[package]] name = "derive_more" version = "0.99.17" @@ -544,6 +603,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 = "env_logger" version = "0.4.3" @@ -566,6 +649,12 @@ version = "0.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "51e2ce894d53b295cf97b05685aa077950ff3e8541af83217fc720a6437169f8" +[[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" @@ -626,7 +715,18 @@ checksum = "8fc3cb4d91f53b50155bdcfd23f6a4c39ae1969c2ae85982b135750cccaf5fce" dependencies = [ "cfg-if 1.0.0", "libc", - "wasi", + "wasi 0.9.0+wasi-snapshot-preview1", +] + +[[package]] +name = "getrandom" +version = "0.2.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "94b22e06ecb0110981051723910cbf0b5f5e09a2062dd7663334ee79a9d1286c" +dependencies = [ + "cfg-if 1.0.0", + "libc", + "wasi 0.11.0+wasi-snapshot-preview1", ] [[package]] @@ -856,6 +956,7 @@ name = "omni-lock-test" version = "0.1.0" dependencies = [ "blake2b-rs", + "bs58", "ckb-chain-spec", "ckb-crypto", "ckb-error", @@ -865,6 +966,7 @@ dependencies = [ "ckb-types", "ckb-vm", "ckb-vm-debug-utils", + "ed25519-dalek", "faster-hex 0.3.1", "gdb-remote-protocol", "hex", @@ -966,6 +1068,16 @@ dependencies = [ "siphasher", ] +[[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" @@ -978,6 +1090,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" @@ -1033,7 +1151,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", @@ -1082,7 +1200,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.14", ] [[package]] @@ -1342,6 +1469,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.2.3" @@ -1357,6 +1493,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 = "strum" version = "0.8.0" @@ -1373,6 +1519,12 @@ dependencies = [ "syn 0.11.11", ] +[[package]] +name = "subtle" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "81cdd64d312baedb58e21336b31bc043b77e01cc99033ce76ef539f78e965ebc" + [[package]] name = "syn" version = "0.11.11" @@ -1458,6 +1610,21 @@ dependencies = [ "lazy_static", ] +[[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 = "toml" version = "0.5.11" @@ -1531,6 +1698,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" @@ -1561,3 +1734,9 @@ name = "winapi-x86_64-pc-windows-gnu" version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" + +[[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 48ca362..8995ca7 100644 --- a/tests/omni_lock_rust/Cargo.toml +++ b/tests/omni_lock_rust/Cargo.toml @@ -33,6 +33,8 @@ ckb-vm = { version = "=0.20.0-rc2", features = ["detect-asm"] } ripemd = "0.1.3" sha2 = "0.10.6" hex = "0.4.3" +ed25519-dalek = "2.1.1" +bs58 = "0.5.1" [build-dependencies] includedir_codegen = "0.5.0" diff --git a/tests/omni_lock_rust/build.rs b/tests/omni_lock_rust/build.rs index b0b2821..df349d6 100644 --- a/tests/omni_lock_rust/build.rs +++ b/tests/omni_lock_rust/build.rs @@ -12,12 +12,10 @@ 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", - "768f306681da232ceb0b94f436c5f813377179762a831c5ad8797bd4fd2d118d", - ), -]; +const BINARIES: &[(&str, &str)] = &[( + "omni_lock", + "6b29b6f10346b43c540e53806ba88ba0fe3a0c3a29d7448ef555840c8f8318fa", +)]; fn main() { let mut bundled = includedir_codegen::start("BUNDLED_CELL"); diff --git a/tests/omni_lock_rust/tests/misc.rs b/tests/omni_lock_rust/tests/misc.rs index 6454416..2ce8d49 100644 --- a/tests/omni_lock_rust/tests/misc.rs +++ b/tests/omni_lock_rust/tests/misc.rs @@ -29,8 +29,9 @@ use ckb_types::{ prelude::*, H256 as CkbH256, }; +use ed25519_dalek::SigningKey; 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; @@ -70,6 +71,7 @@ pub const ERROR_NO_PAIR: i8 = -44; pub const ERROR_DUPLICATED_INPUTS: i8 = -45; pub const ERROR_DUPLICATED_OUTPUTS: i8 = -46; pub const ERROR_LOCK_SCRIPT_HASH_NOT_FOUND: i8 = 70; +pub const ERROR_MISMATCHED: i8 = 73; pub const ERROR_NOT_ON_WHITE_LIST: i8 = 59; pub const ERROR_NO_WHITE_LIST: i8 = 83; pub const ERROR_ON_BLACK_LIST: i8 = 57; @@ -157,15 +159,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. // @@ -515,7 +508,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; @@ -532,11 +525,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); @@ -718,6 +714,54 @@ pub fn sign_tx_by_input_group( &identity, None, ) + } else if config.id.flags == IDENTITY_FLAGS_SOLANA { + if let Some(sig) = config.solana_phantom_sig.clone() { + let msg = String::from("CKB transaction: 0x") + &hex::encode(message); + println!("message to be signed by ed25519: {}", msg); + + 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(); + 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, + config.use_rc, + config.use_rc_identity, + &proof_vec, + &identity, + None, + ) + } } else { let sig = config.private_key.sign_recoverable(&message).expect("sign"); let sig_bytes = Bytes::from(sig.serialize()); @@ -782,8 +826,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) @@ -804,8 +850,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) @@ -824,8 +872,10 @@ pub fn gen_tx_with_grouped_args( // setup secp256k1_data dep let secp256k1_data_out_point = { let tx_hash = { - let mut buf = [0u8; 32]; - rng.fill(&mut buf); + let mut buf = [11u8; 32]; + if config.random_tx { + rng.fill(&mut buf); + } buf.pack() }; OutPoint::new(tx_hash, 0) @@ -883,7 +933,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; } @@ -896,7 +946,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() { @@ -939,7 +991,9 @@ pub fn gen_tx_with_grouped_args( 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()) @@ -1037,6 +1091,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; @@ -1450,6 +1505,9 @@ pub struct TestConfig { pub leading_witness_count: usize, pub chain_config: Option>, + pub random_tx: bool, + pub solana_secret_key: [u8; 32], + pub solana_phantom_sig: Option>, } #[derive(Copy, Clone, PartialEq)] @@ -1471,6 +1529,8 @@ pub enum TestScheme { OwnerLockWithoutWitness, RsaWrongSignature, + SolanaWrongSignature, + SolanaWrongPubkey, } #[derive(Copy, Clone, PartialEq)] @@ -1548,6 +1608,9 @@ impl TestConfig { leading_witness_count: 0, chain_config: None, + random_tx: true, + solana_secret_key: [0u8; 32], + solana_phantom_sig: None, } } @@ -1838,13 +1901,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, @@ -1869,7 +1935,9 @@ pub fn generate_rce_cell( .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(); diff --git a/tests/omni_lock_rust/tests/test_omni_lock.rs b/tests/omni_lock_rust/tests/test_omni_lock.rs index 69649d5..ca73cea 100644 --- a/tests/omni_lock_rust/tests/test_omni_lock.rs +++ b/tests/omni_lock_rust/tests/test_omni_lock.rs @@ -17,6 +17,7 @@ use ckb_types::{ prelude::*, H256, }; +use ed25519_dalek::SigningKey; use lazy_static::lazy_static; use misc::*; @@ -730,3 +731,158 @@ fn test_eth_displaying_unlock() { let verify_result = verifier.verify(MAX_CYCLES); 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 consensus = misc::gen_consensus(); + let tx_env = misc::gen_tx_env(); + let mut verifier = + TransactionScriptsVerifier::new(&resolved_tx, &consensus, &data_loader, &tx_env); + + verifier.set_debug_printer(debug_printer); + let verify_result = verifier.verify(MAX_CYCLES); + 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 consensus = misc::gen_consensus(); + let tx_env = misc::gen_tx_env(); + let mut verifier = + TransactionScriptsVerifier::new(&resolved_tx, &consensus, &data_loader, &tx_env); + + 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 consensus = misc::gen_consensus(); + let tx_env = misc::gen_tx_env(); + let mut verifier = + TransactionScriptsVerifier::new(&resolved_tx, &consensus, &data_loader, &tx_env); + + 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/) +/// 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: +/// 0xd3f012c170b17dc3af2287800a36326c115a82106ded34a05c925345007a988c +/// ``` +/// 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: +/// 0xd3f012c170b17dc3af2287800a36326c115a82106ded34a05c925345007a988c"), +/// "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![ + 110, 136, 73, 29, 91, 65, 30, 129, 36, 62, 6, 82, 128, 173, 75, 247, 131, 116, 154, 120, + 51, 37, 32, 32, 164, 43, 243, 66, 75, 190, 219, 196, 209, 118, 29, 0, 84, 117, 118, 5, 155, + 225, 113, 168, 41, 244, 10, 197, 216, 17, 213, 53, 114, 196, 39, 8, 17, 34, 54, 71, 12, + 133, 200, 6, + ]; + + 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 consensus = misc::gen_consensus(); + let tx_env = misc::gen_tx_env(); + let mut verifier = + TransactionScriptsVerifier::new(&resolved_tx, &consensus, &data_loader, &tx_env); + + verifier.set_debug_printer(debug_printer); + let verify_result = verifier.verify(MAX_CYCLES); + verify_result.expect("pass verification"); +} From bab40f5ef15aa5b37fb3455a408608f9ed9ee0a2 Mon Sep 17 00:00:00 2001 From: xjd Date: Thu, 11 Apr 2024 15:10:09 +0800 Subject: [PATCH 2/2] Add test case test_solana_wrong_auth --- tests/omni_lock_rust/tests/test_omni_lock.rs | 34 ++++++++++++++++++++ 1 file changed, 34 insertions(+) diff --git a/tests/omni_lock_rust/tests/test_omni_lock.rs b/tests/omni_lock_rust/tests/test_omni_lock.rs index ca73cea..f9ce4bd 100644 --- a/tests/omni_lock_rust/tests/test_omni_lock.rs +++ b/tests/omni_lock_rust/tests/test_omni_lock.rs @@ -763,6 +763,40 @@ fn test_solana_unlock() { verify_result.expect("pass verification"); } +#[test] +fn test_solana_wrong_auth() { + 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 mut blake160: Vec = blake160.into(); + blake160[0] ^= 0x01; + let blake160: Bytes = blake160.into(); + 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 consensus = misc::gen_consensus(); + let tx_env = misc::gen_tx_env(); + let mut verifier = + TransactionScriptsVerifier::new(&resolved_tx, &consensus, &data_loader, &tx_env); + + verifier.set_debug_printer(debug_printer); + let verify_result = verifier.verify(MAX_CYCLES); + assert_script_error(verify_result.unwrap_err(), ERROR_PUBKEY_BLAKE160_HASH); +} + #[test] fn test_solana_wrong_pubkey() { let mut data_loader = DummyDataLoader::new();