Go SDK
This tutorial has been tested on Linux and Mac OS. If you use another OS, some steps may be a bit different.
Prerequisites
- Go version 1.22
- URLs and API keys for the MPC nodes in a running instance of the Builder Vault TSM. You can follow one of the tutorials Local Deployment, Hosted Sandbox, or AWS Marketplace to get access to a Builder Vault TSM.
Step 1: Create a Go Project and Fetch the TSM SDK
In the following steps we will create a small Go project that uses the Builder Vault Go SDK:
- Create a new folder for your project on your local machine
- Open a terminal window and go to the project folder.
- Initialize the Go module by executing the following command in the project folder (you can replace
example.com
) with the desired module path for your project):
go mod init example.com
- Check out the latest version of the SDK, by using the following command:
go get gitlab.com/Blockdaemon/go-tsm-sdkv2/v68
TSM Version
When you connect to a MPC node with the SDK, it is important that the version of the SDK matches the version of the MPC node. You can see the actual version of an MPC node in the demo TSM using this Linux command:
curl https://node1.tsm.sepior.net/version
If the returned version is for example
68.0.0
, you can fetch the corresponding version of the SDK with this:
go get gitlab.com/Blockdaemon/go-tsm-sdkv2/[email protected]
For the full list of SDK versions, please see here.
Versioned Imports
Once fetched, you can import the SDK in your code like this:
import (
"gitlab.com/Blockdaemon/go-tsm-sdkv2/v68/tsm"
"gitlab.com/Blockdaemon/go-tsm-sdkv2/v68/tsm/tsmutils"
)
Step 2: Use the SDK to Generate a Key and Sign
In the next steps we will create a small Go program that (1) generates an ECDSA key in the TSM; (2) prints the public ECDSA key; and (3) signs a message using the newly generated key.
- Create a file
main.go
in the project folder, with the following contents. Don't worry about the details for now.
package main
import (
"bytes"
"context"
"crypto/sha256"
"encoding/hex"
"fmt"
"gitlab.com/Blockdaemon/go-tsm-sdkv2/v68/tsm"
"golang.org/x/sync/errgroup"
"sync"
)
func main() {
// Create clients for each of the nodes
configs := []*tsm.Configuration{
tsm.Configuration{URL: "https://node1.tsm.sepior.net"}.WithAPIKeyAuthentication("NODE1_API_KEY"),
tsm.Configuration{URL: "https://node2.tsm.sepior.net"}.WithAPIKeyAuthentication("NODE2_API_KEY"),
tsm.Configuration{URL: "https://node3.tsm.sepior.net"}.WithAPIKeyAuthentication("NODE3_API_KEY"),
}
clients := make([]*tsm.Client, len(configs))
for i, config := range configs {
var err error
if clients[i], err = tsm.NewClient(config); err != nil {
panic(err)
}
}
// Generate an ECDSA key
threshold := 1 // The security threshold for this key
keyGenPlayers := []int{0, 1, 2} // The key should be secret shared among all three MPC nodes
keyGenSessionConfig := tsm.NewSessionConfig(tsm.GenerateSessionID(), keyGenPlayers, nil)
fmt.Println("Generating key using players", keyGenPlayers)
ctx := context.Background()
keyIDs := make([]string, len(clients))
var eg errgroup.Group
for i, client := range clients {
client, i := client, i
eg.Go(func() error {
var err error
keyIDs[i], err = client.ECDSA().GenerateKey(ctx, keyGenSessionConfig, threshold, "secp256k1", "")
return err
})
}
if err := eg.Wait(); err != nil {
panic(err)
}
// Validate key IDs
for i := 1; i < len(keyIDs); i++ {
if keyIDs[0] != keyIDs[i] {
panic("key IDs do not match")
}
}
keyID := keyIDs[0]
fmt.Println("Generated key with ID:", keyID)
// Get the public key
var derivationPath []uint32 = nil // We don't use key derivation in this example
publicKeys := make([][]byte, len(clients))
for i, client := range clients {
var err error
publicKeys[i], err = client.ECDSA().PublicKey(ctx, keyID, derivationPath)
if err != nil {
panic(err)
}
}
// Validate public keys
for i := 1; i < len(publicKeys); i++ {
if !bytes.Equal(publicKeys[0], publicKeys[i]) {
panic("public keys do not match")
}
}
publicKey := publicKeys[0]
fmt.Println("Public key:", hex.EncodeToString(publicKey))
// We can now sign with the created key
message := []byte("This is a message to be signed")
msgHash := sha256.Sum256(message)
signPlayers := []int{0, 1} // We want to sign with the first two MPC nodes
sessionID := tsm.GenerateSessionID()
signSessionConfig := tsm.NewSessionConfig(sessionID, signPlayers, nil)
fmt.Println("Creating signature using players", signPlayers)
partialSignaturesLock := sync.Mutex{}
var partialSignatures [][]byte
for _, player := range signPlayers {
player := player
eg.Go(func() error {
if partialSignResult, err := clients[player].ECDSA().Sign(ctx, signSessionConfig, keyID, derivationPath, msgHash[:]); err != nil {
return err
} else {
partialSignaturesLock.Lock()
partialSignatures = append(partialSignatures, partialSignResult.PartialSignature)
partialSignaturesLock.Unlock()
return nil
}
})
}
if err := eg.Wait(); err != nil {
panic(err)
}
signature, err := tsm.ECDSAFinalizeSignature(msgHash[:], partialSignatures)
if err != nil {
panic(err)
}
// Verify the signature relative to the signed message and the public key
if err = tsm.ECDSAVerifySignature(publicKey, msgHash[:], signature.ASN1()); err != nil {
panic(err)
}
fmt.Println("Signature:", hex.EncodeToString(signature.ASN1()))
}
- Replace the API keys for the MPC nodes in line 20-22 above with the actual API keys for your Builder Vault instance.
- Execute the following commands in the project folder:
go mod tidy
go run main.go
You should now see some output similar to this:
Generating key using players [0 1 2]
Generated key with ID: ymdAAttlPmCqKp0vLgz7re4k87As
Public key: 3056301006072a8648ce3d020106052b8104000a03420004a50fbc4dbb0fb9157b26408fc3dbab1b42081668fc0212e1edd97bbd6906a3ee7b3fe69323e401b2b4117edff329879adc1f82e4ef6a1f0f89f084a59665df9f
Creating signature using players [0 1]
Signature: 304402204c33273dec7dac0ff510a1f98036ce62987da8174bb4d4a8456686732096449a022063ef69e1be58275c2cb341b0dcd17d586a28fe647453397d5c9cd5243577df63
This means that you have successfully used the Builder Vault SDK to connect to the Builder Vault, where you generated a new ECDSA key and used it for signing.
If you get error messages instead, you can consult our troubleshooting guide or contact our support team.
Updated 8 days ago