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 Builder Vault 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/v70
Builder Vault 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. If the MPC node has the URL
https://mpc.node.net
, you can see which version the noe runs with this Linux command:
curl https://mpc.node.net/version
If the returned version is for example
70.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.
Once fetched, you can import the SDK in your code like this:
import (
"gitlab.com/Blockdaemon/go-tsm-sdkv2/v70/tsm"
"gitlab.com/Blockdaemon/go-tsm-sdkv2/v70/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/v70/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 6 days ago