package node import ( "context" "crypto/rand" "flag" "fmt" "io/ioutil" "os" "path" "path/filepath" "runtime" types2 "github.com/Secured-Finance/dione/blockchain/types" "github.com/Secured-Finance/dione/rpc" "github.com/Secured-Finance/dione/rpc/filecoin" solana2 "github.com/Secured-Finance/dione/rpc/solana" rtypes "github.com/Secured-Finance/dione/rpc/types" "github.com/sirupsen/logrus" "github.com/asaskevich/EventBus" "github.com/Secured-Finance/dione/blockchain" drand2 "github.com/Secured-Finance/dione/beacon/drand" "github.com/libp2p/go-libp2p-core/protocol" gorpc "github.com/libp2p/go-libp2p-gorpc" "github.com/Secured-Finance/dione/blockchain/sync" "github.com/Secured-Finance/dione/blockchain/pool" "github.com/Secured-Finance/dione/cache" "github.com/Secured-Finance/dione/config" "github.com/Secured-Finance/dione/consensus" "github.com/Secured-Finance/dione/ethclient" "github.com/Secured-Finance/dione/pubsub" "github.com/Secured-Finance/dione/types" "github.com/Secured-Finance/dione/wallet" pex "github.com/Secured-Finance/go-libp2p-pex" "github.com/libp2p/go-libp2p" "github.com/libp2p/go-libp2p-core/crypto" "github.com/libp2p/go-libp2p-core/discovery" "github.com/libp2p/go-libp2p-core/host" "github.com/libp2p/go-libp2p-core/peer" "github.com/multiformats/go-multiaddr" "golang.org/x/xerrors" ) const ( DioneProtocolID = protocol.ID("/dione/1.0") ) func provideCache(config *config.Config) cache.Cache { var backend cache.Cache switch config.CacheType { case "in-memory": backend = cache.NewInMemoryCache() case "redis": backend = cache.NewRedisCache(config) default: backend = cache.NewInMemoryCache() } return backend } func provideDrandBeacon(ps *pubsub.PubSubRouter, bus EventBus.Bus) *drand2.DrandBeacon { db, err := drand2.NewDrandBeacon(ps.Pubsub, bus) if err != nil { logrus.Fatalf("Failed to setup drand beacon: %s", err) } logrus.Info("DRAND beacon subsystem has been initialized!") return db } // FIXME: do we really need this? func provideWallet(peerID peer.ID, privKey []byte) (*wallet.LocalWallet, error) { // TODO make persistent keystore kstore := wallet.NewMemKeyStore() keyInfo := types.KeyInfo{ Type: types.KTEd25519, PrivateKey: privKey, } kstore.Put(wallet.KNamePrefix+peerID.String(), keyInfo) w, err := wallet.NewWallet(kstore) if err != nil { return nil, xerrors.Errorf("failed to setup wallet: %w", err) } return w, nil } func provideEthereumClient(config *config.Config) *ethclient.EthereumClient { ethereum := ethclient.NewEthereumClient() err := ethereum.Initialize(&config.Ethereum) if err != nil { logrus.Fatal(err) } logrus.WithField("ethAddress", ethereum.GetEthAddress().Hex()).Info("Ethereum client has been initialized!") return ethereum } func providePubsubRouter(lhost host.Host, config *config.Config) *pubsub.PubSubRouter { psb := pubsub.NewPubSubRouter(lhost, config.PubSub.ServiceTopicName, config.IsBootstrap) logrus.Info("PubSub subsystem has been initialized!") return psb } func provideConsensusManager( h host.Host, bus EventBus.Bus, psb *pubsub.PubSubRouter, miner *blockchain.Miner, bc *blockchain.BlockChain, ethClient *ethclient.EthereumClient, privateKey crypto.PrivKey, bp *consensus.ConsensusStatePool, db *drand2.DrandBeacon, mp *pool.Mempool, ) *consensus.PBFTConsensusManager { c := consensus.NewPBFTConsensusManager( bus, psb, privateKey, ethClient, miner, bc, bp, db, mp, h.ID(), ) logrus.Info("Consensus subsystem has been initialized!") return c } func provideLibp2pHost(config *config.Config, privateKey crypto.PrivKey) host.Host { listenMultiAddr, err := multiaddr.NewMultiaddr(fmt.Sprintf("/ip4/%s/tcp/%d", config.ListenAddr, config.ListenPort)) if err != nil { logrus.Fatalf("Failed to parse multiaddress: %s", err.Error()) } libp2pHost, err := libp2p.New( context.TODO(), libp2p.ListenAddrs(listenMultiAddr), libp2p.Identity(privateKey), ) if err != nil { logrus.Fatal(err) } logrus.WithField( "multiaddress", fmt.Sprintf("/ip4/%s/tcp/%d/p2p/%s", config.ListenAddr, config.ListenPort, libp2pHost.ID().Pretty(), )).Info("Libp2p host has been initialized!") return libp2pHost } func provideNetworkRPCHost(h host.Host) *gorpc.Server { return gorpc.NewServer(h, DioneProtocolID) } func provideBootstrapAddrs(c *config.Config) []multiaddr.Multiaddr { if c.IsBootstrap { return nil } var bootstrapMaddrs []multiaddr.Multiaddr for _, a := range c.BootstrapNodes { maddr, err := multiaddr.NewMultiaddr(a) if err != nil { logrus.Fatalf("Invalid multiaddress of bootstrap node: %v", err) } bootstrapMaddrs = append(bootstrapMaddrs, maddr) } return bootstrapMaddrs } func providePeerDiscovery(baddrs []multiaddr.Multiaddr, h host.Host) discovery.Discovery { pexDiscovery, err := pex.NewPEXDiscovery(h, baddrs, DefaultPEXUpdateTime) if err != nil { logrus.Fatalf("Failed to setup libp2p PEX discovery: %s", err.Error()) } logrus.Info("Peer discovery subsystem has been initialized!") return pexDiscovery } func provideBlockChain(config *config.Config, bus EventBus.Bus, miner *blockchain.Miner, db *drand2.DrandBeacon) *blockchain.BlockChain { bc, err := blockchain.NewBlockChain(config.Blockchain.DatabasePath, bus, miner, db) if err != nil { logrus.Fatalf("Failed to initialize blockchain storage: %s", err.Error()) } logrus.Info("Blockchain storage has been successfully initialized!") return bc } func provideMempool(bus EventBus.Bus) *pool.Mempool { mp, err := pool.NewMempool(bus) if err != nil { logrus.Fatalf("Failed to initialize mempool: %s", err.Error()) } logrus.Info("Mempool has been successfully initialized!") return mp } func provideSyncManager( bus EventBus.Bus, bp *blockchain.BlockChain, mp *pool.Mempool, c *gorpc.Client, bootstrapAddresses []multiaddr.Multiaddr, psb *pubsub.PubSubRouter, ) sync.SyncManager { bootstrapPeerID := peer.ID("") if bootstrapAddresses != nil { addr, err := peer.AddrInfoFromP2pAddr(bootstrapAddresses[0]) // FIXME if err != nil { logrus.Fatal(err) } bootstrapPeerID = addr.ID } sm := sync.NewSyncManager(bus, bp, mp, c, bootstrapPeerID, psb) logrus.Info("Blockchain sync subsystem has been successfully initialized!") return sm } func provideDirectRPCClient(h host.Host) *gorpc.Client { return gorpc.NewClient(h, DioneProtocolID) } func provideNetworkService(bp *blockchain.BlockChain, mp *pool.Mempool) *NetworkService { ns := NewNetworkService(bp, mp) logrus.Info("Direct RPC has been successfully initialized!") return ns } func provideBlockPool(mp *pool.Mempool, bus EventBus.Bus, config *config.Config) *consensus.ConsensusStatePool { bp, err := consensus.NewConsensusRoundPool(mp, bus, config.ConsensusMinApprovals) if err != nil { logrus.Fatalf("Failed to initialize blockpool: %s", err.Error()) } logrus.Info("Consensus state pool has been successfully initialized!") return bp } func provideEventBus() EventBus.Bus { return EventBus.New() } func provideAppFlags() *AppFlags { var flags AppFlags flag.StringVar(&flags.ConfigPath, "config", "", "Path to config") flag.BoolVar(&flags.Verbose, "verbose", false, "Verbose logging") flag.Parse() return &flags } func provideConfig(flags *AppFlags) *config.Config { if flags.ConfigPath == "" { logrus.Fatal("no config path provided") } cfg, err := config.NewConfig(flags.ConfigPath) if err != nil { logrus.Fatalf("failed to load config: %v", err) } return cfg } func providePrivateKey(cfg *config.Config) crypto.PrivKey { var privateKey crypto.PrivKey if _, err := os.Stat(cfg.PrivateKeyPath); os.IsNotExist(err) { privateKey, err = generatePrivateKey() if err != nil { logrus.Fatal(err) } dirName := filepath.Dir(cfg.PrivateKeyPath) if _, err := os.Stat(dirName); os.IsNotExist(err) { err := os.MkdirAll(dirName, 0755) if err != nil { logrus.Fatalf("Cannot create private key file: %s", err.Error()) } } f, err := os.Create(cfg.PrivateKeyPath) if err != nil { logrus.Fatalf("Cannot create private key file: %s, ", err) } r, err := privateKey.Raw() if err != nil { logrus.Fatal(err) } _, err = f.Write(r) if err != nil { logrus.Fatal(err) } } else { pkey, err := ioutil.ReadFile(cfg.PrivateKeyPath) if err != nil { logrus.Fatal(err) } privateKey, err = crypto.UnmarshalEd25519PrivateKey(pkey) if err != nil { logrus.Fatal(err) } } return privateKey } func generatePrivateKey() (crypto.PrivKey, error) { r := rand.Reader // Creates a new RSA key pair for this host. prvKey, _, err := crypto.GenerateKeyPairWithReader(crypto.Ed25519, 2048, r) if err != nil { return nil, err } return prvKey, nil } func configureDirectRPC(rpcServer *gorpc.Server, ns *NetworkService) { err := rpcServer.Register(ns) if err != nil { logrus.Fatal(err) } } func configureLogger(flags *AppFlags) { logrus.SetReportCaller(true) logrus.SetFormatter(&logrus.TextFormatter{ FullTimestamp: true, CallerPrettyfier: func(f *runtime.Frame) (string, string) { filename := path.Base(f.File) return "", fmt.Sprintf("%s:%d:", filename, f.Line) }, }) if flags.Verbose { logrus.SetLevel(logrus.DebugLevel) } else { logrus.SetLevel(logrus.InfoLevel) } } func configureForeignBlockchainRPC() { fc := filecoin.NewLotusClient() rpc.RegisterRPC(rtypes.RPCTypeFilecoin, map[string]func(string) ([]byte, error){ "getTransaction": fc.GetTransaction, "getBlock": fc.GetBlock, }) sl := solana2.NewSolanaClient() rpc.RegisterRPC(rtypes.RPCTypeSolana, map[string]func(string) ([]byte, error){ "getTransaction": sl.GetTransaction, }) logrus.Info("Foreign Blockchain RPC clients has been successfully configured!") } func configureMiner(m *blockchain.Miner, b *blockchain.BlockChain) { m.SetBlockchainInstance(b) } func initializeBlockchain(bc *blockchain.BlockChain) { _, err := bc.GetLatestBlockHeight() if err == blockchain.ErrLatestHeightNil { gBlock := types2.GenesisBlock() err = bc.StoreBlock(gBlock) // commit genesis block if err != nil { logrus.Fatal(err) } logrus.Info("Committed genesis block") } }