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

feat: 🎸 Support sphincs plus lock script #60

Open
wants to merge 4 commits into
base: master
Choose a base branch
from
Open
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
6 changes: 6 additions & 0 deletions .github/workflows/ci.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,8 @@ jobs:
os: [ ubuntu-latest, macos-latest, windows-2019 ]
steps:
- uses: actions/checkout@v2
with:
submodules: recursive
- if: matrix.os == 'windows-2019'
name: Windows Dependencies
run: |
Expand All @@ -39,6 +41,8 @@ jobs:
os: [ubuntu-latest, macos-latest]
steps:
- uses: actions/checkout@v2
with:
submodules: recursive
- name: Linters
run: |
cargo fmt --version || rustup component add rustfmt
Expand All @@ -50,6 +54,8 @@ jobs:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
with:
submodules: recursive
- name: Security Audit & Licenses
run: |
rustup toolchain install nightly --allow-downgrade --profile minimal
Expand Down
4 changes: 4 additions & 0 deletions .gitmodules
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
[submodule "deps/quantum-resistant-lock-script"]
path = deps/quantum-resistant-lock-script
url = git@github.com:cryptape/quantum-resistant-lock-script.git
branch = main
5 changes: 4 additions & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -38,9 +38,9 @@ ckb-script = { version = "=0.108.0"}
bitflags = "1.3.2"
sha3 = "0.10.1"
enum-repr-derive = "0.2.0"
rand = "0.8"

# for feature test
rand = { version = "0.7.3", optional = true }
ckb-mock-tx-types = { version = "0.108.0", optional = true }
ckb-chain-spec = { version = "0.108.0", optional = true }

Expand All @@ -56,3 +56,6 @@ clap = { version = "4.1.8", features = ["derive"] }
httpmock = "0.6"
async-global-executor = "2.3.1"
hex = "0.4"

[build-dependencies]
cc = "1.0"
75 changes: 75 additions & 0 deletions build.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
use std::path::PathBuf;

fn get_hash_size() -> usize {
128
}

fn get_hash_option() -> &'static str {
"f"
}

fn get_hash_info() -> (&'static str, Vec<&'static str>) {
let thash_file = "../deps/sphincsplus/ref/thash_shake_simple.c";

(
"shake",
vec![
"../deps/sphincsplus/ref/fips202.c",
"../deps/sphincsplus/ref/hash_shake.c",
thash_file,
],
)
}

fn get_os_specic_src_files() -> Vec<&'static str> {
let mut files = vec![];

if !cfg!(windows) {
files.push("../deps/sphincsplus/ref/randombytes.c");
}

files
}

fn main() {
let mut source_list = vec![
"../deps/sphincsplus/ref/address.c",
"../deps/sphincsplus/ref/merkle.c",
"../deps/sphincsplus/ref/wots.c",
"../deps/sphincsplus/ref/wotsx1.c",
"../deps/sphincsplus/ref/utils.c",
"../deps/sphincsplus/ref/utilsx1.c",
"../deps/sphincsplus/ref/fors.c",
"../deps/sphincsplus/ref/sign.c",
"ckb-sphincsplus.c",
];

let (hash_name, mut hash_src_files) = get_hash_info();

source_list.append(&mut hash_src_files);
source_list.append(&mut get_os_specic_src_files());
let define_param = format!(
"sphincs-{}-{}{}",
hash_name,
get_hash_size(),
get_hash_option()
);

let c_src_dir = PathBuf::from("deps/quantum-resistant-lock-script/c/");

let mut builder = cc::Build::new();
builder.define("PARAMS", define_param.as_str());
builder.include(&c_src_dir);
builder.include(
&c_src_dir
.join("..")
.join("deps")
.join("sphincsplus")
.join("ref"),
);

for source in source_list {
builder.file(c_src_dir.join(source));
}
builder.compile("sphincsplus");
}
10 changes: 5 additions & 5 deletions check-cargotoml.sh
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ esac

function check_package_name() {
local regex_to_cut_pkgname='s/^\[\(package\)\]\nname\(\|[ ]\+\)=\(\|[ ]\+\)"\(.\+\)"/\4/p'
for cargo_toml in $(find "${SRC_ROOT}" -type f -name "Cargo.toml" -not -path '*/target/*'); do
for cargo_toml in $(find "${SRC_ROOT}" -type f -name "Cargo.toml" -not -path '*/target/*' -not -path './deps/*'); do
local pkgname=$(${SED} -n -e N -e "${regex_to_cut_pkgname}" "${cargo_toml}")
if [ -z "${pkgname}" ]; then
printf "Error: No package name in <%s>\n" "${cargo_toml}"
Expand All @@ -43,7 +43,7 @@ function check_package_name() {
function check_version() {
local regex_to_cut_version='s/^version = "\(.*\)"$/\1/p'
local expected=$(${SED} -n "${regex_to_cut_version}" "${SRC_ROOT}/Cargo.toml")
for cargo_toml in $(find "${SRC_ROOT}" -type f -name "Cargo.toml" -not -path '*/target/*'); do
for cargo_toml in $(find "${SRC_ROOT}" -type f -name "Cargo.toml" -not -path '*/target/*' -not -path './deps/*'); do
local tmp=$(${SED} -n "${regex_to_cut_version}" "${cargo_toml}")
if [ "${expected}" != "${tmp}" ]; then
printf "Error: Version in <%s> is not right (expect: '%s', actual: '%s')\n" \
Expand All @@ -62,7 +62,7 @@ function check_version() {
function check_license() {
local regex_to_cut_license='s/^license = "\(.*\)"$/\1/p'
local expected=$(${SED} -n "${regex_to_cut_license}" "${SRC_ROOT}/Cargo.toml")
for cargo_toml in $(find "${SRC_ROOT}" -type f -name "Cargo.toml" -not -path '*/target/*'); do
for cargo_toml in $(find "${SRC_ROOT}" -type f -name "Cargo.toml" -not -path '*/target/*' -not -path './deps/*'); do
local tmp=$(${SED} -n "${regex_to_cut_license}" "${cargo_toml}")
if [ "${expected}" != "${tmp}" ]; then
printf "Error: License in <%s> is not right (expect: '%s', actual: '%s')\n" \
Expand All @@ -73,7 +73,7 @@ function check_license() {
}

function check_cargo_publish() {
for cargo_toml in $(find "${SRC_ROOT}" -type f -name "Cargo.toml" -not -path '*/target/*'); do
for cargo_toml in $(find "${SRC_ROOT}" -type f -name "Cargo.toml" -not -path '*/target/*' -not -path './deps/*'); do
if ! grep -q '^description =' "${cargo_toml}"; then
echo "Error: Require description in <${cargo_toml}>"
ERRCNT=$((ERRCNT + 1))
Expand Down Expand Up @@ -115,7 +115,7 @@ function search_crate() {

function check_dependencies_for() {
local deptype="$1"
for cargo_toml in $(find "${SRC_ROOT}" -type f -name "Cargo.toml" -not -path '*/target/*'); do
for cargo_toml in $(find "${SRC_ROOT}" -type f -name "Cargo.toml" -not -path '*/target/*' -not -path './deps/*'); do
local pkgroot=$(dirname "${cargo_toml}")
for dependency_original in $(${SED} -n "/^\[${deptype}\]/,/^\[/p" "${cargo_toml}" \
| { ${GREP} -v "^\(\[\|[ ]*$\|[ ]*#\)" || true; } \
Expand Down
1 change: 1 addition & 0 deletions deps/quantum-resistant-lock-script
127 changes: 127 additions & 0 deletions examples/unlock_sphincsplucs.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,127 @@
use bytes::Bytes;
use ckb_sdk::{
constants::ONE_CKB,
traits::{
DefaultCellCollector, DefaultCellDepResolver, DefaultHeaderDepResolver,
DefaultTransactionDependencyProvider,
},
tx_builder::{
sphincsplus::SphincsPlusEnv, transfer::CapacityTransferBuilder, CapacityBalancer, TxBuilder,
},
unlock::{sphincsplus::SphincsPlusPrivateKey, SphincsPlus},
Address, CkbRpcClient, NetworkType,
};
use ckb_types::{
core::{BlockView, DepType, ScriptHashType, TransactionView},
h256,
packed::{CellOutput, Script},
prelude::*,
};

use std::{convert::TryFrom, error::Error as StdErr, str::FromStr};

pub const SK: [u8; 64] = [
244, 229, 172, 97, 118, 43, 186, 182, 5, 191, 38, 224, 223, 57, 251, 84, // sk.seed
29, 7, 44, 250, 108, 236, 220, 216, 161, 162, 99, 146, 46, 4, 34, 125, // sk.prf
152, 145, 159, 50, 118, 81, 12, 134, 27, 52, 214, 210, 91, 84, 65, 42, // pubkey seed
252, 12, 85, 58, 222, 186, 58, 189, 25, 133, 144, 79, 103, 177, 27, 76, // pubkey root
];

fn build_transfer_tx(
env: &SphincsPlusEnv,
sender: Script,
sender_key: SphincsPlusPrivateKey,
) -> Result<TransactionView, Box<dyn StdErr>> {
// Build ScriptUnlocker
let unlockers = env.build_unlockers(vec![sender_key]);

// Build CapacityBalancer
let placeholder_witness = SphincsPlus::placeholder_witness();
let mut balancer = CapacityBalancer::new_simple(sender, placeholder_witness, 1000);
balancer.force_small_change_as_fee = Some(ONE_CKB);

// Build:
// * CellDepResolver
// * HeaderDepResolver
// * CellCollector
// * TransactionDependencyProvider
let ckb_rpc = "https://testnet.ckb.dev";
let mut ckb_client = CkbRpcClient::new(ckb_rpc);
let cell_dep_resolver = {
let genesis_block = ckb_client.get_block_by_number(0.into())?.unwrap();
let mut cell_dep_resolver =
DefaultCellDepResolver::from_genesis(&BlockView::from(genesis_block))?;
env.add_cell_dep(&mut cell_dep_resolver);
cell_dep_resolver
};
let header_dep_resolver = DefaultHeaderDepResolver::new(ckb_rpc);
let mut cell_collector = DefaultCellCollector::new(ckb_rpc);
let tx_dep_provider = DefaultTransactionDependencyProvider::new(ckb_rpc, 10);

let receiver = Address::from_str("ckt1qzda0cr08m85hc8jlnfp3zer7xulejywt49kt2rr0vthywaa50xwsqv5dsed9par23x4g58seaw58j3ym5ml2hs8ztche").unwrap();
// Build the transaction
let output = CellOutput::new_builder()
.lock(Script::from(&receiver))
.capacity(99_9990_0000u64.pack())
.build();
let builder = CapacityTransferBuilder::new(vec![(output, Bytes::default())]);
let (tx, still_locked_groups) = builder.build_unlocked(
&mut cell_collector,
&cell_dep_resolver,
&header_dep_resolver,
&tx_dep_provider,
&balancer,
&unlockers,
)?;
assert!(still_locked_groups.is_empty());
Ok(tx)
}

fn build_env() -> SphincsPlusEnv {
SphincsPlusEnv {
tx_hash: h256!("0x35f51257673c7a7edd009fa2166e6f8645156207c9da38202f04ba4d94d9e519"),
tx_idx: 0,
dep_type: DepType::Code,
code_hash: h256!("0x989ab456455509a1c2ad1cb8116b7d209df228144445c741b101ec3e55ee8351"),
hash_type: ScriptHashType::Data1,
network_type: NetworkType::Testnet,
}
}

/*
1. build address,
ckt1qzvf4dzkg42sngwz45wtsytt05sfmu3gz3zyt36pkyq7c0j4a6p4zqkur4fuxjyh4fzphavynhdgptuwqsyhdjns028ugqy5jgnesdy8wslj3elk
2. transfer to address:
wallet transfer --from-account 0x946c32d287a3544d5450f0cf5d43ca24dd37f55e \
--to-address ckt1qzvf4dzkg42sngwz45wtsytt05sfmu3gz3zyt36pkyq7c0j4a6p4zqkur4fuxjyh4fzphavynhdgptuwqsyhdjns028ugqy5jgnesdy8wslj3elk \
--capacity 100 --skip-check-to-address
0x7fb5afa7c0bdc9cbbc2b65b523581c2bb3ed43ced114f759651ae407dee3d0c9
3. unlock the cell, and transfer the capacity to address ckt1qzda0cr08m85hc8jlnfp3zer7xulejywt49kt2rr0vthywaa50xwsqv5dsed9par23x4g58seaw58j3ym5ml2hs8ztche
*/
fn main() -> Result<(), Box<dyn StdErr>> {
let sk = SphincsPlusPrivateKey::try_from(SK.to_vec()).unwrap();
let pk = sk.pub_key();
let env = build_env();
let address = env.build_address(&pk);
let resp = serde_json::json!({
"address": address.to_string(),
});
println!("{}", serde_json::to_string_pretty(&resp).unwrap());
let sender = env.script(&pk);

let tx = build_transfer_tx(&env, sender, sk)?;

// Send transaction
let json_tx = ckb_jsonrpc_types::TransactionView::from(tx);
println!("tx: {}", serde_json::to_string_pretty(&json_tx).unwrap());
let outputs_validator = Some(ckb_jsonrpc_types::OutputsValidator::Passthrough);

let ckb_rpc = "https://testnet.ckb.dev";
let _tx_hash = CkbRpcClient::new(ckb_rpc)
.send_transaction(json_tx.inner, outputs_validator)
.expect("send transaction");
// example tx_hash: 0xf83fd6c2fe511a9c39795624b7e0be2157e1543d9f1b1a1cbb676896e31c2b1b
println!(">>> tx sent! <<<");

Ok(())
}
1 change: 1 addition & 0 deletions src/tests/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1090,3 +1090,4 @@ pub mod ckb_rpc;
pub mod cycle;
pub mod omni_lock;
pub mod omni_lock_util;
pub mod sphincsplus;
8 changes: 8 additions & 0 deletions src/tests/sphincsplus.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
use crate::unlock::SphincsPlusPrivateKey;

#[test]
fn test_sphincplus_sk() {
let sk = SphincsPlusPrivateKey::new();
assert!(sk.is_ok());
assert!(sk.unwrap().is_valid().is_ok());
}
1 change: 1 addition & 0 deletions src/tx_builder/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ pub mod acp;
pub mod cheque;
pub mod dao;
pub mod omni_lock;
pub mod sphincsplus;
pub mod transfer;
pub mod udt;

Expand Down
Loading