forked from ckb-devrel/ccc-locks
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
15 changed files
with
534 additions
and
99 deletions.
There are no files selected for viewing
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,2 @@ | ||
/build | ||
/target |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,11 @@ | ||
[package] | ||
name = "ccc-eth-lock" | ||
version = "0.1.0" | ||
edition = "2021" | ||
|
||
[dependencies] | ||
ckb-std = "0.15" | ||
ckb-lock-helper = { path = "../../crates/ckb-lock-helper" } | ||
k256 = { version = "=0.13.1", default-features = false, features = ["arithmetic", "ecdsa", "alloc"] } | ||
hex = { version = "0.4", default-features = false, features = ["alloc"] } | ||
tiny-keccak = { version = "2.0.2", features = ["keccak"] } |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
# ccc-btc-lock | ||
|
||
CCC ETH lock implementation. See [specification](../../docs/eth.md) for more information. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,90 @@ | ||
use crate::error::Error; | ||
use alloc::format; | ||
use alloc::vec::Vec; | ||
use ckb_lock_helper::{generate_sighash_all, println_hex, secp256k1_patch::recover_from_prehash}; | ||
use ckb_std::{ | ||
ckb_constants::Source, | ||
high_level::{load_script, load_witness_args}, | ||
}; | ||
use k256::ecdsa::{RecoveryId, Signature}; | ||
use tiny_keccak::Hasher; | ||
|
||
fn keccak(msg: &[u8]) -> [u8; 32] { | ||
let mut output = [0u8; 32]; | ||
let mut hasher = tiny_keccak::Keccak::v256(); | ||
hasher.update(msg); | ||
hasher.finalize(&mut output); | ||
output | ||
} | ||
|
||
fn keccak160(msg: &[u8]) -> [u8; 20] { | ||
let mut output = [0u8; 20]; | ||
output.copy_from_slice(&keccak(msg)[12..]); | ||
output | ||
} | ||
|
||
fn message_hash(msg: &str) -> [u8; 32] { | ||
// 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 BTC wallet before signing this message\n"; | ||
const ETH_PREFIX: &str = "Ethereum Signed Message:\n"; | ||
let mut data: Vec<u8> = Vec::new(); | ||
assert_eq!(ETH_PREFIX.len(), 25); | ||
data.push(25); | ||
data.extend(ETH_PREFIX.as_bytes()); | ||
data.extend( | ||
format!( | ||
"{}", | ||
(CKB_PREFIX.len() + msg.len() + CKB_SUFFIX.len()) as u8 | ||
) | ||
.as_bytes(), | ||
); | ||
data.extend(CKB_PREFIX.as_bytes()); | ||
data.extend(msg.as_bytes()); | ||
data.extend(CKB_SUFFIX.as_bytes()); | ||
keccak(&data) | ||
} | ||
|
||
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::WrongPubkeyHash); | ||
} | ||
let sighash_all = generate_sighash_all()?; | ||
let sighash_all_hex = hex::encode(&sighash_all); | ||
let digest_hash = message_hash(&sighash_all_hex); | ||
let witness_args = load_witness_args(0, Source::GroupInput)?; | ||
let sig_raw = witness_args | ||
.lock() | ||
.to_opt() | ||
.ok_or(Error::WrongSignatureFormat)? | ||
.raw_data(); | ||
if sig_raw.len() != 65 { | ||
return Err(Error::WrongSignatureFormat); | ||
} | ||
let rec_id = sig_raw[64].wrapping_sub(27); | ||
if rec_id >= 2 { | ||
return Err(Error::InvalidRecoverId); | ||
} | ||
let rec_id = RecoveryId::try_from(rec_id).map_err(|_| Error::InvalidRecoverId)?; | ||
let sig = Signature::from_slice(&sig_raw[..64]).map_err(|_| Error::WrongSignatureFormat)?; | ||
if sig.normalize_s().is_some() { | ||
return Err(Error::SignatureIsNotLowS); | ||
} | ||
let pubkey_result = &recover_from_prehash(&digest_hash, &sig, rec_id) | ||
.map_err(|_| Error::CanNotRecover)? | ||
.to_encoded_point(false) | ||
.to_bytes()[1..]; | ||
println_hex("pubkey_result", &pubkey_result); | ||
assert!(pubkey_result.len() == 64); | ||
let pubkey_hash_result = keccak160(&pubkey_result); | ||
println_hex("pubkey_hash_result", pubkey_hash_result.as_ref()); | ||
println_hex("pubkey_hash_expect", pubkey_hash_expect.as_ref()); | ||
if pubkey_hash_result.as_ref() != pubkey_hash_expect.as_ref() { | ||
return Err(Error::PubkeyHashMismatched); | ||
} | ||
Ok(()) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,43 @@ | ||
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, | ||
WrongPubkeyHash, | ||
PubkeyHashMismatched, | ||
WrongSignatureFormat, | ||
InvalidRecoverId, | ||
CanNotRecover, | ||
SignatureIsNotLowS, | ||
} | ||
|
||
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, | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 | ||
} | ||
} | ||
} |
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.