Skip to content

Commit

Permalink
feat: TSS and Hotkey password Prompt - develop (#1630)
Browse files Browse the repository at this point in the history
* initial commit

* update pr number in changelog
  • Loading branch information
kevinssgh authored Feb 3, 2024
1 parent 82fb21f commit 62e7f7f
Show file tree
Hide file tree
Showing 14 changed files with 90 additions and 66 deletions.
3 changes: 3 additions & 0 deletions changelog.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
# CHANGELOG

## Unreleased
* [1630](https://github.com/zeta-chain/node/pull/1630) added password prompts for hotkey and tss keyshare in zetaclient
Starting zetaclient now requires two passwords to be input; one for the hotkey and another for the tss key-share.
* `zetaclientd start` : 2 inputs required from stdin

### Fixes

Expand Down
6 changes: 5 additions & 1 deletion cmd/zetaclientd/keygen_tss.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,9 @@ func GenerateTss(logger zerolog.Logger,
priKey secp256k1.PrivKey,
ts *mc.TelemetryServer,
tssHistoricalList []observertypes.TSS,
metrics *metrics.Metrics) (*mc.TSS, error) {
metrics *metrics.Metrics,
tssPassword string,
hotkeyPassword string) (*mc.TSS, error) {
keygenLogger := logger.With().Str("module", "keygen").Logger()

// Bitcoin chain ID is currently used for using the correct signature format
Expand All @@ -47,6 +49,8 @@ func GenerateTss(logger zerolog.Logger,
tssHistoricalList,
metrics,
bitcoinChainID,
tssPassword,
hotkeyPassword,
)
if err != nil {
keygenLogger.Error().Err(err).Msg("NewTSS error")
Expand Down
36 changes: 33 additions & 3 deletions cmd/zetaclientd/start.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package main

import (
"bufio"
"encoding/json"
"fmt"
"io"
Expand Down Expand Up @@ -45,6 +46,12 @@ func start(_ *cobra.Command, _ []string) error {

SetupConfigForTest()

//Prompt for Hotkey and TSS key-share passwords
hotkeyPass, tssKeyPass, err := promptPasswords()
if err != nil {
return err
}

//Load Config file given path
cfg, err := config.Load(rootArgs.zetaCoreHome)
if err != nil {
Expand Down Expand Up @@ -77,7 +84,7 @@ func start(_ *cobra.Command, _ []string) error {

// CreateZetaBridge: Zetabridge is used for all communication to zetacore , which this client connects to.
// Zetacore accumulates votes , and provides a centralized source of truth for all clients
zetaBridge, err := CreateZetaBridge(cfg, telemetryServer)
zetaBridge, err := CreateZetaBridge(cfg, telemetryServer, hotkeyPass)
if err != nil {
panic(err)
}
Expand Down Expand Up @@ -120,7 +127,7 @@ func start(_ *cobra.Command, _ []string) error {
// The bridgePk is private key for the Hotkey. The Hotkey is used to sign all inbound transactions
// Each node processes a portion of the key stored in ~/.tss by default . Custom location can be specified in config file during init.
// After generating the key , the address is set on the zetacore
bridgePk, err := zetaBridge.GetKeys().GetPrivateKey()
bridgePk, err := zetaBridge.GetKeys().GetPrivateKey(hotkeyPass)
if err != nil {
startLogger.Error().Err(err).Msg("zetabridge getPrivateKey error")
}
Expand Down Expand Up @@ -160,7 +167,7 @@ func start(_ *cobra.Command, _ []string) error {
}

telemetryServer.SetIPAddress(cfg.PublicIP)
tss, err := GenerateTss(masterLogger, cfg, zetaBridge, peers, priKey, telemetryServer, tssHistoricalList, metrics)
tss, err := GenerateTss(masterLogger, cfg, zetaBridge, peers, priKey, telemetryServer, tssHistoricalList, metrics, tssKeyPass, hotkeyPass)
if err != nil {
return err
}
Expand Down Expand Up @@ -309,3 +316,26 @@ func initPreParams(path string) {
}
}
}

// promptPasswords() This function will prompt for passwords which will be used to decrypt two key files:
// 1. HotKey
// 2. TSS key-share
func promptPasswords() (string, string, error) {
reader := bufio.NewReader(os.Stdin)
fmt.Print("HotKey Password: ")
hotKeyPass, err := reader.ReadString('\n')
if err != nil {
return "", "", err
}
fmt.Print("TSS Password: ")
TSSKeyPass, err := reader.ReadString('\n')
if err != nil {
return "", "", err
}

if TSSKeyPass == "" {
return "", "", errors.New("tss password is required to start zetaclient")
}

return hotKeyPass, TSSKeyPass, err
}
6 changes: 3 additions & 3 deletions cmd/zetaclientd/utils.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,15 +15,15 @@ func CreateAuthzSigner(granter string, grantee sdk.AccAddress) {
zetaclient.SetupAuthZSignerList(granter, grantee)
}

func CreateZetaBridge(cfg *config.Config, telemetry *zetaclient.TelemetryServer) (*zetaclient.ZetaCoreBridge, error) {
func CreateZetaBridge(cfg *config.Config, telemetry *zetaclient.TelemetryServer, hotkeyPassword string) (*zetaclient.ZetaCoreBridge, error) {
hotKey := cfg.AuthzHotkey
if cfg.HsmMode {
hotKey = cfg.HsmHotKey
}

chainIP := cfg.ZetaCoreURL

kb, _, err := zetaclient.GetKeyringKeybase(cfg)
kb, _, err := zetaclient.GetKeyringKeybase(cfg, hotkeyPassword)
if err != nil {
return nil, err
}
Expand All @@ -33,7 +33,7 @@ func CreateZetaBridge(cfg *config.Config, telemetry *zetaclient.TelemetryServer)
return nil, err
}

k := zetaclient.NewKeysWithKeybase(kb, granterAddreess, cfg.AuthzHotkey)
k := zetaclient.NewKeysWithKeybase(kb, granterAddreess, cfg.AuthzHotkey, hotkeyPassword)

bridge, err := zetaclient.NewZetaCoreBridge(k, chainIP, hotKey, cfg.ChainID, cfg.HsmMode, telemetry)
if err != nil {
Expand Down
2 changes: 2 additions & 0 deletions contrib/localnet/scripts/password.file
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
password
pass2
17 changes: 12 additions & 5 deletions contrib/localnet/scripts/start-zetaclientd-background.sh
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,19 @@

HOSTNAME=$(hostname)

# read HOTKEY_BACKEND env var for hotkey keyring backend and set default to test
BACKEND="test"
if [ "$HOTKEY_BACKEND" == "file" ]; then
BACKEND="file"
fi


cp /root/preparams/PreParams_$HOSTNAME.json /root/preParams.json
num=$(echo $HOSTNAME | tr -dc '0-9')
node="zetacore$num"

echo "Wait for zetacore to exchange genesis file"
sleep 30
sleep 40
operator=$(cat $HOME/.zetacored/os.json | jq '.ObserverAddress' )
operatorAddress=$(echo "$operator" | tr -d '"')
echo "operatorAddress: $operatorAddress"
Expand All @@ -18,8 +25,8 @@ if [ $HOSTNAME == "zetaclient0" ]
then
rm ~/.tss/*
MYIP=$(/sbin/ip -o -4 addr list eth0 | awk '{print $4}' | cut -d/ -f1)
zetaclientd init --zetacore-url zetacore0 --chain-id athens_101-1 --operator "$operatorAddress" --log-format=text --public-ip "$MYIP"
zetaclientd start > $HOME/zetaclient.log 2>&1 &
zetaclientd init --zetacore-url zetacore0 --chain-id athens_101-1 --operator "$operatorAddress" --log-format=text --public-ip "$MYIP" --keyring-backend "$BACKEND"
zetaclientd start < /root/password.file > $HOME/zetaclient.log 2>&1 &
else
num=$(echo $HOSTNAME | tr -dc '0-9')
node="zetacore$num"
Expand All @@ -30,8 +37,8 @@ else
SEED=$(curl --retry 10 --retry-delay 5 --retry-connrefused -s zetaclient0:8123/p2p)
done
rm ~/.tss/*
zetaclientd init --peer /ip4/172.20.0.21/tcp/6668/p2p/"$SEED" --zetacore-url "$node" --chain-id athens_101-1 --operator "$operatorAddress" --log-format=text --public-ip "$MYIP" --log-level 0
zetaclientd start > $HOME/zetaclient.log 2>&1 &
zetaclientd init --peer /ip4/172.20.0.21/tcp/6668/p2p/"$SEED" --zetacore-url "$node" --chain-id athens_101-1 --operator "$operatorAddress" --log-format=text --public-ip "$MYIP" --log-level 1 --keyring-backend "$BACKEND"
zetaclientd start < /root/password.file > $HOME/zetaclient.log 2>&1 &
fi

sleep 3
Expand Down
4 changes: 2 additions & 2 deletions contrib/localnet/scripts/start-zetaclientd-genesis.sh
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ then
rm ~/.tss/*
MYIP=$(/sbin/ip -o -4 addr list eth0 | awk '{print $4}' | cut -d/ -f1)
zetaclientd init --zetacore-url zetacore0 --chain-id athens_101-1 --operator "$operatorAddress" --log-format=text --public-ip "$MYIP" --keyring-backend "$BACKEND"
zetaclientd start
zetaclientd start < /root/password.file
else
num=$(echo $HOSTNAME | tr -dc '0-9')
node="zetacore$num"
Expand All @@ -37,5 +37,5 @@ else
done
rm ~/.tss/*
zetaclientd init --peer /ip4/172.20.0.21/tcp/6668/p2p/"$SEED" --zetacore-url "$node" --chain-id athens_101-1 --operator "$operatorAddress" --log-format=text --public-ip "$MYIP" --log-level 1 --keyring-backend "$BACKEND"
zetaclientd start
zetaclientd start < /root/password.file
fi
4 changes: 2 additions & 2 deletions contrib/localnet/scripts/start-zetaclientd-p2p-diag.sh
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ then
--pre-params ~/preParams.json --zetacore-url zetacore0 \
--chain-id athens_101-1 --dev --operator zeta1z46tdw75jvh4h39y3vu758ctv34rw5z9kmyhgz --log-level 0 --hotkey=val_grantee_observer \
--p2p-diagnostic
zetaclientd start
zetaclientd start < /root/password.file
else
num=$(echo $HOSTNAME | tr -dc '0-9')
node="zetacore$num"
Expand All @@ -29,5 +29,5 @@ else
--pre-params ~/preParams.json --zetacore-url $node \
--chain-id athens_101-1 --dev --operator zeta1lz2fqwzjnk6qy48fgj753h48444fxtt7hekp52 --log-level 0 --hotkey=val_grantee_observer \
--p2p-diagnostic
zetaclientd start
zetaclientd start < /root/password.file
fi
1 change: 1 addition & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -338,6 +338,7 @@ replace (
// use cometbft
github.com/tendermint/tendermint => github.com/cometbft/cometbft v0.34.28
github.com/tendermint/tm-db => github.com/BlockPILabs/cosmos-db v0.0.3
github.com/zeta-chain/go-tss => github.com/zeta-chain/go-tss v0.1.1-0.20240115203400-a5b80e5da933

)

Expand Down
4 changes: 4 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -1848,6 +1848,7 @@ github.com/ipfs/go-datastore v0.6.0/go.mod h1:rt5M3nNbSO/8q1t4LNkLyUwRs8HupMeN/8
github.com/ipfs/go-detect-race v0.0.1 h1:qX/xay2W3E4Q1U7d9lNs1sU9nvguX0a7319XbyQ6cOk=
github.com/ipfs/go-detect-race v0.0.1/go.mod h1:8BNT7shDZPo99Q74BpGMK+4D8Mn4j46UU0LZ723meps=
github.com/ipfs/go-ipfs-util v0.0.2 h1:59Sswnk1MFaiq+VcaknX7aYEyGyGDAA73ilhEK2POp8=
github.com/ipfs/go-ipfs-util v0.0.2/go.mod h1:CbPtkWJzjLdEcezDns2XYaehFVNXG9zrdrtMecczcsQ=
github.com/ipfs/go-log v1.0.5 h1:2dOuUCB1Z7uoczMWgAyDck5JLb72zHzrMnGnCNNbvY8=
github.com/ipfs/go-log v1.0.5/go.mod h1:j0b8ZoR+7+R99LD9jZ6+AJsrzkPbSXbZfGakb5JPtIo=
github.com/ipfs/go-log/v2 v2.1.3/go.mod h1:/8d0SH3Su5Ooc31QlL1WysJhvyOTDCjcCZ9Axpmri6g=
Expand Down Expand Up @@ -2760,6 +2761,7 @@ github.com/sourcegraph/go-diff v0.5.1/go.mod h1:j2dHj3m8aZgQO8lMTcTnBcXkRRRqi34c
github.com/sourcegraph/go-diff v0.5.3/go.mod h1:v9JDtjCE4HHHCZGId75rg8gkKKa98RVjBcBGsVmMmak=
github.com/sourcegraph/go-diff v0.6.1/go.mod h1:iBszgVvyxdc8SFZ7gm69go2KDdt3ag071iBaWPF6cjs=
github.com/sourcegraph/syntaxhighlight v0.0.0-20170531221838-bd320f5d308e/go.mod h1:HuIsMU8RRBOtsCgI77wP899iHVBQpCmg4ErYMZB+2IA=
github.com/spacemonkeygo/spacelog v0.0.0-20180420211403-2296661a0572/go.mod h1:w0SWMsp6j9O/dk4/ZpIhL+3CkG8ofA2vuv7k+ltqUMc=
github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA=
github.com/spaolacci/murmur3 v1.1.0 h1:7c1g84S4BPRrfL5Xrdp6fOJ206sU9y293DDHaoy0bLI=
github.com/spaolacci/murmur3 v1.1.0/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA=
Expand Down Expand Up @@ -3035,6 +3037,8 @@ github.com/yvasiyarov/gorelic v0.0.0-20141212073537-a9bba5b9ab50/go.mod h1:NUSPS
github.com/yvasiyarov/newrelic_platform_go v0.0.0-20140908184405-b21fdbd4370f/go.mod h1:GlGEuHIJweS1mbCqG+7vt2nvWLzLLnRHbXz5JKd/Qbg=
github.com/zeta-chain/go-tss v0.1.1-0.20240103170132-35850edf5dbd h1:wv+VGLFX8IhPuoqAVQGAQjlEPWqYjowJgJVNReolJTM=
github.com/zeta-chain/go-tss v0.1.1-0.20240103170132-35850edf5dbd/go.mod h1:+lJfk/qqt+oxXeVuJV+PzpUoxftUfoTRf2eF3qlbyFI=
github.com/zeta-chain/go-tss v0.1.1-0.20240115203400-a5b80e5da933 h1:cx6ZXVmV9LpkYRQER7+sTgu56wdmaU1U5VJcx3rsCwc=
github.com/zeta-chain/go-tss v0.1.1-0.20240115203400-a5b80e5da933/go.mod h1:+lJfk/qqt+oxXeVuJV+PzpUoxftUfoTRf2eF3qlbyFI=
github.com/zeta-chain/keystone/keys v0.0.0-20231105174229-903bc9405da2 h1:gd2uE0X+ZbdFJ8DubxNqLbOVlCB12EgWdzSNRAR82tM=
github.com/zeta-chain/keystone/keys v0.0.0-20231105174229-903bc9405da2/go.mod h1:x7Bkwbzt2W2lQfjOirnff0Dj+tykdbTG1FMJPVPZsvE=
github.com/zeta-chain/protocol-contracts v1.0.2-athens3.0.20230816152528-db7d2bf9144b h1:aZRt5BtXdoDdyrUKwcv3B7mS30m/B854cjKjmnXBE5A=
Expand Down
5 changes: 1 addition & 4 deletions zetaclient/broadcast.go
Original file line number Diff line number Diff line change
Expand Up @@ -142,10 +142,7 @@ func (b *ZetaCoreBridge) GetContext() (client.Context, error) {
}

// if password is needed, set it as input
password, err := b.keys.GetHotkeyPassword()
if err != nil {
return ctx, err
}
password := b.keys.GetHotkeyPassword()
if password != "" {
ctx = ctx.WithInput(strings.NewReader(fmt.Sprintf("%[1]s\n%[1]s\n", password)))
}
Expand Down
51 changes: 12 additions & 39 deletions zetaclient/keys.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ package zetaclient

import (
"bytes"
"errors"
"fmt"
"io"
"os"
Expand All @@ -20,22 +19,21 @@ import (
"github.com/zeta-chain/zetacore/zetaclient/config"
)

// HotkeyPasswordEnvVar is the environment variable used to retrieve the password for the hotkey
const HotkeyPasswordEnvVar = "HOTKEY_PASSWORD"

// Keys manages all the keys used by zeta client
type Keys struct {
signerName string
kb ckeys.Keyring
OperatorAddress sdk.AccAddress
hotkeyPassword string
}

// NewKeysWithKeybase create a new instance of Keys
func NewKeysWithKeybase(kb ckeys.Keyring, granterAddress sdk.AccAddress, granteeName string) *Keys {
func NewKeysWithKeybase(kb ckeys.Keyring, granterAddress sdk.AccAddress, granteeName string, hotkeyPassword string) *Keys {
return &Keys{
signerName: granteeName,
kb: kb,
OperatorAddress: granterAddress,
hotkeyPassword: hotkeyPassword,
}
}

Expand All @@ -44,7 +42,7 @@ func GetGranteeKeyName(signerName string) string {
}

// GetKeyringKeybase return keyring and key info
func GetKeyringKeybase(cfg *config.Config) (ckeys.Keyring, string, error) {
func GetKeyringKeybase(cfg *config.Config, hotkeyPassword string) (ckeys.Keyring, string, error) {
granteeName := cfg.AuthzHotkey
chainHomeFolder := cfg.ZetaCoreHome
logger := log.Logger.With().Str("module", "GetKeyringKeybase").Logger()
Expand All @@ -55,13 +53,9 @@ func GetKeyringKeybase(cfg *config.Config) (ckeys.Keyring, string, error) {
// read password from env if using keyring backend file
buf := bytes.NewBufferString("")
if cfg.KeyringBackend == config.KeyringBackendFile {
password, err := getHotkeyPassword()
if err != nil {
return nil, "", err
}
buf.WriteString(password)
buf.WriteString(hotkeyPassword)
buf.WriteByte('\n') // the library used by keyring is using ReadLine , which expect a new line
buf.WriteString(password)
buf.WriteString(hotkeyPassword)
buf.WriteByte('\n')
}

Expand Down Expand Up @@ -137,12 +131,7 @@ func (k *Keys) GetAddress() sdk.AccAddress {
}

// GetPrivateKey return the private key
func (k *Keys) GetPrivateKey() (cryptotypes.PrivKey, error) {
password, err := k.GetHotkeyPassword()
if err != nil {
return nil, err
}

func (k *Keys) GetPrivateKey(password string) (cryptotypes.PrivKey, error) {
signer := GetGranteeKeyName(k.signerName)
privKeyArmor, err := k.kb.ExportPrivKeyArmor(signer, password)
if err != nil {
Expand All @@ -160,13 +149,13 @@ func (k *Keys) GetKeybase() ckeys.Keyring {
return k.kb
}

func (k *Keys) GetPubKeySet() (common.PubKeySet, error) {
func (k *Keys) GetPubKeySet(password string) (common.PubKeySet, error) {
pubkeySet := common.PubKeySet{
Secp256k1: "",
Ed25519: "",
}

pK, err := k.GetPrivateKey()
pK, err := k.GetPrivateKey(password)
if err != nil {
return pubkeySet, err
}
Expand All @@ -185,25 +174,9 @@ func (k *Keys) GetPubKeySet() (common.PubKeySet, error) {

// GetHotkeyPassword returns the password to be used
// returns empty if no password is needed
func (k *Keys) GetHotkeyPassword() (string, error) {
func (k *Keys) GetHotkeyPassword() string {
if k.GetKeybase().Backend() == ckeys.BackendFile {
return getHotkeyPassword()
}
return "", nil
}

// getHotkeyPassword retrieves the HOTKEY_PASSWORD environment variable
// and returns an error if it's not defined or shorter than 8 characters.
func getHotkeyPassword() (string, error) {
password := os.Getenv(HotkeyPasswordEnvVar)

if password == "" {
return "", errors.New("HOTKEY_PASSWORD environment variable is not defined, use --keyring-backend-test to use the test keyring")
return k.hotkeyPassword
}

if len(password) < 8 {
return "", errors.New("HOTKEY_PASSWORD should be at least 8 characters long")
}

return password, nil
return ""
}
Loading

0 comments on commit 62e7f7f

Please sign in to comment.