diff --git a/cli/options/options.go b/cli/options/options.go index 09bb08572c..82d6f7d4f1 100644 --- a/cli/options/options.go +++ b/cli/options/options.go @@ -73,6 +73,13 @@ var ConfigFile = cli.StringFlag{ Usage: "path to the node configuration file (overrides --config-path option)", } +// RelativePath is a flag for commands that use node configuration and provide +// a prefix to all relative paths in config files. +var RelativePath = cli.StringFlag{ + Name: "relative-path", + Usage: "a prefix to all relative paths in config files", +} + // Debug is a flag for commands that allow node in debug mode usage. var Debug = cli.BoolFlag{ Name: "debug, d", @@ -160,14 +167,15 @@ func GetRPCWithInvoker(gctx context.Context, ctx *cli.Context, signers []transac // returns an appropriate config. func GetConfigFromContext(ctx *cli.Context) (config.Config, error) { var configFile = ctx.String("config-file") + var relativePath = ctx.String("relative-path") if len(configFile) != 0 { - return config.LoadFile(configFile) + return config.LoadFile(configFile, relativePath) } var configPath = "./config" if argCp := ctx.String("config-path"); argCp != "" { configPath = argCp } - return config.Load(configPath, GetNetwork(ctx)) + return config.Load(configPath, GetNetwork(ctx), relativePath) } var ( diff --git a/cli/server/cli_dump_test.go b/cli/server/cli_dump_test.go index f7756add53..d3b66ca619 100644 --- a/cli/server/cli_dump_test.go +++ b/cli/server/cli_dump_test.go @@ -21,7 +21,7 @@ func TestDBRestoreDump(t *testing.T) { loadConfig := func(t *testing.T) config.Config { chainPath := filepath.Join(tmpDir, "neogotestchain") - cfg, err := config.LoadFile(filepath.Join("..", "..", "config", "protocol.unit_testnet.yml")) + cfg, err := config.LoadFile(filepath.Join("..", "..", "config", "protocol.unit_testnet.yml"), "") require.NoError(t, err, "could not load config") cfg.ApplicationConfiguration.DBConfiguration.Type = dbconfig.LevelDB cfg.ApplicationConfiguration.DBConfiguration.LevelDBOptions.DataDirectoryPath = chainPath @@ -119,7 +119,7 @@ func TestDBDumpRestoreIncremental(t *testing.T) { nonincDump := filepath.Join(tmpDir, "nonincDump.acc") incDump := filepath.Join(tmpDir, "incDump.acc") - cfg, err := config.LoadFile(filepath.Join("..", "..", "config", "protocol.unit_testnet.yml")) + cfg, err := config.LoadFile(filepath.Join("..", "..", "config", "protocol.unit_testnet.yml"), "") require.NoError(t, err, "could not load config") cfg.ApplicationConfiguration.DBConfiguration.Type = dbconfig.LevelDB cfg.ApplicationConfiguration.DBConfiguration.LevelDBOptions.DataDirectoryPath = chainPath diff --git a/cli/server/cli_server_test.go b/cli/server/cli_server_test.go index 927b138ec6..56a76233a0 100644 --- a/cli/server/cli_server_test.go +++ b/cli/server/cli_server_test.go @@ -20,7 +20,7 @@ import ( func TestServerStart(t *testing.T) { tmpDir := t.TempDir() - goodCfg, err := config.LoadFile(filepath.Join("..", "..", "config", "protocol.unit_testnet.yml")) + goodCfg, err := config.LoadFile(filepath.Join("..", "..", "config", "protocol.unit_testnet.yml"), "") require.NoError(t, err, "could not load config") ptr := &goodCfg saveCfg := func(t *testing.T, f func(cfg *config.Config)) string { diff --git a/cli/server/server.go b/cli/server/server.go index 7e705d433b..f2670d599d 100644 --- a/cli/server/server.go +++ b/cli/server/server.go @@ -34,7 +34,7 @@ import ( // NewCommands returns 'node' command. func NewCommands() []cli.Command { - cfgFlags := []cli.Flag{options.Config, options.ConfigFile} + cfgFlags := []cli.Flag{options.Config, options.ConfigFile, options.RelativePath} cfgFlags = append(cfgFlags, options.Network...) var cfgWithCountFlags = make([]cli.Flag, len(cfgFlags)) copy(cfgWithCountFlags, cfgFlags) diff --git a/cli/server/server_test.go b/cli/server/server_test.go index 2757ae6bb2..ae00682ace 100644 --- a/cli/server/server_test.go +++ b/cli/server/server_test.go @@ -49,6 +49,16 @@ func TestGetConfigFromContext(t *testing.T) { require.NoError(t, err) require.Equal(t, netmode.TestNet, cfg.ProtocolConfiguration.Magic) }) + t.Run("relative-path", func(t *testing.T) { + set := flag.NewFlagSet("flagSet", flag.ExitOnError) + set.String("relative-path", "../../config", "") + set.Bool("testnet", true, "") + set.String("config-file", "../../config/protocol.testnet.yml", "") + ctx := cli.NewContext(cli.NewApp(), set, nil) + cfg, err := options.GetConfigFromContext(ctx) + require.NoError(t, err) + require.Equal(t, "../../config/chains/testnet", cfg.ApplicationConfiguration.DBConfiguration.LevelDBOptions.DataDirectoryPath) + }) } func TestHandleLoggingParams(t *testing.T) { @@ -233,7 +243,7 @@ func TestRestoreDB(t *testing.T) { badCfgDir := t.TempDir() logfile := filepath.Join(badCfgDir, "logdir") require.NoError(t, os.WriteFile(logfile, []byte{1, 2, 3}, os.ModePerm)) - cfg, err := config.LoadFile(filepath.Join(goodCfg, "protocol.privnet.yml")) + cfg, err := config.LoadFile(filepath.Join(goodCfg, "protocol.privnet.yml"), "") require.NoError(t, err, "could not load config") cfg.ApplicationConfiguration.LogPath = filepath.Join(logfile, "file.log") out, err := yaml.Marshal(cfg) @@ -249,7 +259,7 @@ func TestRestoreDB(t *testing.T) { }) t.Run("invalid bc config", func(t *testing.T) { badCfgDir := t.TempDir() - cfg, err := config.LoadFile(filepath.Join(goodCfg, "protocol.privnet.yml")) + cfg, err := config.LoadFile(filepath.Join(goodCfg, "protocol.privnet.yml"), "") require.NoError(t, err, "could not load config") cfg.ApplicationConfiguration.DBConfiguration.Type = "" out, err := yaml.Marshal(cfg) diff --git a/cli/smartcontract/testdata/rpcbindings/notifications/rpcbindings_guessed.out b/cli/smartcontract/testdata/rpcbindings/notifications/rpcbindings_guessed.out index b71c3d1033..80a864e491 100755 --- a/cli/smartcontract/testdata/rpcbindings/notifications/rpcbindings_guessed.out +++ b/cli/smartcontract/testdata/rpcbindings/notifications/rpcbindings_guessed.out @@ -82,7 +82,6 @@ type LedgerWitnessRule struct { // Unnamed is a contract-specific unnamed type used by its methods. type Unnamed struct { I *big.Int - B bool } // ComplicatedNameEvent represents "! complicated name %$#" event emitted by the contract. @@ -822,7 +821,7 @@ func (res *Unnamed) FromStackItem(item stackitem.Item) error { if !ok { return errors.New("not an array") } - if len(arr) != 2 { + if len(arr) != 1 { return errors.New("wrong number of structure elements") } @@ -836,12 +835,6 @@ func (res *Unnamed) FromStackItem(item stackitem.Item) error { return fmt.Errorf("field I: %w", err) } - index++ - res.B, err = arr[index].TryBool() - if err != nil { - return fmt.Errorf("field B: %w", err) - } - return nil } diff --git a/cli/vm/cli_test.go b/cli/vm/cli_test.go index 8e7d22887b..a7c4565565 100644 --- a/cli/vm/cli_test.go +++ b/cli/vm/cli_test.go @@ -95,8 +95,9 @@ func newTestVMCLIWithLogoAndCustomConfig(t *testing.T, printLogo bool, cfg *conf if cfg == nil { configPath := "../../config/protocol.unit_testnet.single.yml" var err error - c, err = config.LoadFile(configPath) + c, err = config.LoadFile(configPath, "../../config") require.NoError(t, err, "could not load chain config") + require.Equal(t, "../../testdata/wallet1_solo.json", c.ApplicationConfiguration.Consensus.UnlockWallet.Path) c.ApplicationConfiguration.DBConfiguration.Type = dbconfig.InMemoryDB } else { c = *cfg @@ -140,7 +141,7 @@ func newTestVMClIWithState(t *testing.T) *executor { // After that create CLI backed by created chain. configPath := "../../config/protocol.unit_testnet.yml" - cfg, err := config.LoadFile(configPath) + cfg, err := config.LoadFile(configPath, "") require.NoError(t, err) cfg.ApplicationConfiguration.DBConfiguration.Type = dbconfig.LevelDB cfg.ApplicationConfiguration.DBConfiguration.LevelDBOptions = opts diff --git a/cli/vm/vm.go b/cli/vm/vm.go index 89dd351bb9..e9e1e2a585 100644 --- a/cli/vm/vm.go +++ b/cli/vm/vm.go @@ -13,7 +13,7 @@ import ( // NewCommands returns 'vm' command. func NewCommands() []cli.Command { - cfgFlags := []cli.Flag{options.Config, options.ConfigFile} + cfgFlags := []cli.Flag{options.Config, options.ConfigFile, options.RelativePath} cfgFlags = append(cfgFlags, options.Network...) return []cli.Command{{ Name: "vm", diff --git a/internal/testcli/executor.go b/internal/testcli/executor.go index d6cc9da1e8..e812b4b2fa 100644 --- a/internal/testcli/executor.go +++ b/internal/testcli/executor.go @@ -131,7 +131,7 @@ func (w *ConcurrentBuffer) Reset() { func NewTestChain(t *testing.T, f func(*config.Config), run bool) (*core.Blockchain, *rpcsrv.Server, *network.Server) { configPath := "../../config/protocol.unit_testnet.single.yml" - cfg, err := config.LoadFile(configPath) + cfg, err := config.LoadFile(configPath, "") require.NoError(t, err, "could not load config") if f != nil { f(&cfg) diff --git a/pkg/config/application_config_test.go b/pkg/config/application_config_test.go index 83ea488e5f..bdf8cd562a 100644 --- a/pkg/config/application_config_test.go +++ b/pkg/config/application_config_test.go @@ -14,9 +14,9 @@ func TestApplicationConfigurationEquals(t *testing.T) { require.True(t, o.EqualsButServices(a)) require.True(t, a.EqualsButServices(a)) - cfg1, err := LoadFile(filepath.Join("..", "..", "config", "protocol.mainnet.yml")) + cfg1, err := LoadFile(filepath.Join("..", "..", "config", "protocol.mainnet.yml"), "") require.NoError(t, err) - cfg2, err := LoadFile(filepath.Join("..", "..", "config", "protocol.testnet.yml")) + cfg2, err := LoadFile(filepath.Join("..", "..", "config", "protocol.testnet.yml"), "") require.NoError(t, err) require.False(t, cfg1.ApplicationConfiguration.EqualsButServices(&cfg2.ApplicationConfiguration)) } diff --git a/pkg/config/config.go b/pkg/config/config.go index 4e8fe06bd6..6eaee51f4b 100644 --- a/pkg/config/config.go +++ b/pkg/config/config.go @@ -3,6 +3,7 @@ package config import ( "fmt" "os" + "path/filepath" "time" "github.com/nspcc-dev/neo-go/pkg/config/netmode" @@ -57,14 +58,14 @@ func (c Config) Blockchain() Blockchain { // Load attempts to load the config from the given // path for the given netMode. -func Load(path string, netMode netmode.Magic) (Config, error) { +func Load(path string, netMode netmode.Magic, relativePath string) (Config, error) { configPath := fmt.Sprintf("%s/protocol.%s.yml", path, netMode) - return LoadFile(configPath) + return LoadFile(configPath, relativePath) } // LoadFile loads config from the provided path. It also applies backwards compatibility // fixups if necessary. -func LoadFile(configPath string) (Config, error) { +func LoadFile(configPath string, relativePath string) (Config, error) { if _, err := os.Stat(configPath); os.IsNotExist(err) { return Config{}, fmt.Errorf("config '%s' doesn't exist", configPath) } @@ -88,6 +89,10 @@ func LoadFile(configPath string) (Config, error) { return Config{}, fmt.Errorf("failed to unmarshal config YAML: %w", err) } + if relativePath != "" { + updateRelativePaths(relativePath, &config) + } + err = config.ProtocolConfiguration.Validate() if err != nil { return Config{}, err @@ -95,3 +100,20 @@ func LoadFile(configPath string) (Config, error) { return config, nil } + +// updateRelativePaths updates relative paths in the config structure based on the provided relative path. +func updateRelativePaths(relativePath string, config *Config) { + updatePath := func(path *string) { + if *path != "" && !filepath.IsAbs(*path) { + *path = filepath.Join(relativePath, *path) + } + } + + updatePath(&config.ApplicationConfiguration.LogPath) + updatePath(&config.ApplicationConfiguration.DBConfiguration.BoltDBOptions.FilePath) + updatePath(&config.ApplicationConfiguration.DBConfiguration.LevelDBOptions.DataDirectoryPath) + updatePath(&config.ApplicationConfiguration.Consensus.UnlockWallet.Path) + updatePath(&config.ApplicationConfiguration.P2PNotary.UnlockWallet.Path) + updatePath(&config.ApplicationConfiguration.Oracle.UnlockWallet.Path) + updatePath(&config.ApplicationConfiguration.StateRoot.UnlockWallet.Path) +} diff --git a/pkg/config/config_test.go b/pkg/config/config_test.go index 16a59b3173..4020047e1b 100644 --- a/pkg/config/config_test.go +++ b/pkg/config/config_test.go @@ -9,6 +9,6 @@ import ( const testConfigPath = "./testdata/protocol.test.yml" func TestUnexpectedNativeUpdateHistoryContract(t *testing.T) { - _, err := LoadFile(testConfigPath) + _, err := LoadFile(testConfigPath, "") require.Error(t, err) } diff --git a/pkg/config/protocol_config_test.go b/pkg/config/protocol_config_test.go index 1b17fec494..cb56475626 100644 --- a/pkg/config/protocol_config_test.go +++ b/pkg/config/protocol_config_test.go @@ -217,13 +217,13 @@ func TestProtocolConfigurationEquals(t *testing.T) { require.True(t, o.Equals(p)) require.True(t, p.Equals(p)) - cfg1, err := LoadFile(filepath.Join("..", "..", "config", "protocol.mainnet.yml")) + cfg1, err := LoadFile(filepath.Join("..", "..", "config", "protocol.mainnet.yml"), "") require.NoError(t, err) - cfg2, err := LoadFile(filepath.Join("..", "..", "config", "protocol.testnet.yml")) + cfg2, err := LoadFile(filepath.Join("..", "..", "config", "protocol.testnet.yml"), "") require.NoError(t, err) require.False(t, cfg1.ProtocolConfiguration.Equals(&cfg2.ProtocolConfiguration)) - cfg2, err = LoadFile(filepath.Join("..", "..", "config", "protocol.mainnet.yml")) + cfg2, err = LoadFile(filepath.Join("..", "..", "config", "protocol.mainnet.yml"), "") require.NoError(t, err) p = &cfg1.ProtocolConfiguration o = &cfg2.ProtocolConfiguration diff --git a/pkg/consensus/consensus_test.go b/pkg/consensus/consensus_test.go index 07eeb99edd..b63ed7dff6 100644 --- a/pkg/consensus/consensus_test.go +++ b/pkg/consensus/consensus_test.go @@ -541,7 +541,7 @@ func getTestValidator(i int) (*privateKey, *publicKey) { func newSingleTestChain(t *testing.T) *core.Blockchain { configPath := "../../config/protocol.unit_testnet.single.yml" - cfg, err := config.LoadFile(configPath) + cfg, err := config.LoadFile(configPath, "") require.NoError(t, err, "could not load config") chain, err := core.NewBlockchain(storage.NewMemoryStore(), cfg.Blockchain(), zaptest.NewLogger(t)) @@ -553,7 +553,7 @@ func newSingleTestChain(t *testing.T) *core.Blockchain { } func newTestChain(t *testing.T, stateRootInHeader bool) *core.Blockchain { - unitTestNetCfg, err := config.Load("../../config", netmode.UnitTestNet) + unitTestNetCfg, err := config.Load("../../config", netmode.UnitTestNet, "") require.NoError(t, err) unitTestNetCfg.ProtocolConfiguration.StateRootInHeader = stateRootInHeader diff --git a/pkg/core/blockchain_core_test.go b/pkg/core/blockchain_core_test.go index 0e7f6bff5e..6b6ff541a4 100644 --- a/pkg/core/blockchain_core_test.go +++ b/pkg/core/blockchain_core_test.go @@ -185,7 +185,7 @@ func TestBlockchain_InitWithIncompleteStateJump(t *testing.T) { require.NoError(t, err) checkNewBlockchainErr := func(t *testing.T, cfg func(c *config.Config), store storage.Store, errText string) { - unitTestNetCfg, err := config.Load("../../config", testchain.Network()) + unitTestNetCfg, err := config.Load("../../config", testchain.Network(), "") require.NoError(t, err) cfg(&unitTestNetCfg) log := zaptest.NewLogger(t) diff --git a/pkg/core/blockchain_neotest_test.go b/pkg/core/blockchain_neotest_test.go index 1598b7e867..1b944a0f04 100644 --- a/pkg/core/blockchain_neotest_test.go +++ b/pkg/core/blockchain_neotest_test.go @@ -1124,7 +1124,7 @@ func TestConfigNativeUpdateHistory(t *testing.T) { var prefixPath = filepath.Join("..", "..", "config") check := func(t *testing.T, cfgFileSuffix any) { cfgPath := filepath.Join(prefixPath, fmt.Sprintf("protocol.%s.yml", cfgFileSuffix)) - cfg, err := config.LoadFile(cfgPath) + cfg, err := config.LoadFile(cfgPath, "") require.NoError(t, err, fmt.Errorf("failed to load %s", cfgPath)) natives := native.NewContracts(cfg.ProtocolConfiguration) assert.Equal(t, len(natives.Contracts), diff --git a/pkg/core/helper_test.go b/pkg/core/helper_test.go index a2466d6e59..55c1fc5a7b 100644 --- a/pkg/core/helper_test.go +++ b/pkg/core/helper_test.go @@ -40,7 +40,7 @@ func initTestChain(t testing.TB, st storage.Store, f func(*config.Config)) *Bloc } func initTestChainNoCheck(t testing.TB, st storage.Store, f func(*config.Config)) (*Blockchain, error) { - unitTestNetCfg, err := config.Load("../../config", testchain.Network()) + unitTestNetCfg, err := config.Load("../../config", testchain.Network(), "") require.NoError(t, err) if f != nil { f(&unitTestNetCfg) diff --git a/pkg/core/interop/runtime/ext_test.go b/pkg/core/interop/runtime/ext_test.go index c0764fd9f4..ed69b0a948 100644 --- a/pkg/core/interop/runtime/ext_test.go +++ b/pkg/core/interop/runtime/ext_test.go @@ -57,7 +57,7 @@ func getSharpTestTx(sender util.Uint160) *transaction.Transaction { func getSharpTestGenesis(t *testing.T) *block.Block { const configPath = "../../../../config" - cfg, err := config.Load(configPath, netmode.MainNet) + cfg, err := config.Load(configPath, netmode.MainNet, "") require.NoError(t, err) b, err := core.CreateGenesisBlock(cfg.ProtocolConfiguration) require.NoError(t, err) diff --git a/pkg/core/util_test.go b/pkg/core/util_test.go index 6becf5ff09..be4e8a5841 100644 --- a/pkg/core/util_test.go +++ b/pkg/core/util_test.go @@ -11,7 +11,7 @@ import ( ) func TestGenesisBlockMainNet(t *testing.T) { - cfg, err := config.Load("../../config", netmode.MainNet) + cfg, err := config.Load("../../config", netmode.MainNet, "") require.NoError(t, err) block, err := CreateGenesisBlock(cfg.ProtocolConfiguration) @@ -27,7 +27,7 @@ func TestGetConsensusAddressMainNet(t *testing.T) { consensusScript = "6b123dd8bec718648852bbc78595e3536a058f9f" ) - cfg, err := config.Load("../../config", netmode.MainNet) + cfg, err := config.Load("../../config", netmode.MainNet, "") require.NoError(t, err) validators, _, err := validatorsFromConfig(cfg.ProtocolConfiguration) diff --git a/pkg/services/rpcsrv/server_helper_test.go b/pkg/services/rpcsrv/server_helper_test.go index e0a1ef68c0..e43c10cf68 100644 --- a/pkg/services/rpcsrv/server_helper_test.go +++ b/pkg/services/rpcsrv/server_helper_test.go @@ -59,7 +59,7 @@ func getUnitTestChain(t testing.TB, enableOracle bool, enableNotary bool, disabl func getUnitTestChainWithCustomConfig(t testing.TB, enableOracle bool, enableNotary bool, customCfg func(configuration *config.Config)) (*core.Blockchain, OracleHandler, config.Config, *zap.Logger) { net := netmode.UnitTestNet configPath := "../../../config" - cfg, err := config.Load(configPath, net) + cfg, err := config.Load(configPath, net, "") require.NoError(t, err, "could not load config") if customCfg != nil { customCfg(&cfg) diff --git a/scripts/gendump/main.go b/scripts/gendump/main.go index f211593836..997c958ef1 100644 --- a/scripts/gendump/main.go +++ b/scripts/gendump/main.go @@ -128,7 +128,7 @@ func handleError(msg string, err error) { } func newChain() (*core.Blockchain, error) { - unitTestNetCfg, err := config.Load("./config", netmode.UnitTestNet) + unitTestNetCfg, err := config.Load("./config", netmode.UnitTestNet, "") if err != nil { return nil, err }