diff --git a/daemon/kmd/config/config.go b/daemon/kmd/config/config.go index 57a12ba5eb..235935950a 100644 --- a/daemon/kmd/config/config.go +++ b/daemon/kmd/config/config.go @@ -69,6 +69,11 @@ type ScryptParams struct { ScryptP int `json:"scrypt_p"` } +// DefaultConfig returns the default KMDConfig +func DefaultConfig(dataDir string) KMDConfig { + return defaultConfig(dataDir) +} + // defaultConfig returns the default KMDConfig func defaultConfig(dataDir string) KMDConfig { return KMDConfig{ @@ -121,3 +126,14 @@ func LoadKMDConfig(dataDir string) (cfg KMDConfig, err error) { err = cfg.Validate() return } + +// SaveKMDConfig writes the kmd configuration to disk +func SaveKMDConfig(dataDir string, cfg KMDConfig) error { + err := cfg.Validate() + if err != nil { + return err + } + configFilename := filepath.Join(dataDir, kmdConfigFilename) + + return codecs.SaveObjectToFile(configFilename, cfg, true) +} diff --git a/netdeploy/network.go b/netdeploy/network.go index 6951969031..ec93b3800e 100644 --- a/netdeploy/network.go +++ b/netdeploy/network.go @@ -78,6 +78,13 @@ func OverrideConsensusVersion(ver protocol.ConsensusVersion) TemplateOverride { } } +// OverrideKmdConfig changes the KMD config. +func OverrideKmdConfig(kmdConfig TemplateKMDConfig) TemplateOverride { + return func(template *NetworkTemplate) { + template.kmdConfig = kmdConfig + } +} + // CreateNetworkFromTemplate uses the specified template to deploy a new private network // under the specified root directory. func CreateNetworkFromTemplate(name, rootDir string, templateReader io.Reader, binDir string, importKeys bool, nodeExitCallback nodecontrol.AlgodExitErrorCallback, consensus config.ConsensusProtocols, overrides ...TemplateOverride) (Network, error) { diff --git a/netdeploy/networkTemplate.go b/netdeploy/networkTemplate.go index 865edf6ce5..92a7691e16 100644 --- a/netdeploy/networkTemplate.go +++ b/netdeploy/networkTemplate.go @@ -29,6 +29,7 @@ import ( "github.com/algorand/go-algorand/config" "github.com/algorand/go-algorand/crypto" + kmdconfig "github.com/algorand/go-algorand/daemon/kmd/config" "github.com/algorand/go-algorand/data/bookkeeping" "github.com/algorand/go-algorand/gen" "github.com/algorand/go-algorand/libgoal" @@ -42,6 +43,20 @@ type NetworkTemplate struct { Genesis gen.GenesisData Nodes []remote.NodeConfigGoal Consensus config.ConsensusProtocols + kmdConfig TemplateKMDConfig // set by OverrideKmdConfig +} + +// TemplateKMDConfig is a subset of the kmd configuration that can be overridden in the network template +// by using OverrideKmdConfig TemplateOverride opts. +// The reason why config.KMDConfig cannot be used directly is that it contains DataDir field which is +// is not known until the template instantiation. +type TemplateKMDConfig struct { + SessionLifetimeSecs uint64 +} + +func (c TemplateKMDConfig) apply(cfg kmdconfig.KMDConfig) kmdconfig.KMDConfig { + cfg.SessionLifetimeSecs = c.SessionLifetimeSecs + return cfg } var defaultNetworkTemplate = NetworkTemplate{ @@ -131,10 +146,27 @@ func (t NetworkTemplate) createNodeDirectories(targetFolder string, binDir strin } } + var kmdDir string + if (t.kmdConfig != TemplateKMDConfig{}) { + kmdDir = filepath.Join(nodeDir, libgoal.DefaultKMDDataDir) + err = os.MkdirAll(kmdDir, 0700) // kmd requires 700 permissions + if err != nil { + return + } + err = createKMDConfigFile(t.kmdConfig, kmdDir) + if err != nil { + return + } + } + if importKeys && hasWallet { var client libgoal.Client - client, err = libgoal.MakeClientWithBinDir(binDir, nodeDir, "", libgoal.KmdClient) - if err != nil { + if client, err = libgoal.MakeClientFromConfig(libgoal.ClientConfig{ + AlgodDataDir: nodeDir, + KMDDataDir: kmdDir, + CacheDir: "", + BinDir: binDir, + }, libgoal.KmdClient); err != nil { return } _, err = client.CreateWallet(libgoal.UnencryptedWalletName, nil, crypto.MasterDerivationKey{}) @@ -241,12 +273,12 @@ func (t NetworkTemplate) Validate() error { return fmt.Errorf("invalid template: at least one relay is required when more than a single node presents") } - // Validate JSONOverride decoding + // Validate ConfigJSONOverride decoding for _, cfg := range t.Nodes { local := config.GetDefaultLocal() err := decodeJSONOverride(cfg.ConfigJSONOverride, &local) if err != nil { - return fmt.Errorf("invalid template: unable to decode JSONOverride: %w", err) + return fmt.Errorf("invalid template: unable to decode ConfigJSONOverride: %w", err) } } @@ -293,7 +325,7 @@ func countRelayNodes(nodeCfgs []remote.NodeConfigGoal) (relayCount int) { return } -func decodeJSONOverride(override string, cfg *config.Local) error { +func decodeJSONOverride[T any](override string, cfg *T) error { if override != "" { reader := strings.NewReader(override) dec := json.NewDecoder(reader) @@ -340,3 +372,8 @@ func createConfigFile(node remote.NodeConfigGoal, configFile string, numNodes in return cfg, cfg.SaveToFile(configFile) } + +func createKMDConfigFile(kmdConfig TemplateKMDConfig, kmdDir string) error { + cfg := kmdConfig.apply(kmdconfig.DefaultConfig(kmdDir)) + return kmdconfig.SaveKMDConfig(kmdDir, cfg) +} diff --git a/netdeploy/networkTemplates_test.go b/netdeploy/networkTemplates_test.go index 130667f036..13aff8d842 100644 --- a/netdeploy/networkTemplates_test.go +++ b/netdeploy/networkTemplates_test.go @@ -235,7 +235,7 @@ func TestDevModeValidate(t *testing.T) { }, }, } - require.ErrorContains(t, tmpl.Validate(), "unable to decode JSONOverride") + require.ErrorContains(t, tmpl.Validate(), "unable to decode ConfigJSONOverride") }) t.Run("ConfigJSONOverride unknown key", func(t *testing.T) { diff --git a/test/framework/fixtures/libgoalFixture.go b/test/framework/fixtures/libgoalFixture.go index c6659b33be..9af69b3c8a 100644 --- a/test/framework/fixtures/libgoalFixture.go +++ b/test/framework/fixtures/libgoalFixture.go @@ -127,7 +127,15 @@ func (f *LibGoalFixture) setup(test TestingTB, testName string, templateFile str importKeys := false // Don't automatically import root keys when creating folders, we'll import on-demand file, err := os.Open(templateFile) f.failOnError(err, "Template file could not be opened: %v") - network, err := netdeploy.CreateNetworkFromTemplate("test", f.rootDir, file, f.binDir, importKeys, f.nodeExitWithError, f.consensus, overrides...) + defer file.Close() + + // Override the kmd session lifetime to 5 minutes to prevent kmd wallet handles from expiring + kmdConfOverride := netdeploy.OverrideKmdConfig(netdeploy.TemplateKMDConfig{SessionLifetimeSecs: 300}) + // copy overrides to prevent caller's data from being modified + extraOverrides := append([]netdeploy.TemplateOverride(nil), overrides...) + extraOverrides = append(extraOverrides, kmdConfOverride) + + network, err := netdeploy.CreateNetworkFromTemplate("test", f.rootDir, file, f.binDir, importKeys, f.nodeExitWithError, f.consensus, extraOverrides...) f.failOnError(err, "CreateNetworkFromTemplate failed: %v") f.network = network