Skip to content
Merged
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: 3 additions & 3 deletions Cargo.lock

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

4 changes: 4 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,11 @@ This SDK covers the full action lifecycle used by apps and services:

1. **Chain client (`chain`)**
- action params + fee lookup
- account info lookup
- action registration transaction flow
- generic tx build/sign/broadcast utilities
- tx gas simulation helper + adjusted gas signing flow
- tx confirmation lookup/wait helpers

2. **sn-api client (`snapi`)**
- start upload
Expand Down
47 changes: 47 additions & 0 deletions docs/chain-feasibility-gap-analysis.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
# sdk-rs chain expansion feasibility (vs sdk-go)

## Scope requested
1) Safe/unified key handling for tx signing + message signing.
2) Expand beyond Cascade into stronger chain interactions (account info, fees, tx sending, tx confirmation).

## Feasibility verdict
**Feasible now with current Rust stack** (`cosmrs`, `tendermint-rpc`, `reqwest`).
No blocker found for parity on the requested baseline.

## What exists in sdk-rs today
- Action params and action-fee query (REST).
- Register-action tx flow with signing and sequence retry.
- sn-api upload/download orchestration.

## Gaps relative to sdk-go baseline
- No dedicated identity/key module (key derivation duplicated in examples).
- No explicit signer/address mismatch guard in tx path.
- Fee handling in tx path was static/fixed (not parsed from configured gas price).
- No generic tx lookup + wait-for-confirmation utility.
- No explicit account-info public API.

## What was implemented in this step
- Added `src/keys.rs`:
- `SigningIdentity::from_mnemonic(...)` (single source for tx + arbitrary signing keys).
- `validate_address(...)` and `validate_chain_prefix(...)`.
- `derive_signing_keys_from_mnemonic(...)` helper for existing flows.
- Updated `src/chain.rs`:
- Added signer-vs-creator precheck (`validate_signer_matches_creator`).
- Added `get_account_info(address)`.
- Added `calculate_fee_amount(gas_limit)` from configured gas price.
- Replaced fixed tx fee with gas-price-derived fee.
- Added `get_tx(tx_hash)` and `wait_for_tx_confirmation(tx_hash, timeout_secs)`.
- Added generic tx path: `build_signed_tx(...)`, `broadcast_signed_tx(...)`, `send_any_msgs(...)`.
- Added gas simulation + adjusted signing flow: `simulate_gas_for_tx(...)`, `build_signed_tx_with_simulation(...)`.
- Added common Lumera wrapper: `request_action_tx(...)`.
- Added broadcast mode support: `Async`, `Sync`, `Commit`.
- Refactored examples (`golden_devnet`, `ui_server`) to use centralized key derivation helper.

## Next parity steps (recommended)
1. Add richer tx result/event extraction helpers for action/general events.
2. Add integration tests for signer/address mismatch and tx wait timeout behavior.
3. Add convenience wrappers for more common Lumera msgs on top of generic tx path.

## Validation done
- `cargo check`: pass
- `cargo test --lib`: pass (13 tests)
25 changes: 5 additions & 20 deletions examples/golden_devnet.rs
Original file line number Diff line number Diff line change
@@ -1,27 +1,11 @@
use std::{
path::PathBuf,
str::FromStr,
time::{Duration, SystemTime, UNIX_EPOCH},
};

use bip32::{DerivationPath, XPrv};
use bip39::Mnemonic;
use k256::ecdsa::SigningKey as K256SigningKey;
use lumera_sdk_rs::{CascadeConfig, CascadeSdk, RegisterTicketRequest};

fn derive_signing_keys(
mnemonic: &str,
) -> anyhow::Result<(cosmrs::crypto::secp256k1::SigningKey, K256SigningKey)> {
let m = Mnemonic::parse(mnemonic)?;
let seed = m.to_seed("");
let path = DerivationPath::from_str("m/44'/118'/0'/0/0")?;
let xprv = XPrv::derive_from_path(seed, &path)?;
let sk_bytes = xprv.private_key().to_bytes();
let chain_sk = cosmrs::crypto::secp256k1::SigningKey::from_slice(&sk_bytes)
.map_err(|e| anyhow::anyhow!("cosmrs signing key: {e}"))?;
let arb_sk = K256SigningKey::from_slice(&sk_bytes)?;
Ok((chain_sk, arb_sk))
}
use lumera_sdk_rs::{
keys::derive_signing_keys_from_mnemonic, CascadeConfig, CascadeSdk, RegisterTicketRequest,
};

fn extract_state(v: &serde_json::Value) -> String {
for key in ["status", "state", "task_status"] {
Expand Down Expand Up @@ -69,7 +53,8 @@ async fn main() -> anyhow::Result<()> {
};

let sdk = CascadeSdk::new(cfg);
let (chain_sk, arb_sk) = derive_signing_keys(&mnemonic)?;
let (chain_sk, arb_sk) =
derive_signing_keys_from_mnemonic(&mnemonic).map_err(|e| anyhow::anyhow!(e.to_string()))?;

let exp_secs = SystemTime::now().duration_since(UNIX_EPOCH)?.as_secs() + 172800;
let expiration_time = exp_secs.to_string();
Expand Down
25 changes: 3 additions & 22 deletions examples/ui_server.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
use std::{
collections::HashMap,
net::SocketAddr,
str::FromStr,
sync::Arc,
time::{SystemTime, UNIX_EPOCH},
};
Expand All @@ -14,8 +13,6 @@ use axum::{
Json, Router,
};
use base64::{engine::general_purpose::STANDARD as B64, Engine as _};
use bip32::{DerivationPath, XPrv};
use bip39::Mnemonic;
use k256::ecdsa::SigningKey as K256SigningKey;
use lumera_sdk_rs::{CascadeConfig, CascadeSdk, RegisterTicketRequest};
use rand::{distributions::Alphanumeric, Rng};
Expand Down Expand Up @@ -132,25 +129,9 @@ fn random_token(len: usize) -> String {
fn derive_signing_keys(
mnemonic: &str,
) -> Result<(cosmrs::crypto::secp256k1::SigningKey, K256SigningKey), ApiError> {
let m = Mnemonic::parse(mnemonic).map_err(|e| ApiError {
error: format!("invalid LUMERA_MNEMONIC: {e}"),
})?;
let seed = m.to_seed("");
let path = DerivationPath::from_str("m/44'/118'/0'/0/0").map_err(|e| ApiError {
error: format!("invalid derivation path: {e}"),
})?;
let xprv = XPrv::derive_from_path(seed, &path).map_err(|e| ApiError {
error: format!("failed deriving key from mnemonic: {e}"),
})?;
let sk_bytes = xprv.private_key().to_bytes();
let chain_sk =
cosmrs::crypto::secp256k1::SigningKey::from_slice(&sk_bytes).map_err(|e| ApiError {
error: format!("failed creating chain signing key: {e}"),
})?;
let arb_sk = K256SigningKey::from_slice(&sk_bytes).map_err(|e| ApiError {
error: format!("failed creating arbitrary signing key: {e}"),
})?;
Ok((chain_sk, arb_sk))
lumera_sdk_rs::keys::derive_signing_keys_from_mnemonic(mnemonic).map_err(|e| ApiError {
error: e.to_string(),
})
}
Comment on lines 131 to 135
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The function body now delegates to derive_signing_keys_from_mnemonic, but the file still imports bip32::{DerivationPath, XPrv}, bip39::Mnemonic, and std::str::FromStr (lines 4, 17-18) which were only used by the old inline derivation logic. These are now dead imports and will trigger compiler warnings (or errors under #[deny(unused_imports)]).

Fix it with Roo Code or mention @roomote and request a fix.


fn extract_state(v: &serde_json::Value) -> String {
Expand Down
Loading
Loading