Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Solana (phantom wallet) support #48

Merged
merged 8 commits into from
Apr 8, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .gitmodules
Original file line number Diff line number Diff line change
Expand Up @@ -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
17 changes: 14 additions & 3 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -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 $@ $<
Expand All @@ -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)
Expand Down Expand Up @@ -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 $@ [email protected]
$(OBJCOPY) --strip-debug --strip-all $@

Expand 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
Expand Down
47 changes: 43 additions & 4 deletions c/ckb_identity.h
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down Expand Up @@ -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,
Expand All @@ -72,6 +73,7 @@ enum IdentityFlagsType {
IdentityCkbMultisig = 6,

IdentityFlagsEthereumDisplaying = 18,
IdentityFlagsSolana = 19,
IdentityFlagsOwnerLock = 0xFC,
IdentityFlagsExec = 0xFD,
IdentityFlagsDl = 0xFE,
Expand Down Expand Up @@ -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<signing message hash, hex format>
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;
Expand Down Expand Up @@ -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) {
Expand Down
36 changes: 23 additions & 13 deletions c/cobuild.h
Original file line number Diff line number Diff line change
Expand Up @@ -209,15 +209,16 @@ 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) {
return COBUILD_ERROR_MOL2_UNEXPECTED;
}

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) {
Expand Down Expand Up @@ -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:
Expand Down Expand Up @@ -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) {
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -490,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};

Expand Down Expand Up @@ -532,7 +541,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);
Expand Down Expand Up @@ -691,7 +700,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;
Expand All @@ -712,9 +721,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) {
Expand Down Expand Up @@ -749,7 +759,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;
Expand Down Expand Up @@ -861,7 +871,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);
Expand All @@ -883,7 +893,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;
Expand All @@ -898,7 +908,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;
}
Expand Down
42 changes: 29 additions & 13 deletions c/molecule2_verify.h
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down Expand Up @@ -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;
Expand Down
1 change: 0 additions & 1 deletion c/omni_lock.c
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
1 change: 1 addition & 0 deletions deps/ed25519
Submodule ed25519 added at 34582a
5 changes: 5 additions & 0 deletions tests/omni_lock/omni_lock_sim.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
Loading