Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Dual network support #63

Merged
merged 4 commits into from
Oct 17, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 5 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,13 +9,13 @@ dcrseeder is a crawler for the Decred network, which exposes a list of reliable
nodes via a built-in HTTP server.

When dcrseeder is started for the first time, it will connect to the dcrd node
specified with the `-s` flag, send a `getaddrs` request, expecting an `addr`
specified in config, send a `getaddrs` request, expecting an `addr`
message response. This message contains hostnames and IPs of peers known by the
node. dcrseeder will then connect to each of these peers, send a `getaddrs`
request, and will continue traversing the network in this fashion. dcrseeder
maintains a list of all known peers and periodically checks that they are
online and available. The list is stored on disk in a json file, so on
subsequent start ups the dcrd node specified with `-s` does not need to be
subsequent start ups the dcrd node specified in config does not need to be
online.

When dcrseeder is queried for node information, it responds with details of a
Expand All @@ -33,11 +33,13 @@ root directory.
To start dcrseeder listening on localhost:8000 with an initial connection to working testnet node 192.168.0.1:

```no-highlight
$ ./dcrseeder -s 192.168.0.1 --testnet --httplisten=localhost:8000
$ ./dcrseeder --testnet.enabled --testnet.seeder 192.168.0.1 --testnet.listen=localhost:8000
```

You will then need to redirect HTTPS traffic on your public IP to localhost:8000

An [example configuration file](./sample-dcrseeder.conf) lists the full set of options available.

## Issue Tracker

The [integrated github issue tracker](https://github.com/decred/dcrseeder/issues)
Expand Down
56 changes: 40 additions & 16 deletions config.go
Original file line number Diff line number Diff line change
Expand Up @@ -34,9 +34,14 @@ var (
//
// See loadConfig for details on the configuration load process.
type config struct {
Listen string `long:"httplisten" description:"HTTP listen on address:port"`
Seeder string `short:"s" description:"IP address of a working node"`
TestNet bool `long:"testnet" description:"Use testnet"`
Mainnet *netConfig `group:"Mainnet" namespace:"mainnet"`
Testnet *netConfig `group:"Testnet" namespace:"testnet"`
}

type netConfig struct {
Enabled bool `long:"enabled" description:"Enable dcrseeder on this network"`
Listen string `long:"listen" description:"HTTP listen on address:port (must be unique per network)"`
Seeder string `long:"seeder" description:"IP address of a working node on this network"`

netParams *chaincfg.Params
seederIP netip.AddrPort
Expand Down Expand Up @@ -101,27 +106,46 @@ func loadConfig() (*config, error) {
}
return nil, err
}
if cfg.TestNet {
cfg.netParams = chaincfg.TestNet3Params()
} else {
cfg.netParams = chaincfg.MainNetParams()

if !cfg.Mainnet.Enabled && !cfg.Testnet.Enabled {
return nil, fmt.Errorf("no networks enabled")
}

cfg.dataDir = filepath.Join(defaultHomeDir, cfg.netParams.Name)
parseNet := func(cfg *netConfig, params *chaincfg.Params) error {
// Only parse params for this network if it is enabled.
if !cfg.Enabled {
return nil
}

cfg.netParams = params
cfg.dataDir = filepath.Join(defaultHomeDir, cfg.netParams.Name)

if cfg.Listen == "" {
return fmt.Errorf("no listeners specified")
}
cfg.Listen = normalizeAddress(cfg.Listen, defaultHTTPPort)

if len(cfg.Seeder) == 0 {
return fmt.Errorf("no seeder specified")
}
cfg.Seeder = normalizeAddress(cfg.Seeder, cfg.netParams.DefaultPort)

if cfg.Listen == "" {
return nil, fmt.Errorf("no listeners specified")
cfg.seederIP, err = netip.ParseAddrPort(cfg.Seeder)
if err != nil {
return fmt.Errorf("invalid seeder ip: %v", err)
}

return nil
}
cfg.Listen = normalizeAddress(cfg.Listen, defaultHTTPPort)

if len(cfg.Seeder) == 0 {
return nil, fmt.Errorf("no seeder specified")
err = parseNet(cfg.Mainnet, chaincfg.MainNetParams())
if err != nil {
return nil, fmt.Errorf("mainnet params error: %w", err)
}
cfg.Seeder = normalizeAddress(cfg.Seeder, cfg.netParams.DefaultPort)

cfg.seederIP, err = netip.ParseAddrPort(cfg.Seeder)
err = parseNet(cfg.Testnet, chaincfg.TestNet3Params())
if err != nil {
return nil, fmt.Errorf("invalid seeder ip: %v", err)
return nil, fmt.Errorf("testnet params error: %w", err)
}

return &cfg, nil
Expand Down
100 changes: 61 additions & 39 deletions decred.go
Original file line number Diff line number Diff line change
Expand Up @@ -153,60 +153,82 @@ func main() {
// run is the real main function for dcrseeder. It is necessary to work around
// the fact that deferred functions do not run when os.Exit() is called.
func run() int {
ctx := shutdownListener()
ctx, cancel := shutdownListener()

cfg, err := loadConfig()
if err != nil {
fmt.Fprintf(os.Stderr, "loadConfig: %v\n", err)
return 1
}

// Prefix log lines with current network, e.g. "[mainnet]" or "[testnet]".
logPrefix := fmt.Sprintf("[%.7s] ", cfg.netParams.Name)
log := log.New(os.Stdout, logPrefix, log.LstdFlags|log.Lmsgprefix)
defer log.Print("Bye!")

amgr, err := NewManager(cfg.dataDir, log)
if err != nil {
fmt.Fprintf(os.Stderr, "NewManager: %v\n", err)
return 1
}
// Wait for all subsystems to shut down before returning and allowing the
// process to end.
var wg sync.WaitGroup
defer wg.Wait()

runNet := func(cfg *netConfig) error {
// Nothing to do if this network is not enabled.
if !cfg.Enabled {
return nil
}

// Prefix log lines with current network, e.g. "[mainnet]" or "[testnet]".
logPrefix := fmt.Sprintf("[%.7s] ", cfg.netParams.Name)
log := log.New(os.Stdout, logPrefix, log.LstdFlags|log.Lmsgprefix)

amgr, err := NewManager(cfg.dataDir, log)
if err != nil {
log.Println(err)
return err
}

amgr.AddAddresses([]netip.AddrPort{cfg.seederIP})
amgr.AddAddresses([]netip.AddrPort{cfg.seederIP})

c := newCrawler(cfg.netParams, amgr, log)
c := newCrawler(cfg.netParams, amgr, log)

server, err := newServer(cfg.Listen, amgr, log)
server, err := newServer(cfg.Listen, amgr, log)
if err != nil {
log.Println(err)
return err
}

wg.Add(1)
go func() {
defer wg.Done()
amgr.run(ctx) // Only returns on context cancellation.
log.Print("Address manager done.")
}()

wg.Add(1)
go func() {
defer wg.Done()
c.run(ctx) // Only returns on context cancellation.
log.Print("Crawler done.")
}()

wg.Add(1)
go func() {
defer wg.Done()
server.run(ctx) // Only returns on context cancellation.
log.Print("HTTP server done.")
}()

return nil
}

err = runNet(cfg.Mainnet)
if err != nil {
fmt.Fprint(os.Stderr, err)
cancel()
return 1
}

var wg sync.WaitGroup

wg.Add(1)
go func() {
defer wg.Done()
amgr.run(ctx) // Only returns on context cancellation.
log.Print("Address manager done.")
}()

wg.Add(1)
go func() {
defer wg.Done()
c.run(ctx) // Only returns on context cancellation.
log.Print("Crawler done.")
}()

wg.Add(1)
go func() {
defer wg.Done()
server.run(ctx) // Only returns on context cancellation.
log.Print("HTTP server done.")
}()

// Wait for crawler and http server, then stop address manager.
wg.Wait()
log.Print("Bye!")
err = runNet(cfg.Testnet)
if err != nil {
cancel()
return 1
}

return 0
}
25 changes: 25 additions & 0 deletions sample-dcrseeder.conf
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
; ------------------------------------------------------------------------------
; Mainnet settings
; ------------------------------------------------------------------------------

; Enable dcrseeder on mainnet.
mainnet.enabled=1

; HTTP listen on address:port (must be unique per network).
mainnet.listen=127.0.0.1:8000

; IP address of a working node on mainnet.
mainnet.seeder=127.0.0.1

; ------------------------------------------------------------------------------
; Testnet settings
; ------------------------------------------------------------------------------

; Enable dcrseeder on testnet.
testnet.enabled=1

; HTTP listen on address:port (must be unique per network).
testnet.listen=127.0.0.1:8001

; IP address of a working node on testnet.
testnet.seeder=127.0.0.1
4 changes: 2 additions & 2 deletions signal.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ var interruptSignals = []os.Signal{os.Interrupt}

// shutdownListener returns a context whose done channel will be closed when OS
// signals such as SIGINT (Ctrl+C) are received.
func shutdownListener() context.Context {
func shutdownListener() (context.Context, context.CancelFunc) {
ctx, cancel := context.WithCancel(context.Background())
go func() {
interruptChannel := make(chan os.Signal, 1)
Expand All @@ -39,5 +39,5 @@ func shutdownListener() context.Context {
}
}()

return ctx
return ctx, cancel
}
Loading