Skip to content
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 .changeset/gold-ducks-repeat.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
---
'@lit-protocol/networks': patch
'@lit-protocol/e2e': patch
---

PKP signing now auto-hashes Cosmos payloads, exposes a documented bypassAutoHashing option, and ships with a new e2e suite plus docs so builders can rely on every listed curve working out of the box.
23 changes: 22 additions & 1 deletion docs/learning-lit/how-it-works.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -15,10 +15,31 @@ Each Lit Protocol node participates in a Distributed Key Generation (DKG) proces

The Lit network supports multiple cryptographic curves, signing schemes, and key types. Additional curves and schemes can be added as desired to enable additional interoperability with a wide variety of protocols and standards.

Today this includes:

| Scheme | Curve |
|--------|-------|
| `EcdsaK256Sha256` | secp256k1 |
| `EcdsaP256Sha256` | NIST P-256 |
| `EcdsaP384Sha384` | NIST P-384 |
| `SchnorrEd25519Sha512` | ed25519 |
| `SchnorrEd448Shake256` | ed448 |
| `SchnorrK256Taproot` | secp256k1 |
| `SchnorrK256Sha256` | secp256k1 |
| `SchnorrP256Sha256` | NIST P-256 |
| `SchnorrP384Sha384` | NIST P-384 |
| `SchnorrRistretto25519Sha512` | Ristretto25519 |
| `SchnorrRedJubjubBlake2b512` | Jubjub |
| `SchnorrRedDecaf377Blake2b512` | Decaf377 |
| `SchnorrkelSubstrate` | sr25519 |


Refer to the [PKP Sign guide](/sdk/auth-context-consumption/pkp-sign#available-signing-schemes) for the full scheme-by-curve matrix that SDK users call directly.

## Policy Enforcement and Data Orchestration – Lit Actions

Each Lit node contains a JavaScript execution environment which allows developers to write arbitrary code that dictates how the secrets and keys managed by the network are used. These programs are called Lit Actions, immutable JS serverless functions that govern signing and encryption / decryption operations. Lit Actions can natively fetch and process data from any on or off-chain source, be used to create complex transaction automations (e.g. dollar-cost-averaging), define rules for usage and access, create spending policies, trigger signature generation, and more.

## Further Reading

For an in-depth overview of how Lit keeps keys and assets secure, please check out the [security](/learning-lit/security) section.
For an in-depth overview of how Lit keeps keys and assets secure, please check out the [security](/learning-lit/security) section.
6 changes: 4 additions & 2 deletions docs/node-ops/staking-and-delegation.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,10 @@ After meeting the minimum self-stake requirement, any node operator can increase

Each node operator has the ability to set their own commission rate, which determines the percentage of staking rewards that are retained prior to being distributed to delegators. This allows operators to compete on both performance and pricing. Commission rates are managed and displayed on the Lit staking portal.

## Consensus Threshold Rule

All Lit validator and signing operations are guarded by a threshold rule that requires at least `ROUNDUP(MAX(3, 2N/3))` participating nodes for a committee of size `N`.
Copy link

Copilot AI Nov 7, 2025

Choose a reason for hiding this comment

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

The threshold formula notation could be clearer with explicit parentheses to avoid ambiguity. Consider updating to:

All Lit validator and signing operations are guarded by a threshold rule that requires at least `ROUNDUP(MAX(3, (2N/3)))` participating nodes for a committee of size `N`.

This makes it explicit that the division 2N/3 is computed first, then compared with 3, and finally rounded up.

Suggested change
All Lit validator and signing operations are guarded by a threshold rule that requires at least `ROUNDUP(MAX(3, 2N/3))` participating nodes for a committee of size `N`.
All Lit validator and signing operations are guarded by a threshold rule that requires at least `ROUNDUP(MAX(3, (2N/3)))` participating nodes for a committee of size `N`.

Copilot uses AI. Check for mistakes.

## Slashing

Slashing has been implemented to ensure that node operators keep their machines online and responsive at all times, preventing any downtime that could disrupt the network. Unlike some other protocols where slashing may also enforce computational ‘correctness,’ Lit Protocol relies on Trusted Execution Environments (TEEs) and threshold consensus mechanisms to guarantee the accuracy and integrity of operations. As a result, slashing in Lit Protocol is specifically designed to enforce availability and liveness rather than correctness.
Expand All @@ -62,5 +66,3 @@ Slashed funds are sent to a contract that is initially administered by the Lit P
### Governance Oversight

Outside of integrating slashing outcomes with the Lit governance process to ensure consistent enforcement of rules while maintaining flexibility to address edge cases, the Lit Protocol Council will serve an important role in overseeing the parameters associated with the slashing process itself. This includes configuring the slashing penalty itself, as well as managing the kick counter and decay mechanism.


19 changes: 18 additions & 1 deletion docs/sdk/auth-context-consumption/pkp-sign.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,23 @@ const signatures = await litClient.chain.raw.pkpSign({
});
```

### Hashing defaults and bypass

By default the SDK hashes ECDSA payloads for you using the canonical function for each chain (Ethereum → keccak256, Bitcoin/Cosmos → SHA-256/SHA-384) before sending to the nodes for signing. Schnorr/EdDSA schemes receive the raw bytes exactly as you provided them. If you already computed a digest (for example when signing EIP-712 typed data) you can pass it directly and opt out of the SDK hashing step by setting `bypassAutoHashing: true`:

```ts
const digestBytes = hexToBytes(hashTypedData(typedData));

const signature = await litClient.chain.raw.pkpSign({
chain: 'ethereum',
signingScheme: 'EcdsaK256Sha256',
pubKey: pkpInfo.pubkey,
authContext,
toSign: digestBytes,
bypassAutoHashing: true,
});
```

---

# Available signing schemes
Expand Down Expand Up @@ -66,4 +83,4 @@ const signatures = await litClient.chain.raw.pkpSign({
| `SchnorrRistretto25519Sha512` | Ristretto25519 |
| `SchnorrRedJubjubBlake2b512` | Jubjub |
| `SchnorrRedDecaf377Blake2b512` | Decaf377 |
| `SchnorrkelSubstrate` | sr25519 |
| `SchnorrkelSubstrate` | sr25519 |
3 changes: 3 additions & 0 deletions packages/e2e/src/tickets/signing-schemes.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
import { registerSigningSchemesTicketSuite } from './signing-schemes.suite';

registerSigningSchemesTicketSuite();
83 changes: 83 additions & 0 deletions packages/e2e/src/tickets/signing-schemes.suite.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
import { LitCurve } from '@lit-protocol/constants';
import { SigningChainSchema } from '@lit-protocol/schemas';
import { z } from 'zod';
import { createEnvVars } from '../helper/createEnvVars';
import { createTestAccount } from '../helper/createTestAccount';
import { createTestEnv } from '../helper/createTestEnv';

type SigningChain = z.infer<typeof SigningChainSchema>;

type SchemeUnderTest = {
scheme: LitCurve;
chain: SigningChain;
};

const SIGNING_MATRIX: SchemeUnderTest[] = [
// ECDSA variants
{ scheme: 'EcdsaK256Sha256', chain: 'ethereum' },
{ scheme: 'EcdsaP256Sha256', chain: 'ethereum' },
{ scheme: 'EcdsaP384Sha384', chain: 'ethereum' },
// Schnorr over secp256k1 (Bitcoin / Taproot)
{ scheme: 'SchnorrK256Sha256', chain: 'bitcoin' },
{ scheme: 'SchnorrK256Taproot', chain: 'bitcoin' },
// Schnorr over NIST curves
{ scheme: 'SchnorrP256Sha256', chain: 'cosmos' },
{ scheme: 'SchnorrP384Sha384', chain: 'cosmos' },
// EdDSA-style curves
{ scheme: 'SchnorrEd25519Sha512', chain: 'solana' },
{ scheme: 'SchnorrEd448Shake256', chain: 'solana' },
// ZK / privacy-focused curves
{ scheme: 'SchnorrRistretto25519Sha512', chain: 'solana' },
{ scheme: 'SchnorrRedJubjubBlake2b512', chain: 'solana' },
{ scheme: 'SchnorrRedDecaf377Blake2b512', chain: 'solana' },
{ scheme: 'SchnorrkelSubstrate', chain: 'solana' },
];

export function registerSigningSchemesTicketSuite() {
describe('pkp signing schemes', () => {
let testEnv: Awaited<ReturnType<typeof createTestEnv>>;
let signerAccount: Awaited<ReturnType<typeof createTestAccount>>;

beforeAll(async () => {
const envVars = createEnvVars();
testEnv = await createTestEnv(envVars);
signerAccount = await createTestAccount(testEnv, {
label: 'Signing Schemes',
fundAccount: true,
fundLedger: true,
hasEoaAuthContext: true,
hasPKP: true,
fundPKP: true,
fundPKPLedger: true,
});
});

it.each(SIGNING_MATRIX)(
'should sign using %s',
async ({ scheme, chain }) => {
if (!signerAccount.pkp?.pubkey) {
throw new Error('Signer PKP was not initialized');
}
if (!signerAccount.eoaAuthContext) {
throw new Error('Signer account is missing an EOA auth context');
}

const toSign = new TextEncoder().encode(
`Lit signing e2e test using ${scheme}`
);

const signature = await testEnv.litClient.chain.raw.pkpSign({
authContext: signerAccount.eoaAuthContext,
pubKey: signerAccount.pkp.pubkey,
signingScheme: scheme,
chain,
toSign,
userMaxPrice: 100_000_000_000_000_000n, // 0.1 ETH in wei to clear threshold comfortably
});

expect(signature.signature).toBeTruthy();
expect(signature.sigType).toBe(scheme);
}
);
});
}
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ export const PKPSignInputSchema = z.object({
toSign: z.any(),
authContext: z.union([PKPAuthContextSchema, EoaAuthContextSchema]),
userMaxPrice: z.bigint().optional(),
bypassAutoHashing: z.boolean().optional(),
});

export const EthereumPKPSignInputSchema = PKPSignInputSchema.omit({
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -70,10 +70,15 @@ export const chainHashMapper: ChainHashMapper = {
EcdsaP384Sha384: sha384,
},

// @ts-ignore TODO: add support for this
cosmos: undefined,
cosmos: {
EcdsaK256Sha256: sha256,
EcdsaP256Sha256: sha256,
EcdsaP384Sha384: sha384,
},

// @ts-ignore TODO: add support for this
// Solana signatures use Ed25519 (handled by the FROST branch),
// so we intentionally omit it from the ECDSA mapper.
// @ts-ignore
solana: undefined,
};

Expand All @@ -89,9 +94,23 @@ export const LitMessageSchema = z
}

if (CURVE_GROUP_BY_CURVE_TYPE[signingScheme] === 'ECDSA') {
const hashedMessage = chainHashMapper[chain][
signingScheme as DesiredEcdsaSchemes
](new Uint8Array(toSign));
const chainHasher = chainHashMapper[chain];

if (!chainHasher) {
throw new Error(
`Chain "${chain}" does not support ECDSA signing with Lit yet.`
);
}

const hashFn = chainHasher[signingScheme as DesiredEcdsaSchemes];

if (!hashFn) {
throw new Error(
`Signing scheme "${signingScheme}" is not enabled for chain "${chain}".`
);
}

const hashedMessage = hashFn(new Uint8Array(toSign));
return BytesArraySchema.parse(hashedMessage);
}

Expand Down
Loading