Skip to main content

Getting Started

Generate a key and begin signing in under a minute.

Key Generation

First we must generate a public key and some key shares so that we can later sign messages.
You will need your API_KEY.

We show example code for a 2-of-3 threshold signing scenario.
(Note that Sodot MPC SDK allows any t-of-n threshold signing setting)

To generate a key we simply run:

Feature

BIP-340 (for Bitcoin Taproot) is also supported.

import "github.com/sodot-rs/sodot-go-sdk"

// Your server side creates a room for 3 parties using its API_KEY
// Creating a room uuid should always happen on the server side using your API_KEY
const N = 3
const T = 2
const API_KEY = "MY_API_KEY"

ecdsa := sodot.NewEcdsa("")
keygenRoomUuid, err := ecdsa.CreateRoom(N, API_KEY)
if err != nil {
panic(err)
}
// All parties call InitKeygen to get a KeygenID and a KeygenPrivateKey
keygenID, keygenPrivKey, err := ecdsa.InitKeygen()
if err != nil {
panic(err)
}
// Send the keygenID to all other parties
_ = keygenID
// All parties receive the KeygenIDs from all other parties
keygenIds := []sodot.KeygenID{"keygenID1", "keygenID2"}
// All parties join the keygen room
secretShare, pk, err := ecdsa.Keygen(keygenRoomUuid, N, T, keygenPrivKey, keygenIds)
if err != nil {
panic(err)
}
// The public key can now be used to verify signatures
_, _ = secretShare, pk

// Pick the derivation path of the public key you want to sign for
derivationPath := []uint32{44, 60, 0, 0, 0}
// Get the public key for the derivation path
derivedPubKey, err := ecdsa.DerivePubkey(secretShare, derivationPath)
if err != nil {
panic(err)
}
// The derived public key can now be used to verify signatures with the same derivation path
_ = derivedPubKey
API reference

Full details can be found here.

Behind the scenes this is the rough flow of communication that occurs:

Signing

Now that we have key shares on all the devices/servers of the potential signers we can sign by running:


import (
"crypto/sha256"
"github.com/kaspanet/go-secp256k1"
)

// A public key were generated using MPC
// Pick the derivation path of the public key you want to sign for
derivationPath := []uint32{44, 60, 0, 0, 0}
// Get the public key for the derivation path
derivedPubKey, err := ecdsa.DerivePubkey(secretShare, derivationPath)
if err != nil {
panic(err)
}
// Hash the message
messageHash := sodot.MessageHashFromSha256([]byte("my message"))
// 2 parties join the signing room
signature, err := ecdsa.Sign(signingRoomUuid, secretShare, messageHash, derivationPath)
if err != nil {
panic(err)
}

// Create a signature and public key according to kaspanet/go-secp256k's format
serializedSignature := append(signature.R(), signature.S()...)
deserializedSignature, _ := secp256k1.DeserializeECDSASignature((*secp256k1.SerializedECDSASignature)(serializedSignature))
compressedPk = derivedPubKey.SerializeCompressed()
pubKey, _ := secp256k1.DeserializeECDSAPubKey(compressedPk[:])
var hash = secp256k1.Hash(messageHash)
// validate the siganture
isValid := pubKey.ECDSAVerify(&messageHash, deserializedSignature)

Full library documentation can be found here.