Skip to content

Sign

Signs a message hash using a stored private key.

Method Signature

func (ks *MemKeystore) Sign(
	ctx context.Context,
	id string,
	msgHash *big.Int,
) (r, s *big.Int, err error)

Parameters

  • ctx - Context for cancellation and timeout
  • id - Identifier for the key (typically public key)
  • msgHash - Message hash to sign

Returns

  • r - R component of the signature
  • s - S component of the signature
  • err - Error if signing fails

Usage Example

package main
 
import (
	"context"
	"fmt"
	"math/big"
 
	"github.com/NethermindEth/starknet.go/account"
)
 
func main() {
	// Create a keystore with keys
	ks := account.NewMemKeystore()
 
	// Use simple test keys
	pubKey1 := "0xabc123def456789"
	privKey1 := new(big.Int).SetUint64(123456)
	ks.Put(pubKey1, privKey1)
 
	fmt.Printf("Stored key pair:\n")
	fmt.Printf("Public Key:  %s\n", pubKey1)
	fmt.Printf("Private Key: %s\n", privKey1)
 
	// Create a message to sign (as big.Int)
	fmt.Println("\nSigning a simple message:")
	msgHash := new(big.Int).SetUint64(42)
	fmt.Printf("Message hash: %d\n", msgHash)
 
	// Sign the message
	ctx := context.Background()
	r, s, err := ks.Sign(ctx, pubKey1, msgHash)
	if err != nil {
		fmt.Printf("Error signing: %v\n", err)
		return
	}
 
	fmt.Printf("Signature R: %s\n", r)
	fmt.Printf("Signature S: %s\n", s)
 
	// Note: In Starknet, signatures may include a random nonce component,
	// so consecutive signatures might differ even for the same message.
	// This is a security feature, not a bug.
	fmt.Println("\nNote: Starknet signatures may include randomness for security.")
	fmt.Println("Multiple signatures of the same message may differ - this is expected behavior.")
 
	// Sign a different message
	fmt.Println("\nSigning a different message:")
	msgHash2 := new(big.Int).SetUint64(999)
	fmt.Printf("Message hash 2: %d\n", msgHash2)
 
	r3, s3, err := ks.Sign(ctx, pubKey1, msgHash2)
	if err != nil {
		fmt.Printf("Error: %v\n", err)
		return
	}
 
	fmt.Printf("Signature R: %s\n", r3)
	fmt.Printf("Signature S: %s\n", s3)
 
	diffSig := r.String() != r3.String() || s.String() != s3.String()
	fmt.Printf("Different signature for different message: %v\n", diffSig)
 
	// Try to sign with a non-existent key
	fmt.Println("\nAttempting to sign with non-existent key:")
	_, _, err = ks.Sign(ctx, "0xnonexistent", msgHash)
	if err != nil {
		fmt.Printf("Error (expected): %v\n", err)
	}
 
	// Sign with another key
	fmt.Println("\nAdding and signing with another key:")
	pubKey2 := "0xfedcba9876543210"
	privKey2 := new(big.Int).SetUint64(654321)
	ks.Put(pubKey2, privKey2)
 
	fmt.Printf("Added second key with public: %s\n", pubKey2)
 
	r4, s4, err := ks.Sign(ctx, pubKey2, msgHash)
	if err != nil {
		fmt.Printf("Error: %v\n", err)
		return
	}
 
	fmt.Printf("Signature from key 2 - R: %s\n", r4)
	fmt.Printf("Signature from key 2 - S: %s\n", s4)
 
	// Verify different keys produce different signatures for same message
	diffKeys := r.String() != r4.String() || s.String() != s4.String()
	fmt.Printf("Different keys produce different signatures: %v\n", diffKeys)
 
	// Sign a larger message hash
	fmt.Println("\nSigning with large message hash:")
	largeMsgHash := new(big.Int)
	largeMsgHash.SetString("123456789012345678901234567890123456789012345678901234567890", 10)
	fmt.Printf("Large message hash: %s\n", largeMsgHash)
 
	rLarge, sLarge, err := ks.Sign(ctx, pubKey1, largeMsgHash)
	if err != nil {
		fmt.Printf("Error: %v\n", err)
		return
	}
 
	fmt.Printf("Signature R: %s\n", rLarge)
	fmt.Printf("Signature S: %s\n", sLarge)
	fmt.Println("Successfully signed large message hash")
 
	// Test with zero message
	fmt.Println("\nSigning zero message:")
	zeroMsg := new(big.Int).SetUint64(0)
	rZero, sZero, err := ks.Sign(ctx, pubKey1, zeroMsg)
	if err != nil {
		fmt.Printf("Error: %v\n", err)
		return
	}
 
	fmt.Printf("Zero message signature R: %s\n", rZero)
	fmt.Printf("Zero message signature S: %s\n", sZero)
	fmt.Println("Successfully signed zero message")
}

Expected Output

Stored key pair:
Public Key:  0xabc123def456789
Private Key: 123456
 
Signing a simple message:
Message hash: 42
Signature R: 350363698744514202016596156652529272146312782472185297641155919123890195017
Signature S: 1136077181137289796417253397844796064472716637152827049360996051469560431158
 
Note: Starknet signatures may include randomness for security.
Multiple signatures of the same message may differ - this is expected behavior.
 
Signing a different message:
Message hash 2: 999
Signature R: 1736365541321367122816357901054995873699313682125155373054428716510585654869
Signature S: 2084249067964098487100353618615176900001234111843826180138347451423234285961
Different signature for different message: true
 
Attempting to sign with non-existent key:
Error (expected): error getting key for sender 0xnonexistent: sender does not exist
 
Adding and signing with another key:
Added second key with public: 0xfedcba9876543210
Signature from key 2 - R: 1029479647647729092220824335274876135520449145876594273096441874620016545989
Signature from key 2 - S: 434638878563242991244618224841604684737714407539215907505315878466101298584
Different keys produce different signatures: true
 
Signing with large message hash:
Large message hash: 123456789012345678901234567890123456789012345678901234567890
Signature R: 2232426504623317383893977183996594994473055636645183038345148484870852780850
Signature S: 3604508386811491507657261658418621879396365229714043403744608049000890444981
Successfully signed large message hash
 
Signing zero message:
Zero message signature R: 952538782479164887216841984139180509490833921846972892507017292090768852286
Zero message signature S: 561335298835723713488277599279511567212141586280880298810698678065918642398
Successfully signed zero message

Description

Sign retrieves the private key for the given identifier and uses it to sign the message hash using ECDSA on the Starknet curve. The method is thread-safe and respects context cancellation.

Error Handling

r, s, err := ks.Sign(ctx, id, msgHash)
if err != nil {
	// Handle errors like:
	// - Key not found (ErrSenderNoExist)
	// - Context cancelled
	// - Signing failure
	return err
}

Related Methods

  • Get - Retrieve keys from keystore
  • Put - Store keys in keystore
  • Account.Sign - Higher-level signing method