GO SDK
This article will guide you through the process of connecting the TSM Node SDK V1 with our Docker Demo in just four straightforward steps. Before proceeding with these steps, please ensure that you have completed the prerequisites outlined below:
Prerequisites
- Make sure that you are using either Linux or Mac OS to install SDK
- Install GO
- Install the Docker Desktop Application
- Install Make
- Contact our support team to get:
- Credentials for accessing https://gitlab.com/sepior and https://nexus.sepior.net.
- Zip file for a Docker demo project.
1. Launch the Demo TSM via Docker
To run the demo TSM via Docker, follow the steps below:
- Unzip the docker setup zip file (demo-tsm.zip) that you got from our support team.
- Open the folder in your terminal.
- Login to Docker by using the code below:
docker login -u <yourUsername> -p <yourPassword> nexus.sepior.net:19001
Note:
Replace the username and password in the provided configuration with the credentials supplied by our support team.
- Run the TSM locally by using the code below:
make setup
- The TSM runs locally with three MPC Nodes that have been configured before. The MPC nodes can be reached from the host machine using the following addresses:
- TSM Node 1: http://localhost:8500/ping
- TSM Node 2: http://localhost:8501/ping
- TSM Node 3: http://localhost:8502/ping
- You have the flexibility to modify the Docker setup configuration file to suit your requirements. Feel free to make any desired edits. For a complete list of available configurations, please see here.
- Additionally, you have the option to employ the following command while executing your demo TSM:
docker compose up # Start the TSM
docker compose stop # Stop the TSM (without whipping the databases)
docker compose down # Stop the TSM and wipe all databases
docker compose logs # See tsm logs
2. Fetch the TSM SDK
To install and initialize the SDK on your local machine, follow the steps below:
- Create a new folder for your project.
- Open a terminal in your local machine.
- Set the
GOPRIVATE
environment variable to thegitlab.com/sepior
in a Unix-like shell by using the command below:
export GOPRIVATE=gitlab.com/sepior
- Add a configuration entry for authenticating with
gitlab.com/sepior
by using the command below:
echo "machine gitlab.com login <yourUsername> password <yourPassword>" >> $HOME/.netrc
Note:
Replace the username and password in the provided configuration with the credentials supplied by our support team.
- Initialize the Go module on your newly installed SDK project, by using the code below:
go mod init example.com
Note:
You can replace 'example.com' with the desired module path for your project.
- Since the demo TSM is currently at version 54.2.0, please use the following code to check out this specific version of the SDK:
go get gitlab.com/sepior/[email protected]
Note:
For the full list of our SDK version, please see here.
3. Generate Credentials for the TSM
The local process requires you to generate your own credentials for the MPC nodes of the TSM. You can generate the credentials by following the steps below:
- In your new SDK folder, create a new GO file. For example, we named the file “initialization.go”.
- Paste the code below into the newly created file:
package main
import (
"fmt"
"gitlab.com/sepior/go-tsm-sdk/sdk/tsm"
"net/url"
"os"
"time"
)
func main() {
const (
playerCount = 3 // Number of MPC nodes in the TSM
threhsold = 1 // Security threshold
)
// Create an admin client that connects to all MPC nodes
servers := []string{"http://localhost:8500", "http://localhost:8501", "http://localhost:8502"}
var nodes []tsm.Node
for _, s := range servers {
u, err := url.Parse(s)
if err != nil {
panic(err)
}
nodes = append(nodes, tsm.NewURLNode(*u, tsm.NullAuthenticator{}))
}
client := tsm.NewClient(playerCount, threhsold, nodes)
ac := tsm.NewAdminClient(client)
version, err := ac.TSMVersion()
if err != nil {
fmt.Println("Could not ping. Retrying...")
time.Sleep(time.Second)
version, err = ac.TSMVersion()
}
if err != nil {
fmt.Println("Could not ping servers")
panic(err)
}
fmt.Printf("TSM version: %s\n", version)
// Use the admin client to create an initial admin user and save credentials to 'admin.json'.
uc := tsm.NewUsersClient(client)
adminCreds, err := uc.CreateInitialAdmin()
if err != nil {
fmt.Printf("Could not create initial admin: %s\n", err)
fmt.Println("Exiting. We expect the TSM has already been initialized.")
return
}
fmt.Println("Created initial admin with user ID", adminCreds.UserID)
adminJson, err := adminCreds.Encode()
if err != nil {
panic(err)
}
err = os.WriteFile("admin.json", []byte(adminJson), 0666)
if err != nil {
panic(err)
}
// Log in as the initial admin, create a regular user, and save the user's credentials in 'user.json'.
admClient, err := tsm.NewPasswordClientFromCredentials(3, 1, adminCreds)
if err != nil {
panic(err)
}
uc = tsm.NewUsersClient(admClient)
userCreds, err := uc.CreatePasswordUser("user", "")
fmt.Println("Created regular user with user ID", userCreds.UserID)
userJson, err := userCreds.Encode()
if err != nil {
panic(err)
}
err = os.WriteFile("user.json", []byte(userJson), 0666)
if err != nil {
panic(err)
}
}
Note:
Make sure to substitute the URLs with the addresses of the TSM Nodes you've deployed using Docker, especially if you decide to deploy them on different URLs than the ones we've provided above.
- Call the command below on your terminal to run the code above:
go run initialization.go
- This process will generate two distinct JSON-format files: one containing admin keys and the other containing user keys for connecting to the TSM.
4. Generate Keys and Signatures using the TSM
You can run the TSM with a separate SDK for each MPC node. This ensures that crucial tasks require everyone, or a majority of designated operators, to give the green light. This adds an extra layer of security and control. It's like a group decision, making sure that important actions meet certain criteria, like transaction amounts, before they're allowed to proceed. Follow the steps below to generate a key in the TSM and use it to sign:
- Create a new go file in your SDK folder, then use the code provided below:
package main
import (
"crypto/sha256"
"encoding/hex"
"fmt"
"gitlab.com/sepior/go-tsm-sdk/sdk/tsm"
"golang.org/x/sync/errgroup"
"os"
)
func main() {
const (
playerCount = 3 // Number of MPC nodes in the TSM
threshold = 1 // The security threshold
)
credsBytes, err := os.ReadFile("user.json")
if err != nil {
panic(err)
}
var creds tsm.PasswordCredentials
if err := creds.UnmarshalJSON(credsBytes); err != nil {
panic(err)
}
// Create individual clients for each MPC node
ecdsaClients := make([]tsm.ECDSAClient, playerCount)
for player := 0; player < playerCount; player++ {
credsPlayer := tsm.PasswordCredentials{
UserID: creds.UserID,
URLs: []string{creds.URLs[player]},
Passwords: []string{creds.Passwords[player]},
}
client, err := tsm.NewPasswordClientFromCredentials(playerCount, threshold, credsPlayer)
if err != nil {
panic(err)
}
ecdsaClients[player] = tsm.NewECDSAClient(client)
}
// Generate ECSDA key
keyGenSessionID := tsm.GenerateSessionID()
var keyID string
var eg errgroup.Group
for i := 0; i < playerCount; i++ {
i := i
eg.Go(func() error {
var err error
keyID, err = ecdsaClients[i].KeygenWithSessionID(keyGenSessionID, "secp256k1")
return err
})
}
if err = eg.Wait(); err != nil {
panic(err)
}
fmt.Println("Generated key with ID:", keyID)
// Generate partial signatures using the key
message := []byte("This is the message to be signed")
msgHash := sha256.Sum256(message)
chainPath := []uint32{2, 5} // Sign using the derived key m/2/5
players := []int{0, 2} // Choose a subset of threshold+1 players to participate in signature generation
partialSignatures := make([][]byte, len(players))
// The call to PartialSign is blocking, so we must call each ecdsaClient concurrently.
signSessionID := ecdsaClients[0].GenerateSessionID()
fmt.Println("Generating signature using players", players)
for i, player := range players {
i, player := i, player
eg.Go(func() error {
var err error
partialSignatures[i], err = ecdsaClients[player].PartialSign(signSessionID, keyID, chainPath, msgHash[:], players...)
return err
})
}
if err := eg.Wait(); err != nil {
panic(err)
}
// Combine the partial signatures into an actual signature
signature, _, err := tsm.ECDSAFinalize(partialSignatures...)
if err != nil {
panic(err)
}
fmt.Println("Signature:", hex.EncodeToString(signature))
}
- The provided SDK code will autonomously establish connections with all MPC nodes, acquire signature shares from each node, combine them, and then provide the final signature and keyID as shown below:
Generated key with ID: AIWKvP7mXI5h9SMN1dvDMK12jdzl
Generating signature using players [0 2]
Signature: 3045022100cf8493ed985d3ef03b0fd283ca9ff84b12a50e93762aabc4a7b91f7ad033897e02207bbd8dff6f7ef052a8ac11fd81dad0e3d846002cc6c73e26ae7f830bbb77c55d
Updated 10 months ago