Skip to content

Conversation

@Lazark0x
Copy link
Member

@Lazark0x Lazark0x commented Oct 16, 2025

gsigner Feature Comparison

This document compares the features implemented in gsigner with the legacy crates

  • ethexe-signer (secp256k1-focused Ethereum signer)
  • gring (utils/gring, sr25519-only keyring/CLI compatible with polkadot-js keystores)

Feature Matrix

Feature ethexe-signer gring gsigner
Cryptographic Schemes
secp256k1 (ECDSA)
ed25519 (EdDSA)
sr25519 (Schnorrkel)
Key Storage
In-memory storage
Filesystem storage
Encrypted keystore ✅ (secp256k1, ed25519, sr25519)
Polkadot-js compatible ✅ (sr25519)
Signing Features
Basic signatures
Recoverable signatures ✅ (secp256k1)
SignedData wrapper ✅ (secp256k1)
Contract signatures (EIP-191) ✅ (secp256k1)
Custom signing context ✅ (sr25519)
Signature verification
Key Management
Key generation
Vanity key generation ✅ (sr25519)
Key import ✅ (secp256k1, ed25519, sr25519)
Key export
Primary key concept ✅ (secp256k1, ed25519, sr25519)
Keyring manager ✅ (secp256k1, ed25519, sr25519)
Sub-signer
Integration
libp2p PeerId derivation ✅ (secp256k1, ed25519 via peer-id feature)
Address Formats
Ethereum (0x...)
SS58 (Substrate) ✅ (ed25519, sr25519)
SS58 re-encoding ✅ (ed25519, sr25519)
Encryption
Password protection ✅ (sr25519)
Scrypt KDF ✅ (sr25519)
XSalsa20-Poly1305 ✅ (sr25519)
Storage Format
Raw key files ✅ (secp256k1)
JSON keystores ✅ (secp256k1, ed25519, sr25519)
PKCS8 format ✅ (sr25519)
API Design
Unified signer interface
Trait-based abstraction
Extension traits
CLI
Command-line interface
Structured output formats ✅ (human/plain/json)

Detailed Feature Descriptions

Secp256k1 Features (from ethexe-signer)

✅ Implemented

  • Recoverable ECDSA Signatures: Full support for signature recovery
  • SignedData Wrapper: Bundles data with signature and public key
  • Contract Signatures (EIP-191): Domain-separated signatures for contracts
  • Sub-signer: Create signers with subset of keys
  • Ethereum Addresses: Standard 20-byte Ethereum address derivation
  • SURI / Mnemonic Import: Keyring can derive secp256k1 keys via Substrate SURIs and BIP39 phrases using sp_core::ecdsa::Pair

API Comparison

ethexe-signer:

let signer = Signer::memory();
let key = signer.generate_key()?;
let signature = signer.sign(key, data)?;
let signed_data = signer.signed_data(key, data)?;
let contract_addr = Address([0x11; 20]);
let contract_sig = signer.sign_for_contract(contract_addr, key, data)?;

gsigner:

use gsigner::secp256k1::{self, Secp256k1SignerExt};
use gsigner::Address;

let signer = secp256k1::Signer::memory();
let key = signer.generate_key()?;
let signature = signer.sign(key, data)?;
let signed_data = signer.signed_data(key, data)?;
let contract_addr = Address([0x11; 20]);
let contract_sig = signer.sign_for_contract(contract_addr, key, data)?;

Sr25519 Features (from gring)

✅ Implemented

  • Polkadot-js Keystore Format: Full compatibility for import/export
  • Scrypt + XSalsa20-Poly1305: Industry-standard encryption
  • PKCS8 Encoding: Standard key format
  • Signing Contexts: Schnorrkel signing contexts for domain separation
  • Vanity Key Generation: Generate keys with specific SS58 prefix
  • Keyring Manager: Manage multiple keys with primary key concept
  • SS58 Addresses: Substrate address format (VARA network)
  • Password Protection: Encrypted keystores with passphrase

API Comparison

gring:

let mut keyring = Keyring::load(store)?;
let (keystore, private_key) = keyring.create("alice", Some("5Ge"), Some("pass"))?;
keyring.set_primary("alice")?;
let primary = keyring.primary()?;
let pair = primary.decrypt(Some(b"pass"))?;
let sig = pair.sign(ctx.bytes(msg));

gsigner:

use gsigner::sr25519::{self, Sr25519SignerExt, Keyring};

// High-level signer API
let signer = sr25519::Signer::memory();
let key = signer.generate_key()?;
let sig = signer.sign_with_context(key, b"my-app", msg)?;

// Or use Keyring for keystore management
let mut keyring = Keyring::load(store)?;
let (keystore, private_key) = keyring.create_vanity("alice", "5Ge", Some(b"pass"))?;
keyring.set_primary("alice")?;

Ed25519 Features

✅ Implemented

  • SURI Compatibility: Uses sp_core::ed25519::Pair for derivations matching Substrate tooling
  • SS58 Addresses: Default Vara prefix with full SS58 encoding/decoding support
  • Shared Signer API: Available through the generic Signer<Ed25519> with filesystem and memory storage
  • Substrate Integration: Works alongside sr25519 flows for mixed key environments

New Features in gsigner

Unified Interface

  • Single Signer<S> type works with any scheme
  • Trait-based design allows easy extension
  • Consistent API across different cryptographic schemes

Extension Traits

  • Secp256k1SignerExt: Ethereum-specific functionality
  • Sr25519SignerExt: Substrate-specific functionality
  • Keeps core API clean while providing scheme-specific features

Universal Address Type

  • Address enum supports both Ethereum and Substrate addresses
  • Automatic conversions and display formatting
  • SS58 encoding/decoding utilities

Enhanced Type Safety

  • Strong typing prevents mixing keys from different schemes
  • Compile-time guarantees about key compatibility
  • Clear separation of concerns

Improved Error Handling

  • Consistent anyhow::Result return types
  • Descriptive error messages
  • Better error propagation

Production Parity Updates

  • All signing schemes now wrap their corresponding sp_core pair/public/signature types (secp256k1, ed25519, sr25519), so SURIs, SS58 addresses, and SCALE codecs match upstream Substrate tooling byte-for-byte.
  • secp256k1 contract signatures continue to validate against the original digest and are normalised to low-S form before being stored as sp_core::ecdsa::Signature values.
  • The CLI keyring flows cover secp256k1 and ed25519 in addition to the existing sr25519 workflows, giving a consistent management story across every algorithm.

Storage Abstraction

  • StorageLocationArgs unifies the CLI surface for every scheme, providing --path/--storage, --memory, and --storage-password flags.
  • Default per-scheme directories are resolved automatically when no explicit path is provided ($XDG_DATA_HOME/gsigner/<scheme> on Unix-like systems).
  • Password-protected keystores now span secp256k1, ed25519, and sr25519 so operators can encrypt any keyring with the same workflow.
  • Storage-backed constructors are fallible: Signer::fs and Signer::fs_with_password return Result, surfacing IO and decoding failures instead of panicking.
  • libp2p PeerId derivation is available behind the optional peer-id feature for secp256k1 and ed25519 keys.
  • CLI output formatting supports --format human|plain|json, making scripting easier while keeping human-friendly defaults.

Legacy crates

  • gring (utils/gring): sr25519-only; CLI supports add/import, generate (with optional vanity), list/use primary, sign/verify with a fixed signing context; keystore path is fixed to the platform data dir (e.g. ~/Library/Application Support/gring on macOS) and uses polkadot-js JSON with scrypt + xsalsa20 encryption; no multi-scheme support, no output-format selection, no peer-id; only sr25519 keys and PKCS8+SURI flows.
  • ethexe-signer: Ethereum-only (secp256k1) signer API from earlier releases; recoverable signatures, contract signing, and sub-signer were present but no multi-scheme keyring or CLI parity—storage/encryption were limited, there was no SS58/ed25519/sr25519 support, and no peer-id/output-format features.

Migration Guide

From ethexe-signer

// Old
use gsigner::secp256k1::{Signer, PrivateKey, PublicKey};
let signer = Signer::memory();

// New
use gsigner::secp256k1;
let signer = secp256k1::Signer::memory();

All ethexe-signer functionality is preserved with minimal API changes.

From gring

// Old
use gring::{Keyring, Keystore};
let keyring = Keyring::load(path)?;

// New
use gsigner::secp256k1::Keyring as SecpKeyring;
let secp_keyring = SecpKeyring::load(path.clone())?;

use gsigner::ed25519::Keyring as EdKeyring;
let ed_keyring = EdKeyring::load(path.clone())?;

use gsigner::sr25519::Keyring as SrKeyring;
let sr_keyring = SrKeyring::load(path)?;

Keyring APIs remain largely unchanged across schemes and share common primary-key behaviour.

@Lazark0x Lazark0x self-assigned this Oct 16, 2025
@semanticdiff-com
Copy link

semanticdiff-com bot commented Oct 16, 2025

Review changes with  SemanticDiff

Changed Files
File Status
  ethexe/consensus/src/validator/participant.rs  73% smaller
  ethexe/common/src/gear.rs  65% smaller
  ethexe/network/src/injected.rs  60% smaller
  ethexe/network/src/validator/topic.rs  60% smaller
  ethexe/network/src/validator/discovery.rs  55% smaller
  ethexe/consensus/src/validator/coordinator.rs  52% smaller
  ethexe/consensus/src/mock.rs  50% smaller
  gcli/src/app.rs  50% smaller
  gcli/examples/mycli.rs  48% smaller
  ethexe/db/src/database.rs  47% smaller
  ethexe/service/src/tests/utils/env.rs  43% smaller
  ethexe/ethereum/src/router/mod.rs  40% smaller
  ethexe/ethereum/src/deploy.rs  40% smaller
  ethexe/network/src/lib.rs  40% smaller
  ethexe/consensus/src/utils.rs  40% smaller
  ethexe/cli/src/params/network.rs  32% smaller
  gcli/src/cmd/wallet.rs  29% smaller
  gsigner/src/schemes/secp256k1/signature.rs  22% smaller
  gsigner/src/cli/mod.rs  14% smaller
  ethexe/consensus/src/validator/producer.rs  12% smaller
  ethexe/common/src/lib.rs  11% smaller
  gsigner/src/schemes/secp256k1/digest.rs  9% smaller
  gsigner/src/schemes/secp256k1/address.rs  6% smaller
  ethexe/common/src/injected.rs  5% smaller
  ethexe/cli/src/commands/key.rs  2% smaller
  gsigner/src/utils.rs  1% smaller
  gsigner/src/bin/gsigner.rs  1% smaller
  Cargo.lock Unsupported file format
  Cargo.toml Unsupported file format
  Makefile Unsupported file format
  _typos.toml Unsupported file format
  core/Cargo.toml Unsupported file format
  ethexe/README.md Unsupported file format
  ethexe/cli/Cargo.toml Unsupported file format
  ethexe/cli/src/commands/tx.rs Unsupported file format
  ethexe/common/Cargo.toml Unsupported file format
  ethexe/common/src/crypto/keys.rs  0% smaller
  ethexe/common/src/validators.rs  0% smaller
  ethexe/consensus/Cargo.toml Unsupported file format
  ethexe/consensus/src/validator/core.rs  0% smaller
  ethexe/consensus/src/validator/mod.rs  0% smaller
  ethexe/consensus/src/validator/tx_pool.rs  0% smaller
  ethexe/ethereum/Cargo.toml Unsupported file format
  ethexe/ethereum/src/lib.rs Unsupported file format
  ethexe/ethereum/src/mirror/mod.rs  0% smaller
  ethexe/network/Cargo.toml Unsupported file format
  ethexe/network/src/kad.rs  0% smaller
  ethexe/observer/Cargo.toml Unsupported file format
  ethexe/observer/src/tests.rs Unsupported file format
  ethexe/service/Cargo.toml Unsupported file format
  ethexe/service/src/config.rs  0% smaller
  ethexe/service/src/lib.rs Unsupported file format
  ethexe/service/src/tests/mod.rs  0% smaller
  ethexe/signer/Cargo.toml Unsupported file format
  ethexe/signer/src/signer.rs  0% smaller
  ethexe/signer/src/storage.rs  0% smaller
  gcli/Cargo.toml Unsupported file format
  gcli/src/keyring.rs  0% smaller
  gcli/src/lib.rs  0% smaller
  gsdk/Cargo.toml Unsupported file format
  gsdk/src/result.rs  0% smaller
  gsigner/CLI.md Unsupported file format
  gsigner/Cargo.toml Unsupported file format
  gsigner/README.md Unsupported file format
  gsigner/src/address.rs  0% smaller
  gsigner/src/cli/commands.rs  0% smaller
  gsigner/src/cli/display.rs  0% smaller
  gsigner/src/cli/handlers.rs  0% smaller
  gsigner/src/cli/keyring_ops.rs  0% smaller
  gsigner/src/cli/scheme.rs  0% smaller
  gsigner/src/cli/secp.rs  0% smaller
  gsigner/src/cli/storage.rs  0% smaller
  gsigner/src/cli/substrate.rs  0% smaller
  gsigner/src/cli/util.rs  0% smaller
  gsigner/src/crypto.rs  0% smaller
  gsigner/src/error.rs  0% smaller
  gsigner/src/hash.rs  0% smaller
  gsigner/src/keyring/encryption.rs  0% smaller
  gsigner/src/keyring/key_codec.rs  0% smaller
  gsigner/src/keyring/mod.rs  0% smaller
  gsigner/src/keyring/scheme.rs  0% smaller
  gsigner/src/lib.rs  0% smaller
  gsigner/src/peer_id.rs  0% smaller
  gsigner/src/schemes/ed25519/keyring.rs  0% smaller
  gsigner/src/schemes/ed25519/mod.rs  0% smaller
  gsigner/src/schemes/mod.rs  0% smaller
  gsigner/src/schemes/secp256k1/keyring.rs  0% smaller
  gsigner/src/schemes/secp256k1/keys.rs  0% smaller
  gsigner/src/schemes/secp256k1/mod.rs  0% smaller
  gsigner/src/schemes/secp256k1/signer_ext.rs  0% smaller
  gsigner/src/schemes/sr25519/keyring.rs  0% smaller
  gsigner/src/schemes/sr25519/keystore.rs  0% smaller
  gsigner/src/schemes/sr25519/mod.rs Unsupported file format
  gsigner/src/schemes/sr25519/signer_ext.rs  0% smaller
  gsigner/src/signer.rs  0% smaller
  gsigner/src/substrate.rs Unsupported file format
  gsigner/src/traits.rs  0% smaller
  gsigner/tests/cli.rs  0% smaller
  gstd/Cargo.toml Unsupported file format
  pallets/gear-builtin/Cargo.toml Unsupported file format
  pallets/grandpa-signer/Cargo.toml Unsupported file format
  runtime/vara/Cargo.toml Unsupported file format
  utils/crates-io/src/lib.rs  0% smaller
  utils/gear-workspace-hack/Cargo.toml Unsupported file format
  utils/gring/Cargo.toml Unsupported file format
  utils/gring/README.md Unsupported file format
  utils/gring/res/pair.json  0% smaller
  utils/gring/src/bin/gring.rs  0% smaller
  utils/gring/src/cmd.rs  0% smaller
  utils/gring/src/keyring.rs  0% smaller
  utils/gring/src/keystore.rs  0% smaller
  utils/gring/src/pair.rs  0% smaller
  utils/gring/src/scrypt.rs  0% smaller
  utils/gring/tests/command.rs  0% smaller
  utils/gring/tests/keystore.rs  0% smaller

Lazark0x and others added 10 commits October 16, 2025 19:32
# Conflicts:
#	ethexe/consensus/src/mock.rs
#	ethexe/consensus/src/validator/coordinator.rs
#	ethexe/network/src/lib.rs
#	ethexe/service/src/lib.rs
Resolved conflicts:
- ethexe/consensus: Updated to use ValidatorMessage wrapper for signing
- ethexe/network: Added nonempty, auto_impl, lru dependencies
- ethexe/observer/tests: Use gsigner with nonempty
- ethexe/service/tests: Added NonEmpty import
- gsigner/signature: Merged codec features and Hash impl
# Conflicts:
#	Cargo.lock
#	ethexe/common/src/tx_pool.rs
#	ethexe/consensus/src/utils.rs
#	ethexe/consensus/src/validator/coordinator.rs
#	ethexe/consensus/src/validator/core.rs
#	ethexe/consensus/src/validator/mod.rs
#	ethexe/consensus/src/validator/producer.rs
#	ethexe/ethereum/src/deploy.rs
#	ethexe/ethereum/src/lib.rs
#	ethexe/network/src/validator.rs
#	ethexe/observer/src/tests.rs
#	ethexe/service/src/lib.rs
#	ethexe/service/src/tests/mod.rs
#	ethexe/service/src/tests/utils/env.rs
#	gsigner/src/schemes/secp256k1/address.rs
pub enum Secp256k1Commands {
#[command(about = "Generate a new secp256k1 keypair")]
Generate {
#[arg(short, long, help = "Storage directory (default: memory only)")]
Copy link
Member

Choose a reason for hiding this comment

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

we need some default storage on disk for cli as well

Copy link
Member Author

Choose a reason for hiding this comment

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

Now by default it will be written to the disk, and in memory it will live on the additional argument memory


/// Keyring subcommands
#[derive(Subcommand, Debug, Clone)]
pub enum KeyringCommands {
Copy link
Member

Choose a reason for hiding this comment

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

whats keyring here? isn't all of this is keyring?

Copy link
Member Author

@Lazark0x Lazark0x Nov 27, 2025

Choose a reason for hiding this comment

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

Some commands aren't related to keyring storage implementation, so they live at the root scheme level

@breathx breathx added the A0-pleasereview PR is ready to be reviewed by the team label Nov 5, 2025
Comment on lines 383 to 389
let mut bytes = Vec::new();
for claim in value_claims {
bytes.extend_from_slice(claim.message_id.into_bytes().as_ref());
bytes.extend_from_slice(claim.destination.to_address_lossy().as_ref());
bytes.extend_from_slice(&claim.value.to_be_bytes());
}
sha3::Keccak256::digest(&bytes).into()
Copy link
Member

Choose a reason for hiding this comment

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

That was exactly old implementation for [ValueClaim], so you only need value_claims.to_digest().as_ref().update_hasher(hasher)

Copy link
Member Author

Choose a reason for hiding this comment

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

Yep, there WAS implementation for [ValueClaim], but now trait ToDigit live in gsigner crate and so I can't implement ToDigit for arrays

Comment on lines 397 to 401
let mut message_hashes = Vec::with_capacity(messages.len() * 32);
for message in messages {
message_hashes.extend_from_slice(message.to_digest().as_ref());
}
sha3::Keccak256::digest(&message_hashes).into()
Copy link
Member

Choose a reason for hiding this comment

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

Use sha3::Keccak256 directly to feed it with message digest bytes

Copy link
Member Author

Choose a reason for hiding this comment

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

hasher.update(messages.to_digest()); is enough I think

}

/// Get write access to the underlying storage.
pub fn storage_mut(&self) -> RwLockWriteGuard<'_, dyn KeyStorage<S>> {
Copy link
Member

Choose a reason for hiding this comment

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

Why it's decided to use RwLock? Is signer used in multi-threaded env somewhere?

Copy link
Member Author

Choose a reason for hiding this comment

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

The Signer type is Clone and wraps the keyring in Arc<RwLock<…>>, making it Send + Sync. That’s what lets the network/service code (e.g., ethexe service, network validators, and async tasks) share a signer across threads/tasks safely
The overhead is minimal for our use (mostly single-threaded CLI), our operations are coarse (IO, serde, crypto), so the lock cost is noise

}

/// Create temporary filesystem storage.
pub fn tmp() -> Self {
Copy link
Member

Choose a reason for hiding this comment

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

I guess it never actually needed because of memory storage

Copy link
Member Author

Choose a reason for hiding this comment

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

It's used in tests

# Conflicts:
#	Cargo.lock
#	ethexe/common/src/gear.rs
#	ethexe/common/src/tx_pool.rs
#	ethexe/db/src/database.rs
#	ethexe/service/src/lib.rs
#	ethexe/service/src/tests/mod.rs
#	ethexe/service/src/tests/utils/env.rs
#	ethexe/tx-pool/src/tests.rs
#	gsdk/Cargo.toml
#	gsdk/src/signer/mod.rs
#	gsigner/src/schemes/secp256k1/address.rs
#	gsigner/src/schemes/secp256k1/digest.rs
#	gsigner/src/schemes/secp256k1/signature.rs
# Conflicts:
#	Cargo.lock
#	ethexe/cli/src/commands/tx.rs
#	ethexe/consensus/src/utils.rs
#	ethexe/consensus/src/validator/core.rs
#	ethexe/consensus/src/validator/participant.rs
#	ethexe/consensus/src/validator/producer.rs
#	ethexe/service/src/lib.rs
#	ethexe/service/src/tests/utils/env.rs
# Conflicts:
#	Cargo.lock
#	ethexe/common/src/injected.rs
#	ethexe/consensus/src/validator/coordinator.rs
#	ethexe/consensus/src/validator/core.rs
#	ethexe/consensus/src/validator/mod.rs
#	ethexe/consensus/src/validator/tx_pool.rs
#	ethexe/ethereum/src/lib.rs
#	ethexe/service/src/lib.rs
# Conflicts:
#	Cargo.lock
#	ethexe/network/Cargo.toml
# Conflicts:
#	ethexe/ethereum/src/lib.rs
#	ethexe/ethereum/src/router/mod.rs
# Conflicts:
#	Cargo.lock
#	ethexe/network/src/lib.rs
#	ethexe/network/src/validator/topic.rs
# Conflicts:
#	ethexe/network/src/validator/topic.rs
# Conflicts:
#	ethexe/cli/src/commands/key.rs
#	ethexe/common/src/crypto/keys.rs
#	ethexe/service/src/lib.rs
# Conflicts:
#	Cargo.lock
#	ethexe/signer/Cargo.toml
#	gcli/Cargo.toml
#	gcli/src/app.rs
#	gcli/src/lib.rs
#	gsdk/Cargo.toml
#	gsdk/src/signer/mod.rs
#	utils/gring/Cargo.toml
# Conflicts:
#	Cargo.lock
#	ethexe/cli/Cargo.toml
#	ethexe/cli/src/commands/tx.rs
#	ethexe/signer/src/signer.rs
#	gsigner/src/schemes/secp256k1/address.rs
#	gsigner/src/schemes/secp256k1/signature.rs
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

A0-pleasereview PR is ready to be reviewed by the team

Projects

None yet

Development

Successfully merging this pull request may close these issues.

7 participants