Skip to content

Commit

Permalink
Add first success test (ckb-devrel#3)
Browse files Browse the repository at this point in the history
* Add BTC tests
  • Loading branch information
mohanson authored Jul 18, 2024
1 parent 792f291 commit 762265b
Show file tree
Hide file tree
Showing 14 changed files with 1,422 additions and 341 deletions.
17 changes: 10 additions & 7 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,22 +9,25 @@ dedicated chains can be found in [docs](./docs/).
## Build

Build on native machine:

```sh
$ make build
```
make build
```

See [ckb-script-templates](https://github.com/cryptape/ckb-script-templates) for required setup.


Make a reproducible build:

```sh
$ bash scripts/reproducible_build_docker
```
bash scripts/reproducible_build_docker
```

The docker is required.


## Test

```sh
$ cd tests && cargo test
```
cd tests && cargo test
```

2 changes: 1 addition & 1 deletion checksums.txt
Original file line number Diff line number Diff line change
@@ -1 +1 @@
bef047973a601d7bab6bcbf638f4c92a41a471fa53d5bf44f7233d3340b30e01 build/release/ccc-btc-lock
e6c62bda407981adb924ccc629a23368c41c896d9cc339308b7f7df7f971b0b4 build/release/ccc-btc-lock
100 changes: 60 additions & 40 deletions contracts/ccc-btc-lock/src/entry.rs
Original file line number Diff line number Diff line change
@@ -1,65 +1,85 @@
use crate::error::Error;
use alloc::{string::String, vec::Vec};
use ckb_lock_helper::{
constant::{BTC_PREFIX, PREFIX, SUFFIX},
generate_sighash_all,
};
use alloc::vec::Vec;
use ckb_lock_helper::{generate_sighash_all, println_hex};
use ckb_std::{
ckb_constants::Source,
high_level::{load_script, load_witness_args},
};
use k256::ecdsa::{RecoveryId, Signature, VerifyingKey};
use ripemd::Ripemd160;
use sha2::{Digest, Sha256};
use ripemd::{Digest, Ripemd160};
use sha2::Sha256;

fn ripemd160_sha256(msg: &[u8]) -> [u8; 20] {
ripemd160(&sha256(msg))
}

fn ripemd160(message: &[u8]) -> [u8; 20] {
let mut hasher = Ripemd160::new();
hasher.update(message);
hasher.finalize().into()
}

fn sha256(msg: &[u8]) -> [u8; 32] {
let mut hasher = Sha256::new();
hasher.update(msg);
hasher.finalize().into()
}

fn sha256_sha256(msg: &[u8]) -> [u8; 32] {
sha256(&sha256(msg))
}

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 BTC_PREFIX: &str = "Bitcoin Signed Message:\n";
let mut data: Vec<u8> = Vec::new();
assert_eq!(BTC_PREFIX.len(), 24);
data.push(24);
data.extend(BTC_PREFIX.as_bytes());
data.push((CKB_PREFIX.len() + msg.len() + CKB_SUFFIX.len()) as u8);
data.extend(CKB_PREFIX.as_bytes());
data.extend(msg.as_bytes());
data.extend(CKB_SUFFIX.as_bytes());
sha256_sha256(&data)
}

pub fn entry() -> Result<(), Error> {
let script = load_script()?;
let pubkey_hash = script.args().raw_data();
if pubkey_hash.len() != 20 {
let pubkey_hash_expect = script.args().raw_data();
if pubkey_hash_expect.len() != 20 {
return Err(Error::WrongPubkeyHash);
}

let mut to_be_hashed: Vec<u8> = Default::default();
assert_eq!(BTC_PREFIX.len(), 24);
to_be_hashed.push(BTC_PREFIX.len() as u8);
to_be_hashed.extend(BTC_PREFIX.as_bytes());

let sighash_all = generate_sighash_all()?;
let sighash_all_hex = hex::encode(&sighash_all);
let message1 = String::from(PREFIX) + &sighash_all_hex + SUFFIX;

assert!(message1.len() < 256);
to_be_hashed.push(message1.len() as u8);
to_be_hashed.extend(message1.into_bytes());

// Double SHA-256 from bitcoin
let msg = Sha256::digest(&Sha256::digest(&to_be_hashed));

let digest_hash = message_hash(&sighash_all_hex);
let witness_args = load_witness_args(0, Source::GroupInput)?;
let sig = witness_args
let sig_raw = witness_args
.lock()
.to_opt()
.ok_or(Error::WrongSignatureFormat)?
.raw_data();

if sig.len() != 65 {
if sig_raw.len() != 65 {
return Err(Error::WrongSignatureFormat);
}
let rec_id = match sig[0] {
31 | 32 | 33 | 34 => sig[0] - 31,
39 | 40 | 41 | 42 => sig[0] - 39,
_ => sig[0],
let rec_id = match sig_raw[0] {
31 | 32 | 33 | 34 => sig_raw[0] - 31,
39 | 40 | 41 | 42 => sig_raw[0] - 39,
_ => sig_raw[0],
};
let rec_id = RecoveryId::try_from(rec_id).map_err(|_| Error::InvalidRecoverId)?;
let signature = Signature::from_slice(&sig[1..]).map_err(|_| Error::WrongSignatureFormat)?;
let recovered_key = VerifyingKey::recover_from_prehash(&msg, &signature, rec_id)
.map_err(|_| Error::CanNotRecover)?;
// TODO: double check its format
let recovered_key_bytes = recovered_key.to_sec1_bytes();
// RIPEMD160 over SHA-256 for pubkey hashing
let pubkey_hash_result: [u8; 20] =
Ripemd160::digest(&Sha256::digest(&recovered_key_bytes)).into();
if pubkey_hash_result.as_ref() != pubkey_hash.as_ref() {
let sig = Signature::from_slice(&sig_raw[1..]).map_err(|_| Error::WrongSignatureFormat)?;
let pubkey_result = VerifyingKey::recover_from_prehash(&digest_hash, &sig, rec_id)
.map_err(|_| Error::CanNotRecover)?
.to_sec1_bytes();
assert!(pubkey_result.len() == 33);
let pubkey_hash_result = ripemd160_sha256(&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(())
Expand Down
2 changes: 1 addition & 1 deletion crates/ckb-lock-helper/src/blake2b.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ pub fn blake160(data: &[u8]) -> [u8; 20] {
blake2b.update(data);
blake2b.finalize(&mut hash);
let mut ret = [0u8; 20];
(&mut ret).copy_from_slice(&hash[0..20]);
ret.copy_from_slice(&hash[0..20]);
ret
}

Expand Down
3 changes: 0 additions & 3 deletions crates/ckb-lock-helper/src/constant.rs

This file was deleted.

14 changes: 9 additions & 5 deletions crates/ckb-lock-helper/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@
#![no_main]
extern crate alloc;
pub mod blake2b;
pub mod constant;
pub mod error;

use crate::blake2b::new_blake2b_stat;
Expand All @@ -15,6 +14,10 @@ use ckb_std::debug;
use ckb_std::high_level::{load_tx_hash, load_witness, load_witness_args};
use ckb_std::syscalls::{load_input_by_field, SysError};

pub fn println_hex(name: &str, data: &[u8]) {
debug!("{}(len={}): {}", name, data.len(), hex::encode(data));
}

pub fn generate_sighash_all() -> Result<[u8; 32], Error> {
let mut blake2b_ctx = new_blake2b_stat();
let tx_hash = load_tx_hash()?;
Expand Down Expand Up @@ -57,10 +60,11 @@ pub fn generate_sighash_all() -> Result<[u8; 32], Error> {
}
}
}
let mut msg = [0u8; 32];
debug!("Hashed {} bytes in sighash_all", blake2b_ctx.count());
blake2b_ctx.finalize(&mut msg);
Ok(msg)
let mut sighash_all = [0u8; 32];
debug!("hashed {} bytes in sighash_all", blake2b_ctx.count());
blake2b_ctx.finalize(&mut sighash_all);
println_hex("sighash_all", &sighash_all);
Ok(sighash_all)
}

fn calculate_inputs_len() -> Result<usize, Error> {
Expand Down
Loading

0 comments on commit 762265b

Please sign in to comment.