diff --git a/cmd/tendermint/commands/init.go b/cmd/tendermint/commands/init.go index 89f4724903..93f8cb5b86 100644 --- a/cmd/tendermint/commands/init.go +++ b/cmd/tendermint/commands/init.go @@ -3,6 +3,7 @@ package commands import ( "fmt" "os" + "path/filepath" ipfscfg "github.com/ipfs/go-ipfs-config" "github.com/ipfs/go-ipfs/plugin/loader" @@ -104,14 +105,14 @@ func initFilesWithConfig(config *cfg.Config) error { logger.Info("Generated genesis file", "path", genFile) } - if err := InitIpfs(config); err != nil { + if err := initIpfs(config); err != nil { return err } return nil } -func InitIpfs(config *cfg.Config) error { // add counter part in ResetAllCmd +func initIpfs(config *cfg.Config) error { // add counter part in ResetAllCmd // init IPFS config with params from config.IPFS // and store in config.IPFS.ConfigRootPath repoRoot := config.IPFSRepoRoot() @@ -125,7 +126,7 @@ func InitIpfs(config *cfg.Config) error { // add counter part in ResetAllCmd return err } - logger.Info("initializing IPFS node at:", repoRoot) + logger.Info("initializing IPFS node", "ipfs-path", repoRoot) if err := tmos.EnsureDir(repoRoot, 0700); err != nil { return err @@ -137,15 +138,7 @@ func InitIpfs(config *cfg.Config) error { // add counter part in ResetAllCmd } applyFromTmConfig(conf, config.IPFS) - plugins, err := loader.NewPluginLoader(repoRoot) - if err != nil { - return err - } - if err := plugins.Initialize(); err != nil { - return err - } - - if err := plugins.Inject(); err != nil { + if err := setupPlugins(repoRoot); err != nil { return err } @@ -153,11 +146,29 @@ func InitIpfs(config *cfg.Config) error { // add counter part in ResetAllCmd return err } } else { - logger.Info("IPFS was already initialized in %v", config.IPFS.ConfigRootPath) + logger.Info("IPFS was already initialized", "ipfs-path", repoRoot) } return nil } +func setupPlugins(path string) error { + // Load plugins. This will skip the repo if not available. + plugins, err := loader.NewPluginLoader(filepath.Join(path, "plugins")) + if err != nil { + return fmt.Errorf("error loading plugins: %s", err) + } + + if err := plugins.Initialize(); err != nil { + return fmt.Errorf("error initializing plugins: %s", err) + } + + if err := plugins.Inject(); err != nil { + return fmt.Errorf("error initializing plugins: %s", err) + } + + return nil +} + func applyFromTmConfig(ipfsConf *ipfscfg.Config, tmConf *cfg.IPFSConfig) { ipfsConf.Addresses.API = ipfscfg.Strings{tmConf.API} ipfsConf.Addresses.Gateway = ipfscfg.Strings{tmConf.Gateway} diff --git a/config/config.go b/config/config.go index fb5e3cee27..d4f2c84121 100644 --- a/config/config.go +++ b/config/config.go @@ -97,6 +97,7 @@ func TestConfig() *Config { Consensus: TestConsensusConfig(), TxIndex: TestTxIndexConfig(), Instrumentation: TestInstrumentationConfig(), + IPFS: TetsIpfsConfig(), } } @@ -1029,6 +1030,10 @@ func DefaultInstrumentationConfig() *InstrumentationConfig { } } +func TetsIpfsConfig() *IPFSConfig { + return DefaultIPFSConfig() +} + // TestInstrumentationConfig returns a default configuration for metrics // reporting. func TestInstrumentationConfig() *InstrumentationConfig { diff --git a/node/node.go b/node/node.go index 1dd8d06bdf..173529b923 100644 --- a/node/node.go +++ b/node/node.go @@ -8,12 +8,15 @@ import ( "net" "net/http" _ "net/http/pprof" // nolint: gosec // securely exposed on separate, optional port + "path/filepath" "strings" "time" ipfscore "github.com/ipfs/go-ipfs/core" "github.com/ipfs/go-ipfs/core/node/libp2p" + "github.com/ipfs/go-ipfs/plugin/loader" "github.com/ipfs/go-ipfs/repo/fsrepo" + "github.com/lazyledger/lazyledger-core/p2p/ipld/plugin/nodes" "github.com/prometheus/client_golang/prometheus" "github.com/prometheus/client_golang/prometheus/promhttp" "github.com/rs/cors" @@ -107,6 +110,7 @@ func DefaultNewNode(config *cfg.Config, logger log.Logger) (*Node, error) { DefaultDBProvider, DefaultMetricsProvider(config.Instrumentation), logger, + EmbedIpfsNode(true), ) } @@ -170,6 +174,22 @@ func StateProvider(stateProvider statesync.StateProvider) Option { } } +// IpfsPluginsWereLoaded indicates that all IPFS plugin were already loaded. +// Setting up plugins will skipped when creating the IPFS node. +func IpfsPluginsWereLoaded(wereAlreadyLoaded bool) Option { + return func(n *Node) { + n.areIfsPluginsAlreadyLoaded = wereAlreadyLoaded + } +} + +// IpfsPluginsWereLoaded indicates that all IPFS plugin were already loaded. +// Setting up plugins will skipped when creating the IPFS node. +func EmbedIpfsNode(embed bool) Option { + return func(n *Node) { + n.embedIpfsNode = embed + } +} + //------------------------------------------------------------------------------ // Node is the highest level interface to a full Tendermint node. @@ -211,7 +231,9 @@ type Node struct { indexerService *txindex.IndexerService prometheusSrv *http.Server // we store a ref to the full IpfsNode (instead of ipfs' CoreAPI) so we can Close() it OnStop() - ipfsNode *ipfscore.IpfsNode // ipfs node + embedIpfsNode bool // whether the node should start an IPFS node on startup + ipfsNode *ipfscore.IpfsNode // ipfs node + areIfsPluginsAlreadyLoaded bool // avoid injecting plugins twice in tests etc } func initDBs(config *cfg.Config, dbProvider DBProvider) (blockStore *store.BlockStore, stateDB dbm.DB, err error) { @@ -929,10 +951,11 @@ func (n *Node) OnStart() error { return fmt.Errorf("failed to start state sync: %w", err) } } - - n.ipfsNode, err = createIpfsNode(n.config) - if err != nil { - return fmt.Errorf("failed to create IPFS node: %w", err) + if n.embedIpfsNode { + n.ipfsNode, err = createIpfsNode(n.config, n.areIfsPluginsAlreadyLoaded, n.Logger) + if err != nil { + return fmt.Errorf("failed to create IPFS node: %w", err) + } } return nil @@ -1412,12 +1435,18 @@ func createAndStartPrivValidatorSocketClient( return pvscWithRetries, nil } -func createIpfsNode(config *cfg.Config) (*ipfscore.IpfsNode, error) { +func createIpfsNode(config *cfg.Config, arePluginsAlreadyLoaded bool, logger log.Logger) (*ipfscore.IpfsNode, error) { repoRoot := config.IPFSRepoRoot() + logger.Info("creating node in repo", "ipfs-root", repoRoot) if !fsrepo.IsInitialized(repoRoot) { // TODO: sentinel err return nil, fmt.Errorf("ipfs repo root: %v not intitialized", repoRoot) } + if !arePluginsAlreadyLoaded { + if err := setupPlugins(repoRoot, logger); err != nil { + return nil, err + } + } // Open the repo repo, err := fsrepo.Open(repoRoot) if err != nil { @@ -1441,10 +1470,31 @@ func createIpfsNode(config *cfg.Config) (*ipfscore.IpfsNode, error) { } // run as daemon: node.IsDaemon = true - return node, nil } +func setupPlugins(path string, logger log.Logger) error { + // Load plugins. This will skip the repo if not available. + plugins, err := loader.NewPluginLoader(filepath.Join(path, "plugins")) + if err != nil { + return fmt.Errorf("error loading plugins: %s", err) + } + plugins.Load(&nodes.LazyLedgerPlugin{}) + if err != nil { + return fmt.Errorf("error loading lazyledger plugin: %s", err) + } + + if err := plugins.Initialize(); err != nil { + return fmt.Errorf("error initializing plugins: plugins.Initialize(): %s", err) + } + + if err := plugins.Inject(); err != nil { + logger.Error("error initializing plugins: could not Inject()", "err", err) + } + + return nil +} + // splitAndTrimEmpty slices s into all subslices separated by sep and returns a // slice of the string s with all leading and trailing Unicode code points // contained in cutset removed. If sep is empty, SplitAndTrim splits after each diff --git a/node/node_test.go b/node/node_test.go index e42a4bbb6f..1ae6c9dd66 100644 --- a/node/node_test.go +++ b/node/node_test.go @@ -40,6 +40,7 @@ func TestNodeStartStop(t *testing.T) { // create & start node n, err := DefaultNewNode(config, log.TestingLogger()) + n.embedIpfsNode = false // TODO: or init ipfs upfront require.NoError(t, err) err = n.Start() require.NoError(t, err) @@ -103,6 +104,7 @@ func TestNodeDelayedStart(t *testing.T) { // create & start node n, err := DefaultNewNode(config, log.TestingLogger()) + n.embedIpfsNode = false // TODO: or init ipfs upfront n.GenesisDoc().GenesisTime = now.Add(2 * time.Second) require.NoError(t, err) diff --git a/rpc/client/examples_test.go b/rpc/client/examples_test.go index b243b15499..80389e991d 100644 --- a/rpc/client/examples_test.go +++ b/rpc/client/examples_test.go @@ -15,7 +15,7 @@ import ( func ExampleHTTP_simple() { // Start a tendermint node (and kvstore) in the background to test against app := kvstore.NewApplication() - node := rpctest.StartTendermint(app, rpctest.SuppressStdout, rpctest.RecreateConfig) + node := rpctest.StartTendermint(app, rpctest.SuppressStdout, rpctest.RecreateConfig, rpctest.DoNotLoadIpfsPlugins) defer rpctest.StopTendermint(node) // Create our RPC client @@ -68,7 +68,7 @@ func ExampleHTTP_simple() { func ExampleHTTP_batching() { // Start a tendermint node (and kvstore) in the background to test against app := kvstore.NewApplication() - node := rpctest.StartTendermint(app, rpctest.SuppressStdout, rpctest.RecreateConfig) + node := rpctest.StartTendermint(app, rpctest.SuppressStdout, rpctest.RecreateConfig, rpctest.DoNotLoadIpfsPlugins) // Create our RPC client rpcAddr := rpctest.GetConfig().RPC.ListenAddress diff --git a/rpc/test/helpers.go b/rpc/test/helpers.go index 288a7c2729..fbfb10c069 100644 --- a/rpc/test/helpers.go +++ b/rpc/test/helpers.go @@ -3,14 +3,20 @@ package rpctest import ( "context" "fmt" + "io/ioutil" "os" "path/filepath" "strings" "time" + ipfscfg "github.com/ipfs/go-ipfs-config" + "github.com/ipfs/go-ipfs/plugin/loader" + "github.com/ipfs/go-ipfs/repo/fsrepo" + "github.com/ipfs/interface-go-ipfs-core/options" abci "github.com/lazyledger/lazyledger-core/abci/types" - cmd "github.com/lazyledger/lazyledger-core/cmd/tendermint/commands" "github.com/lazyledger/lazyledger-core/libs/log" + tmos "github.com/lazyledger/lazyledger-core/libs/os" + "github.com/lazyledger/lazyledger-core/p2p/ipld/plugin/nodes" cfg "github.com/lazyledger/lazyledger-core/config" tmnet "github.com/lazyledger/lazyledger-core/libs/net" @@ -26,14 +32,16 @@ import ( // Options helps with specifying some parameters for our RPC testing for greater // control. type Options struct { - suppressStdout bool - recreateConfig bool + suppressStdout bool + recreateConfig bool + loadIpfsPlugins bool } var globalConfig *cfg.Config var defaultOptions = Options{ - suppressStdout: false, - recreateConfig: false, + suppressStdout: false, + recreateConfig: false, + loadIpfsPlugins: true, } func waitForRPC() { @@ -151,7 +159,6 @@ func StopTendermint(node *nm.Node) { // NewTendermint creates a new tendermint server and sleeps forever func NewTendermint(app abci.Application, opts *Options) *nm.Node { - // TODO init ipfs // Create & start node config := GetConfig(opts.recreateConfig) var logger log.Logger @@ -174,7 +181,7 @@ func NewTendermint(app abci.Application, opts *Options) *nm.Node { } config.IPFS = cfg.DefaultIPFSConfig() - err = cmd.InitIpfs(config) + err = initIpfs(config, opts.loadIpfsPlugins, logger) if err != nil { panic(err) } @@ -182,13 +189,63 @@ func NewTendermint(app abci.Application, opts *Options) *nm.Node { nm.DefaultGenesisDocProviderFunc(config), nm.DefaultDBProvider, nm.DefaultMetricsProvider(config.Instrumentation), - logger) + logger, + nm.IpfsPluginsWereLoaded(true), + ) if err != nil { panic(err) } return node } +func initIpfs(config *cfg.Config, loadPlugins bool, log log.Logger) error { // add counter part in ResetAllCmd + // init IPFS config with params from config.IPFS + // and store in config.IPFS.ConfigRootPath + repoRoot := config.IPFSRepoRoot() + if !fsrepo.IsInitialized(repoRoot) { + var conf *ipfscfg.Config + + identity, err := ipfscfg.CreateIdentity(ioutil.Discard, []options.KeyGenerateOption{ + options.Key.Type(options.Ed25519Key), + }) + if err != nil { + return err + } + + if err := tmos.EnsureDir(repoRoot, 0700); err != nil { + return err + } + if loadPlugins { + plugins, err := loader.NewPluginLoader(filepath.Join(repoRoot, "plugins")) + if err != nil { + return fmt.Errorf("error loading plugins: %s", err) + } + if err := plugins.Load(&nodes.LazyLedgerPlugin{}); err != nil { + return err + } + + if err := plugins.Initialize(); err != nil { + return fmt.Errorf("error initializing plugins: %s", err) + } + + if err := plugins.Inject(); err != nil { + return fmt.Errorf("error initializing plugins: %s", err) + } + } + conf, err = ipfscfg.InitWithIdentity(identity) + if err != nil { + return fmt.Errorf("InitWithIdentity(): %w", err) + } + + if err := fsrepo.Init(repoRoot, conf); err != nil { + return err + } + } else { + log.Info("ipfs repo already initialized", "repo-root", repoRoot) + } + return nil +} + // SuppressStdout is an option that tries to make sure the RPC test Tendermint // node doesn't log anything to stdout. func SuppressStdout(o *Options) { @@ -200,3 +257,8 @@ func SuppressStdout(o *Options) { func RecreateConfig(o *Options) { o.recreateConfig = true } + +// DoNotLoadIpfsPlugins +func DoNotLoadIpfsPlugins(o *Options) { + o.loadIpfsPlugins = false +}