Skip to content

Commit

Permalink
Implement sol lock
Browse files Browse the repository at this point in the history
  • Loading branch information
mohanson committed Aug 1, 2024
1 parent 909ac18 commit f5da627
Show file tree
Hide file tree
Showing 18 changed files with 657 additions and 12 deletions.
113 changes: 113 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ members = [
# Please don't remove the following line, we use it to automatically
# detect insertion point for newly generated crates.
# @@INSERTION_POINT@@
"contracts/ccc-sol-lock",
"contracts/ccc-eth-lock",
"contracts/ccc-btc-lock",
"crates/ckb-lock-helper"
Expand Down
1 change: 1 addition & 0 deletions checksums.txt
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
3d659b15f2aad5f9350f55ce471806c6d6ad4f51a555a82b7918e9d88f84f04a build/release/ccc-btc-lock
4ae08bd7ed954997dcbca5ff88700bf7f949b1080c2bd1cb024f15c8b0436396 build/release/ccc-eth-lock
66bbb7041a10a0b2a2fd51ae2aa9394e9f7ee6e8b2b32dd5d3e4d37c0d4a64b8 build/release/ccc-sol-lock
2 changes: 2 additions & 0 deletions contracts/ccc-sol-lock/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
/build
/target
10 changes: 10 additions & 0 deletions contracts/ccc-sol-lock/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
[package]
name = "ccc-sol-lock"
version = "0.1.0"
edition = "2021"

[dependencies]
ckb-std = "0.15"
ckb-lock-helper = { path = "../../crates/ckb-lock-helper" }
ed25519-dalek = { version = "2.1.1", default-features = false }
hex = { version = "0.4", default-features = false, features = ["alloc"] }
77 changes: 77 additions & 0 deletions contracts/ccc-sol-lock/Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
# We cannot use $(shell pwd), which will return unix path format on Windows,
# making it hard to use.
cur_dir = $(dir $(abspath $(lastword $(MAKEFILE_LIST))))

TOP := $(cur_dir)
# RUSTFLAGS that are likely to be tweaked by developers. For example,
# while we enable debug logs by default here, some might want to strip them
# for minimal code size / consumed cycles.
CUSTOM_RUSTFLAGS := --cfg debug_assertions
# RUSTFLAGS that are less likely to be tweaked by developers. Most likely
# one would want to keep the default values here.
FULL_RUSTFLAGS := -C target-feature=+zba,+zbb,+zbc,+zbs $(CUSTOM_RUSTFLAGS)
# Additional cargo args to append here. For example, one can use
# make test CARGO_ARGS="-- --nocapture" so as to inspect data emitted to
# stdout in unit tests
CARGO_ARGS :=
MODE := release
# Tweak this to change the clang version to use for building C code. By default
# we use a bash script with somes heuristics to find clang in current system.
CLANG := $(shell $(TOP)/scripts/find_clang)
AR := $(subst clang,llvm-ar,$(CLANG))
# When this is set to some value, the generated binaries will be copied over
BUILD_DIR :=
# Generated binaries to copy. By convention, a Rust crate's directory name will
# likely match the crate name, which is also the name of the final binary.
# However if this is not the case, you can tweak this variable. As the name hints,
# more than one binary is supported here.
BINARIES := $(notdir $(shell pwd))

ifeq (release,$(MODE))
MODE_ARGS := --release
endif

default: build test

build:
RUSTFLAGS="$(FULL_RUSTFLAGS)" TARGET_CC="$(CLANG)" TARGET_AR="$(AR)" \
cargo build --target=riscv64imac-unknown-none-elf $(MODE_ARGS) $(CARGO_ARGS)
@set -eu; \
if [ "x$(BUILD_DIR)" != "x" ]; then \
for binary in $(BINARIES); do \
echo "Copying binary $$binary to build directory"; \
cp $(TOP)/target/riscv64imac-unknown-none-elf/$(MODE)/$$binary $(TOP)/$(BUILD_DIR); \
done \
fi

# test, check, clippy and fmt here are provided for completeness,
# there is nothing wrong invoking cargo directly instead of make.
test:
cargo test $(CARGO_ARGS)

check:
cargo check $(CARGO_ARGS)

clippy:
cargo clippy $(CARGO_ARGS)

fmt:
cargo fmt $(CARGO_ARGS)

# Arbitrary cargo command is supported here. For example:
#
# make cargo CARGO_CMD=expand CARGO_ARGS="--ugly"
#
# Invokes:
# cargo expand --ugly
CARGO_CMD :=
cargo:
cargo $(CARGO_CMD) $(CARGO_ARGS)

clean:
cargo clean

prepare:
rustup target add riscv64imac-unknown-none-elf

.PHONY: build test check clippy fmt cargo clean prepare
3 changes: 3 additions & 0 deletions contracts/ccc-sol-lock/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
# ccc-sol-lock

CCC SOL lock implementation. See [specification](../../docs/sol.md) for more information.
50 changes: 50 additions & 0 deletions contracts/ccc-sol-lock/src/entry.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
use crate::error::Error;
use alloc::string::String;
use ckb_lock_helper::{blake2b::blake160, generate_sighash_all};
use ckb_std::{
ckb_constants::Source,
high_level::{load_script, load_witness_args},
};
use ed25519_dalek::{Signature, Verifier, VerifyingKey, PUBLIC_KEY_LENGTH};

fn message_wrap(msg: &str) -> String {
// Only 32-bytes hex representation of the hash is allowed.
assert_eq!(msg.len(), 64);
// Text used to signify that a signed message follows and to prevent inadvertently signing a transaction.
const CKB_PREFIX: &str = "Signing a CKB transaction: 0x";
const CKB_SUFFIX: &str = "\n\nIMPORTANT: Please verify the integrity and authenticity of connected Solana wallet before signing this message\n";
[CKB_PREFIX, msg, CKB_SUFFIX].join("")
}

pub fn entry() -> Result<(), Error> {
let script = load_script()?;
let pubkey_hash_expect = script.args().raw_data();
if pubkey_hash_expect.len() != 20 {
return Err(Error::WrongPubkey);
}
let sighash_all = generate_sighash_all()?;
let sighash_all_hex = hex::encode(&sighash_all);
let msg = message_wrap(&sighash_all_hex);
let witness_args = load_witness_args(0, Source::GroupInput)?;
let witness_args_lock = witness_args
.lock()
.to_opt()
.ok_or(Error::WrongSignatureFormat)?
.raw_data();
if witness_args_lock.len() != 96 {
return Err(Error::WrongSignatureFormat);
}
let sig =
Signature::from_slice(&witness_args_lock[..64]).map_err(|_| Error::WrongSignatureFormat)?;
let mut pubkey = [0u8; PUBLIC_KEY_LENGTH];
pubkey.copy_from_slice(&witness_args_lock[64..]);
let pubkey_hash_result = blake160(&pubkey);
if pubkey_hash_result.as_ref() != pubkey_hash_expect.as_ref() {
return Err(Error::WrongPubkey);
}
let pubkey = VerifyingKey::from_bytes(&pubkey).map_err(|_| Error::WrongPubkey)?;
pubkey
.verify(msg.as_bytes(), &sig)
.map_err(|_| Error::WrongSignature)?;
Ok(())
}
40 changes: 40 additions & 0 deletions contracts/ccc-sol-lock/src/error.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
use ckb_lock_helper::error::Error as HelperError;
use ckb_std::error::SysError;

#[repr(i8)]
pub enum Error {
IndexOutOfBound = 1,
ItemMissing,
LengthNotEnough,
Encoding,
Unknown = 30,
WrongWitnessArgs,
WrongPubkey,
WrongSignatureFormat,
WrongSignature,
}

impl From<HelperError> for Error {
fn from(value: HelperError) -> Self {
match value {
HelperError::IndexOutOfBound => Error::IndexOutOfBound,
HelperError::ItemMissing => Error::ItemMissing,
HelperError::LengthNotEnough => Error::LengthNotEnough,
HelperError::Encoding => Error::Encoding,
HelperError::Unknown => Error::Unknown,
HelperError::WrongWitnessArgs => Error::WrongWitnessArgs,
}
}
}

impl From<SysError> for Error {
fn from(err: SysError) -> Self {
match err {
SysError::IndexOutOfBound => Self::IndexOutOfBound,
SysError::ItemMissing => Self::ItemMissing,
SysError::LengthNotEnough(_) => Self::LengthNotEnough,
SysError::Encoding => Self::Encoding,
SysError::Unknown(_) => Self::Unknown,
}
}
}
22 changes: 22 additions & 0 deletions contracts/ccc-sol-lock/src/main.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
#![no_std]
#![no_main]

mod entry;
mod error;

use ckb_std::default_alloc;
ckb_std::entry!(program_entry);
default_alloc!(4 * 1024, 1400 * 1024, 64);

use entry::entry;

pub fn program_entry() -> i8 {
match entry() {
Ok(_) => 0,
Err(e) => {
let result = e as i8;
assert!(result != 0);
result
}
}
}
Loading

0 comments on commit f5da627

Please sign in to comment.