App Login

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.