laconicd/nitro/server.go
2025-02-13 11:41:58 +08:00

215 lines
5.2 KiB
Go

package nitro
import (
"context"
"encoding/hex"
"fmt"
"path/filepath"
"strings"
"cosmossdk.io/core/server"
"cosmossdk.io/core/transaction"
"cosmossdk.io/log"
serverv2 "cosmossdk.io/server/v2"
"github.com/cosmos/cosmos-sdk/client"
"github.com/ethereum/go-ethereum/common"
"github.com/spf13/cobra"
"github.com/spf13/pflag"
"github.com/statechannels/go-nitro/node"
"github.com/statechannels/go-nitro/node/engine"
"github.com/statechannels/go-nitro/node/engine/chainservice"
p2pms "github.com/statechannels/go-nitro/node/engine/messageservice/p2p-message-service"
"github.com/statechannels/go-nitro/node/engine/store"
"github.com/statechannels/go-nitro/types"
)
// type ServerComponent[T transaction.Tx] interface {
// Name() string
// Start(context.Context) error
// Stop(context.Context) error
// }
const (
serverName = "nitro"
)
var (
_ serverv2.ServerComponent[transaction.Tx] = (*Server)(nil)
_ serverv2.HasCLICommands = (*Server)(nil)
_ serverv2.HasStartFlags = (*Server)(nil)
_ serverv2.HasConfig = (*Server)(nil)
// _ serverv2.ConfigWriter = (*Server)(nil)
)
type Server struct {
*node.Node
logger log.Logger
config *Config
// name of Ethereum private key used for chain txs
EthKey string
// path to Nitro store directory
storeDir string
ScAddr types.PartyAddress
}
func New(logger log.Logger, cfg server.ConfigMap) (*Server, error) {
home, _ := cfg[serverv2.FlagHome].(string)
s := &Server{
logger: logger.With(log.ModuleKey, serverName),
storeDir: filepath.Join(home, "nitro")}
s.config = s.Config().(*Config)
if len(cfg) > 0 {
if err := serverv2.UnmarshalSubConfig(cfg, s.Name(), &s.config); err != nil {
return nil, fmt.Errorf("failed to unmarshal config: %w", err)
}
}
return s, nil
}
func (s *Server) init(ctx context.Context) error {
c := s.config
var clientCtx client.Context
if v := ctx.Value(client.ClientContextKey); v != nil {
clientCtx = v.(client.Context)
}
sckey, err := extractPrivKeyBytes(clientCtx.Keyring, c.Pk)
if err != nil {
return nil
}
ethkey, err := extractPrivKeyBytes(clientCtx.Keyring, c.EthPk)
if err != nil {
return err
}
// TODO inject signer or callback into nitro node instead of naked privkey
// signer.Sign(message)
// client case is simple signature
// for validator, multisignatures can be negotiated over abci txs, then loaded into signer
// Sign(message) only passes once validator's signature is prepared
storeOpts := store.StoreOpts{
PkBytes: sckey,
UseDurableStore: true,
DurableStoreFolder: s.storeDir,
}
messageOpts := p2pms.MessageOpts{
PkBytes: sckey,
TcpPort: c.MsgPort,
WsMsgPort: c.WsMsgPort,
BootPeers: strings.Split(c.BootPeers, ","),
PublicIp: c.PublicIp,
ExtMultiAddr: c.ExtMultiAddr,
}
chainOpts := chainservice.ChainOpts{
ChainUrl: c.EthUrl,
ChainStartBlockNum: c.EthStartBlock,
ChainAuthToken: c.EthAuthToken,
ChainPk: hex.EncodeToString(ethkey),
NaAddress: common.HexToAddress(c.NaAddress),
VpaAddress: common.HexToAddress(c.VpaAddress),
CaAddress: common.HexToAddress(c.CaAddress),
}
node, store, _, _, err := initNode(s.logger, chainOpts, storeOpts, messageOpts, &engine.PermissivePolicy{})
if err != nil {
return err
}
s.Node = node
s.ScAddr = *store.GetAddress()
return nil
}
func initNode(
logger log.Logger,
chainOpts chainservice.ChainOpts,
storeOpts store.StoreOpts,
messageOpts p2pms.MessageOpts,
policymaker engine.PolicyMaker,
) (
*node.Node,
store.Store,
*p2pms.P2PMessageService,
chainservice.ChainService,
error,
) {
ourStore, err := store.NewStore(storeOpts)
if err != nil {
return nil, nil, nil, nil, err
}
logger.Info("Initializing message service",
"tcp port", messageOpts.TcpPort,
"web socket port", messageOpts.WsMsgPort)
messageOpts.SCAddr = *ourStore.GetAddress()
messageService := p2pms.NewMessageService(messageOpts)
// Compare chainOpts.ChainStartBlock to lastBlockNum seen in store. The larger of the two
// gets passed as an argument when creating NewEthChainService
storeBlockNum, err := ourStore.GetLastBlockNumSeen()
if err != nil {
return nil, nil, nil, nil, err
}
if storeBlockNum > chainOpts.ChainStartBlockNum {
chainOpts.ChainStartBlockNum = storeBlockNum
}
logger.Info("Initializing chain service...")
ourChain, err := chainservice.NewL1ChainService(chainOpts)
if err != nil {
return nil, nil, nil, nil, err
}
node := node.New(
messageService,
ourChain,
ourStore,
policymaker,
)
return &node, ourStore, messageService, ourChain, nil
}
func (s *Server) Name() string {
return serverName
}
func (s *Server) Start(ctx context.Context) error {
return nil
}
func (s *Server) Stop(context.Context) error {
return nil
}
func (s *Server) Config() any {
if s.config == nil {
return DefaultConfig()
}
return s.config
}
func (s *Server) StartCmdFlags() *pflag.FlagSet {
flags := pflag.NewFlagSet(s.Name(), pflag.ExitOnError)
AddNitroFlags(flags)
return flags
}
// func (s *Server) WriteConfig(path string) error
func (s *Server) CLICommands() serverv2.CLIConfig {
return serverv2.CLIConfig{
Commands: []*cobra.Command{
// command to create payment channel
s.FundCmd(),
},
// Queries: []*cobra.Command{},
}
}