Address

Examples showing how to integrate Bitcoin with the TSM

The example uses two btcsuite library dependencies, to create a Bitcoin address. You need to have access to an ECDSA Client, which is connected to the TSM, as shown here https://docs.sepior.com/docs/tsm-sdk.

import (
	"github.com/btcsuite/btcd/chaincfg"
	"github.com/btcsuite/btcutil"
)

curveName := "secp256k1"
keyID, err := ecdsaClient.Keygen(curveName)
if err != nil {
	panic(err)
}

chainPath := []uint32{1, 2, 3, 4}
derPublicKey, err := ecdsaClient.PublicKey(keyID, chainPath)
if err != nil {
	panic(err)
}

publicKey, err := ecdsaClient.ParsePublicKey(derPublicKey)
if err != nil {
	panic(err)
}

compressedPublicKey := make([]byte, 1+32)
ySignFlag := byte(publicKey.Y.Bit(0))
compressedPublicKey[0] = 2 | ySignFlag
publicKey.X.FillBytes(compressedPublicKey[1:])

address, err := btcutil.NewAddressPubKey(compressedPublicKey, &chaincfg.TestNet3Params)
if err != nil {
	panic(err)
}
// Note: Encoding a *AddressPubKey (pay-to-pubkey) results in a P2PKH address
//       (pay-to-pubkey-hash). Convert address to a *AddressPubKeyHash before using it.
btcAddress := address.EncodeAddress()
fmt.Println("Bitcoin address: ", btcAddress)
final String keyID = sdk.ecdsaKeygenWithSessionID(sdk.generateSessionID(),"secp256k1");

final int[] chainPath = new int[] {1,2,3,4};
final byte[] derPublicKey = sdk.ecdsaPublicKey(keyID, chainPath);

Security.addProvider(new BouncyCastleProvider());
final X509EncodedKeySpec spec = new X509EncodedKeySpec(derPublicKey);
final KeyFactory kf = KeyFactory.getInstance("EC", "BC");

final ECPublicKey publicKey = (ECPublicKey) kf.generatePublic(spec);
final ECPoint w = publicKey.getW();
final BigInteger x = w.getAffineX();
final BigInteger y = w.getAffineY();
        
byte[] xBytes = x.toByteArray();
if (xBytes.length == 33) {
  xBytes = Arrays.copyOfRange(xBytes, 1, 33);
}
byte[] leadingZeroes = new byte[32 - xBytes.length];

final ByteArrayOutputStream baos = new ByteArrayOutputStream();
baos.write(y.testBit(0) ? 3 : 2);
baos.write(leadingZeroes, 0, leadingZeroes.length);
baos.write(xBytes, 0, xBytes.length);

final MessageDigest sha256 = MessageDigest.getInstance("SHA-256");
final byte[] digest1 = sha256.digest(baos.toByteArray());

final MessageDigest ripemd160 = MessageDigest.getInstance("RIPEMD160", "BC");
final byte[] digest2 = ripemd160.digest(digest1);

final ByteArrayOutputStream addressBuilder = new ByteArrayOutputStream();
addressBuilder.write(0x00); // 0x00 for Mainnet, 0x6f for Testnet
addressBuilder.write(digest2, 0, digest2.length);

byte[] checksum = sha256.digest(addressBuilder.toByteArray());
checksum = sha256.digest(checksum);

addressBuilder.write(checksum, 0, 4);
        
// TODO: Use some Base58 encoder instead of Hex
String base58 = HexFormat.of().formatHex(addressBuilder.toByteArray());

System.out.println("Bitcoin address: " + base58);

The chainPath is the bip-32 derivation path.