Files
laconicd/server/nitro/server.go
T
roysc 5aa2594073 Integrate go-nitro
- Add nitro server and x/nitro module
    - wire go-nitro p2p through cometbft

- Add distsig server, currently WIP
    - integrate DKG and DSS schemes into ABCI methods

- Remove deprecated features
    - crisis module
    - module invariants

- Update to use newer SDK patterns
    - upgrade sdk to v0.53.x
    - custom address codec
    - expand use of depinject
    - migrate e2e tests to system tests
    - use depinject to set up integration tests
    - change reserved protobuf field name `cerc.registry.v1.Record.type`

- Revise & add documentation
    - TransferCoinsToModuleAccount: clarify function

- Update init.sh script
2025-09-21 11:44:44 +08:00

211 lines
5.5 KiB
Go

package nitro
import (
"context"
"encoding/hex"
"fmt"
"log/slog"
"path/filepath"
"sync/atomic"
cmtcrypto "github.com/cometbft/cometbft/crypto"
"github.com/cometbft/cometbft/p2p"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/crypto"
"github.com/spf13/pflag"
nitrocrypto "github.com/statechannels/go-nitro/crypto"
"github.com/statechannels/go-nitro/node"
"github.com/statechannels/go-nitro/node/engine"
"github.com/statechannels/go-nitro/node/engine/chainservice"
chainutils "github.com/statechannels/go-nitro/node/engine/chainservice/utils"
"github.com/statechannels/go-nitro/node/engine/store"
nitrotypes "github.com/statechannels/go-nitro/types"
"cosmossdk.io/core/address"
"cosmossdk.io/log"
"github.com/cosmos/cosmos-sdk/client/flags"
"github.com/cosmos/cosmos-sdk/crypto/keyring"
sdk "github.com/cosmos/cosmos-sdk/types"
"git.vdb.to/cerc-io/laconicd/server"
"git.vdb.to/cerc-io/laconicd/utils"
)
const (
serverName = "nitro"
ServerContextKey = "server." + serverName
)
// TODO
// 1. implement participation as a signing group member:
// 1.1. initialize group delegate credentials, with distsig over ABCI
// 1.2. bind custodian contract for chain/adjudicator actions
// 2. operation to update DKG group members
// 3. integrate with onboarding module
// Server wraps and configures a go-nitro Node.
type Server struct {
logger log.Logger
config *Config
node *node.Node
storeDir string // path to Nitro store directory
reactor *p2pReactor
keyring keyring.Keyring
addressCodec address.Codec
ethAddress nitrotypes.Address
nodeKey cmtcrypto.PrivKey
// participantID nitrotypes.Participant
ready atomic.Bool
}
func New(globalConfig server.ConfigMap, logger log.Logger, kr keyring.Keyring) (*Server, error) {
home, _ := globalConfig[flags.FlagHome].(string)
cfg, err := UnmarshalConfig(globalConfig)
if err != nil {
return nil, err
}
if err := cfg.Validate(); err != nil {
return nil, err
}
s := &Server{
config: cfg,
storeDir: filepath.Join(home, "nitro"),
reactor: newReactor(cfg, logger.With(log.ModuleKey, serverName, "component", "p2p-reactor")),
keyring: kr,
logger: logger.With(log.ModuleKey, serverName),
addressCodec: utils.NewAddressCodec(),
}
return s, nil
}
func (*Server) Name() string {
return serverName
}
func (s *Server) Start(ctx context.Context) error {
if !s.config.Enable {
s.logger.Info(fmt.Sprintf("%s server is disabled via config", s.Name()))
return nil
}
// TODO: pass keyring through context?
if err := s.init(s.keyring); err != nil {
return err
}
s.logger.Info("starting Nitro server")
s.ready.Store(true)
return nil
}
func (s *Server) Stop(context.Context) error {
s.logger.Info("stopping Nitro server")
s.ready.Store(false)
if s.node != nil {
return s.node.Close()
}
return nil
}
func (s *Server) init(kr keyring.Keyring) error {
var (
ethkey cmtcrypto.PrivKey
err error
c = s.config
)
if c.EthKey != "" {
ethkey, err = utils.ExtractPrivateKeyByUid(kr, c.EthKey)
if err != nil {
return err
}
} else {
return fmt.Errorf("eth-key is not set") // should already be caught by Config.Validate
}
s.nodeKey = ethkey
storeOpts := store.StoreOpts{
UseDurableStore: true,
DurableStoreDir: s.storeDir,
}
chainOpts := chainservice.ChainOpts{
ChainUrl: c.EthURL,
StartBlockNum: c.EthStartBlock,
ChainAuthToken: c.EthAuthToken,
ChainPk: hex.EncodeToString(ethkey.Bytes()),
}
// inject SDK logger into slog, which Nitro uses
loggerImpl, ok := s.logger.Impl().(*slog.Logger)
if ok {
slog.SetDefault(loggerImpl)
} else {
s.logger.Warn("Logger backend is not slog, cannot set as Nitro logger")
}
store, err := store.NewStore(storeOpts)
if err != nil {
return err
}
// Compare chainOpts.ChainStartBlock to lastBlockNum seen in store. The larger of the two
// gets passed as an argument when creating NewEthChainService
storeBlockNum, err := store.GetLastBlockNumSeen()
if err != nil {
return err
}
chainOpts.StartBlockNum = max(storeBlockNum, chainOpts.StartBlockNum)
contractAddresses := node.ContractAddresses{
NaAddress: common.HexToAddress(c.EthNaAddress),
VpaAddress: common.HexToAddress(c.EthVpaAddress),
CaAddress: common.HexToAddress(c.EthCaAddress),
}
chainOpts.CreateAdjudicator = chainutils.AdjudicatorCreator(contractAddresses.NaAddress)
chain, err := chainservice.NewL1ChainService(chainOpts)
if err != nil {
return err
}
// note: utils.EthAddressFromPubKey expects uncompressed keys
ethPubkey, err := crypto.DecompressPubkey(ethkey.PubKey().Bytes())
if err != nil {
return err
}
s.ethAddress = nitrotypes.Address(crypto.PubkeyToAddress(*ethPubkey))
s.node = node.New(
newMessageService("laconicd-p2p-"+s.ethAddress.String(), s.reactor),
chain,
store,
&engine.PermissivePolicy{},
nitrocrypto.NewSimpleCredential(ethkey.Bytes()),
contractAddresses,
)
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)
AddFlags(flags)
return flags
}
func (s *Server) Ready() bool {
return s.ready.Load()
}
func (s *Server) ContextKey() string {
return ServerContextKey
}
func (s *Server) P2PReactors() map[string]p2p.Reactor {
return map[string]p2p.Reactor{"NITRO": s.reactor}
}
func (s *Server) nodeAddress() sdk.AccAddress {
return sdk.AccAddress(s.nodeKey.PubKey().Address())
}