Skip to main content

SAG for Starknet

The SAG for Starknet implementation enables verification of Spontaneous Anonymous Group (SAG) signatures on the Starknet blockchain. This implementation leverages Starknet-specific optimizations to perform ring signature verification efficiently on-chain, supporting both SECP256K1 and ED25519 elliptic curves.

The SAG verification functionality on Starknet is provided by the starknet-sag-ts package from Alice's Ring, a TypeScript library for creating SAG signatures.

Key Differences in the Starknet Implementation

The Starknet version of the SAG verifier shares the same fundamental logic as the EVM version, with a few important adjustments to optimize for the Starknet ecosystem:

  1. Poseidon Hash Function: The signer and verifier use the Poseidon hashing function, which is computationally cheaper on Starknet compared to traditional hashing algorithms.

  2. Garaga for Multi-Scalar Multiplication (MSM): The implementation is using Garaga allowing efficient on-chain multi-scalar multiplication (MSM). The process incorporates hints passed alongside the ring signature to perform MSM efficiently.

Generating a Ring Signature for Starknet

Here's how to generate a ring signature for starknet using Alice's Ring in TypeScript:

import {
RingSignature,
Curve,
CurveName,
Point,
} from "@cypher-laboratory/alicesring-sag-starknet";

const curve = new Curve(CurveName.SECP256K1);
const message = "Hello, Alice's Ring!";

// Define an array of compressed public keys
const compressedPublickeys = [
"0316d7da70ba247a6a40bb310187e8789b80c45fa6dc0061abb8ced49cbe7f887f",
"0221869ca3ae33be3a7327e9a0272203afa72c52a5460ceb9f4a50930531bd926a",
// ...
];

const ring: Point[] = [];
// Deserialize each compressed public key and add it to the ring array
compressedPublickeys.forEach((compressedKey) => {
const point = Point.deserialize(compressedKey);
ring.push(point);
});

const signerPrivateKey = BigInt("0x..."); // The signer's private key

// Sign
const signature = RingSignature.sign(ring, signerPrivateKey, message, curve);

// Verify locally
console.log("Is signature valid?", signature.verify()); // Should output: true

(async () => {
// Get the signature data to send to the contract
const signature = RingSignature.sign(ring, signerPrivateKey, message, curve);
const rawCallData = await signature.getCallData();
console.log("Starknet callData: ", rawCallData);
})();

Important: ringSignature.getCallData() is an async function.

Verifying a Ring Signature On-Chain

To verify the ring signature on-chain, you can use the SAG verifier in Cairo. Below is an example of how to integrate the verifier into your Cairo contract.

Installation

Add the SAG Cairo Verifier to your Scarb.toml:

[dependencies]
alices_ring_cairo_verifier = {git = "https://github.com/Cypher-Laboratory/Alice-s-Ring-Cairo-verifier.git"}

Importing the Verifier Interface

use alices_ring_cairo_verifier::structtype::ringsignature;

#[starknet::interface]
pub trait IRingSignatureVerifier<TContractState> {
fn verify_rs(self: @TContractState, ring_signature: RingSignature) -> bool;
}

#[starknet::contract]
mod RingSignatureVerifier {
use alices_ring_cairo_verifier::verify;
use alices_ring_cairo_verifier::structType::RingSignature;

#[storage]
struct Storage {}

#[abi(embed_v0)]
impl IRingSignatureVerifier of super::IRingSignatureVerifier<ContractState> {
fn verify_rs(self: @ContractState, ring_signature: RingSignature) -> bool {
verify(ring_signature)
}
}
}

Using Voyager block explorer :

You can also pass the raw call data to the deployed contract at the following address:

Starknet Contract Address: 0x0317Ce745DE0A65308A19f6e184Be43A17F20D1eAe04B7433d984Df1D571DAA4 on Sepolia Starknet.

Number of steps

Verifying a SAG signature requires approximately 46,800 steps per public key in the ring.

Deployed address

NetworkDeployed Address
Starknet-SepoliaStarknet0x0317Ce745DE0A65308A19f6e184Be43A17F20D1eAe04B7433d984Df1D571DAA4

Contribution

We welcome contributions! To contribute, follow these steps:

  1. Fork the Repository for the signer or the Repository for the verifier : Create a fork of the repository in your GitHub account.

  2. Create a New Branch: Make a new branch for your feature or bug fix.

    git checkout -b feature/your-feature-name
  3. Write Code: Implement your feature or fix, ensuring your code is well-documented and tested.

  4. Submit a Pull Request: Push your branch to your fork and open a pull request to the main repository.


Additional Resources