Skip to content

Commit 09cbcb4

Browse files
authored
add CI (#9)
* add CI * fix rust setup * lets use stable rust * cargo fmt * clippy * add deny.toml * remove some steps * remove docs.yml * remove release.yml * update security ci check
1 parent 964b408 commit 09cbcb4

File tree

15 files changed

+267
-80
lines changed

15 files changed

+267
-80
lines changed

.github/workflows/ci.yml

Lines changed: 101 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,101 @@
1+
name: CI
2+
3+
on:
4+
push:
5+
branches: [ main, develop ]
6+
paths:
7+
- 'lit-rust-sdk/**'
8+
- '.github/workflows/**'
9+
pull_request:
10+
branches: [ main, develop ]
11+
paths:
12+
- 'lit-rust-sdk/**'
13+
- '.github/workflows/**'
14+
15+
env:
16+
CARGO_TERM_COLOR: always
17+
RUST_BACKTRACE: 1
18+
19+
jobs:
20+
test:
21+
name: Test Suite
22+
runs-on: ubuntu-latest
23+
timeout-minutes: 45
24+
25+
steps:
26+
- name: Checkout code
27+
uses: actions/checkout@v4
28+
29+
- name: Install Rust
30+
uses: dtolnay/rust-toolchain@stable
31+
with:
32+
components: rustfmt, clippy
33+
34+
- name: Cache cargo registry
35+
uses: actions/cache@v4
36+
with:
37+
path: |
38+
~/.cargo/registry
39+
~/.cargo/git
40+
lit-rust-sdk/target
41+
key: ${{ runner.os }}-cargo-${{ hashFiles('**/Cargo.lock') }}
42+
restore-keys: |
43+
${{ runner.os }}-cargo-
44+
45+
- name: Check formatting
46+
run: |
47+
cd lit-rust-sdk
48+
cargo fmt --all -- --check
49+
50+
- name: Run clippy
51+
run: |
52+
cd lit-rust-sdk
53+
cargo clippy --all-targets --all-features -- -D warnings
54+
55+
- name: Run unit tests
56+
run: |
57+
cd lit-rust-sdk
58+
cargo test --lib
59+
60+
- name: Run all integration tests
61+
env:
62+
ETHEREUM_PRIVATE_KEY: ${{ secrets.ETHEREUM_PRIVATE_KEY }}
63+
PKP_PUBLIC_KEY: ${{ secrets.PKP_PUBLIC_KEY }}
64+
PKP_TOKEN_ID: ${{ secrets.PKP_TOKEN_ID }}
65+
PKP_ETH_ADDRESS: ${{ secrets.PKP_ETH_ADDRESS }}
66+
ETHEREUM_RPC_URL: ${{ secrets.ETHEREUM_RPC_URL }}
67+
run: |
68+
cd lit-rust-sdk
69+
# Run all tests with single thread to avoid conflicts
70+
cargo test -- --nocapture --test-threads=1
71+
72+
security:
73+
name: Security Audit
74+
runs-on: ubuntu-latest
75+
76+
steps:
77+
- name: Checkout code
78+
uses: actions/checkout@v4
79+
80+
- name: Install Rust
81+
uses: dtolnay/rust-toolchain@stable
82+
83+
- name: Install cargo-audit
84+
run: cargo install cargo-audit
85+
86+
- name: Run security audit
87+
run: |
88+
cd lit-rust-sdk
89+
cargo audit
90+
91+
- name: Check for cargo-deny
92+
run: |
93+
cd lit-rust-sdk
94+
# Install cargo-deny if not present
95+
cargo install --locked cargo-deny || true
96+
# Run cargo-deny if deny.toml exists (skip license checking)
97+
if [ -f "deny.toml" ]; then
98+
cargo deny check advisories bans sources
99+
else
100+
echo "No deny.toml found, skipping cargo-deny check"
101+
fi

README.md

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,8 @@
11
# Lit Protocol Rust SDK
22

3+
[![CI](https://github.com/LIT-Protocol/rust-sdk/actions/workflows/ci.yml/badge.svg)](https://github.com/LIT-Protocol/rust-sdk/actions/workflows/ci.yml)
4+
[![Documentation](https://github.com/LIT-Protocol/rust-sdk/actions/workflows/docs.yml/badge.svg)](https://github.com/LIT-Protocol/rust-sdk/actions/workflows/docs.yml)
5+
36
A native Rust implementation of the Lit Protocol SDK, providing programmatic access to the Lit Network for distributed key management, conditional access control, and programmable signing.
47

58
Currently in Beta and only supports Datil, DatilDev, and DatilTest networks.
@@ -560,6 +563,30 @@ Common issues and solutions:
560563
- **"Invalid signature"**: Verify PKP public key format (should include 0x prefix)
561564
- **"Rate limit exceeded"**: Ensure Rate Limit NFT has sufficient capacity
562565

566+
## CI/Development
567+
568+
The repository includes comprehensive GitHub Actions workflows for testing and validation:
569+
570+
### CI Pipeline
571+
572+
- **Basic CI** (`ci.yml`): Runs on every push/PR with formatting, clippy, unit tests, and all integration tests
573+
- **Documentation** (`docs.yml`): Validates README files and builds documentation
574+
- **Release Tests** (`release.yml`): Full test suite that can be run manually for release testing
575+
576+
### Required GitHub Secrets
577+
578+
For CI to work properly, the following secrets must be configured in the repository:
579+
580+
```bash
581+
ETHEREUM_PRIVATE_KEY # Private key for test wallet (should have test ETH)
582+
PKP_PUBLIC_KEY # Existing PKP public key for tests (optional)
583+
PKP_TOKEN_ID # Existing PKP token ID for tests (optional)
584+
PKP_ETH_ADDRESS # Existing PKP Ethereum address for tests (optional)
585+
ETHEREUM_RPC_URL # RPC URL for Ethereum/L2 network interactions
586+
```
587+
588+
**Note**: The CI will work with just `ETHEREUM_PRIVATE_KEY` and `ETHEREUM_RPC_URL` for basic tests. PKP-related secrets are only needed for advanced tests.
589+
563590
## Contributing
564591

565592
Contributions are welcome! Please ensure all tests pass before submitting a PR:
@@ -570,6 +597,19 @@ cargo fmt
570597
cargo clippy
571598
```
572599

600+
### Running Tests Locally
601+
602+
```bash
603+
# Run all tests (requires environment variables)
604+
cargo test -- --nocapture
605+
606+
# Run only local session signature tests (simpler setup)
607+
cargo test local_session_sigs -- --nocapture
608+
609+
# Run specific test
610+
cargo test test_connect_to_lit_network -- --nocapture
611+
```
612+
573613
## License
574614

575615
See LICENSE file in the repository root.

lit-rust-sdk/deny.toml

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
# Configuration for cargo-deny - security-focused with permissive license checking
2+
3+
[advisories]
4+
# Check for critical security vulnerabilities only
5+
version = 2
6+
yanked = "allow" # Many transitive deps in web3 are yanked but safe
7+
unmaintained = "all" # Allow all unmaintained crates
8+
ignore = [
9+
# Allow these specific advisories for now
10+
"RUSTSEC-2021-0141", # dotenv unmaintained but safe
11+
"RUSTSEC-2024-0370", # proc-macro-error unmaintained but safe
12+
"RUSTSEC-2024-0436", # paste unmaintained but safe
13+
]
14+
15+
# Skip license checking entirely - focus only on security
16+
# [licenses]
17+
18+
[bans]
19+
# Warn about multiple versions but don't fail
20+
multiple-versions = "warn"
21+
wildcards = "allow"

lit-rust-sdk/rust-toolchain.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,2 @@
11
[toolchain]
2-
channel = "1.86.0"
2+
channel = "stable"

lit-rust-sdk/src/auth.rs

Lines changed: 24 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ impl EthWalletProvider {
2222
let address = wallet.address();
2323

2424
// Create nonce
25-
let nonce = format!("0x{}", hex::encode(&rand::random::<[u8; 32]>()));
25+
let nonce = format!("0x{}", hex::encode(rand::random::<[u8; 32]>()));
2626

2727
// Create SIWE message for authentication
2828
let issued_at = chrono::Utc::now().to_rfc3339();
@@ -41,7 +41,7 @@ impl EthWalletProvider {
4141
info!("Parsed message: {:?}", parsed_message);
4242

4343
// Sign the SIWE message
44-
let signature = wallet.sign_message(&siwe_message.as_bytes()).await?;
44+
let signature = wallet.sign_message(siwe_message.as_bytes()).await?;
4545

4646
// Convert signature to hex string
4747
let sig_hex = format!("0x{}", hex::encode(signature.as_bytes()));
@@ -68,15 +68,18 @@ impl EthWalletProvider {
6868
delegatee_addresses: &[String],
6969
uses: &str,
7070
) -> Result<AuthSig> {
71+
use serde_json::Value;
7172
use siwe_recap::Capability;
7273
use std::collections::BTreeMap;
73-
use serde_json::Value;
74-
74+
7575
let address = wallet.address();
7676

7777
// Create the nota bene data for the capability
7878
let mut notabene = BTreeMap::new();
79-
notabene.insert("nft_id".to_string(), Value::from(vec![Value::from(capacity_token_id)]));
79+
notabene.insert(
80+
"nft_id".to_string(),
81+
Value::from(vec![Value::from(capacity_token_id)]),
82+
);
8083
notabene.insert("uses".to_string(), Value::from(uses.to_string()));
8184
notabene.insert(
8285
"delegate_to".to_string(),
@@ -85,18 +88,18 @@ impl EthWalletProvider {
8588
.iter()
8689
.map(|addr| {
8790
// Remove 0x prefix if present for the delegate_to field
88-
Value::from(if addr.starts_with("0x") {
89-
addr[2..].to_string()
91+
Value::from(if let Some(stripped) = addr.strip_prefix("0x") {
92+
stripped.to_string()
9093
} else {
9194
addr.to_string()
9295
})
9396
})
94-
.collect::<Vec<_>>()
97+
.collect::<Vec<_>>(),
9598
),
9699
);
97100

98101
// Create nonce - use a random hex string
99-
let nonce = format!("{}", hex::encode(&rand::random::<[u8; 16]>()));
102+
let nonce = hex::encode(rand::random::<[u8; 16]>());
100103

101104
// Create SIWE message for capacity delegation
102105
let issued_at = chrono::Utc::now();
@@ -106,7 +109,7 @@ impl EthWalletProvider {
106109
let mut capabilities = Capability::<Value>::default();
107110
let resource = "Auth/Auth".to_string();
108111
let resource_prefix = format!("lit-ratelimitincrease://{}", capacity_token_id);
109-
112+
110113
let capabilities = capabilities
111114
.with_actions_convert(resource_prefix, [(resource, [notabene])])
112115
.map_err(|e| eyre::eyre!("Failed to create capability: {}", e))?;
@@ -121,8 +124,16 @@ impl EthWalletProvider {
121124
version: "1".parse().unwrap(),
122125
chain_id: 1,
123126
nonce: nonce.clone(),
124-
issued_at: issued_at.to_rfc3339_opts(chrono::SecondsFormat::Millis, true).parse().unwrap(),
125-
expiration_time: Some(expiration.to_rfc3339_opts(chrono::SecondsFormat::Millis, true).parse().unwrap()),
127+
issued_at: issued_at
128+
.to_rfc3339_opts(chrono::SecondsFormat::Millis, true)
129+
.parse()
130+
.unwrap(),
131+
expiration_time: Some(
132+
expiration
133+
.to_rfc3339_opts(chrono::SecondsFormat::Millis, true)
134+
.parse()
135+
.unwrap(),
136+
),
126137
not_before: None,
127138
request_id: None,
128139
resources: vec![],
@@ -133,7 +144,7 @@ impl EthWalletProvider {
133144
let message_str = siwe_message.to_string();
134145

135146
// Sign the SIWE message
136-
let signature = wallet.sign_message(&message_str.as_bytes()).await?;
147+
let signature = wallet.sign_message(message_str.as_bytes()).await?;
137148

138149
let sig_hex = format!("0x{}", hex::encode(signature.as_bytes()));
139150

lit-rust-sdk/src/blockchain/staking.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
#![allow(clippy::too_many_arguments)]
2+
13
use alloy::sol;
24

35
sol!(// `all_derives` - derives standard Rust traits.

lit-rust-sdk/src/bls.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ where
1414
T: core::borrow::Borrow<JsonSignSessionKeyResponseV1>,
1515
{
1616
let shares = signature_shares
17-
.map(|s| s.borrow().signature_share.clone())
17+
.map(|s| s.borrow().signature_share)
1818
.collect::<Vec<_>>();
1919
let sig = Signature::from_shares(&shares)?;
2020
Ok(sig)

lit-rust-sdk/src/client/connect.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,7 @@ impl<P: alloy::providers::Provider> super::LitNodeClient<P> {
5656
async fn handshake_with_nodes(&mut self, urls: &[String]) -> Result<()> {
5757
let mut successful_connections = 0;
5858
for url in urls {
59-
match self.handshake_with_node(&url).await {
59+
match self.handshake_with_node(url).await {
6060
Ok(response) => {
6161
info!("Successfully connected to node: {}", url);
6262
self.connection_state.insert(

lit-rust-sdk/src/client/execute.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -186,7 +186,7 @@ impl<P: alloy::providers::Provider> super::LitNodeClient<P> {
186186
if !response.success {
187187
continue;
188188
}
189-
for (_key, signed_data) in &response.signed_data {
189+
for signed_data in response.signed_data.values() {
190190
let sig_name = signed_data.sig_name.clone();
191191
signatures_by_name
192192
.entry(sig_name)

lit-rust-sdk/src/client/mod.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ impl LitNodeClient<DynProvider> {
3737
let http_client = Client::builder().timeout(config.connect_timeout).build()?;
3838

3939
let rpc_url = config.lit_network.rpc_url();
40-
let provider = ProviderBuilder::new().connect(&rpc_url).await?;
40+
let provider = ProviderBuilder::new().connect(rpc_url).await?;
4141
let staking_address = config.lit_network.staking_contract_address()?;
4242

4343
let staking = Staking::new(staking_address, provider.erased());

0 commit comments

Comments
 (0)