Skip to content

Commit 888411e

Browse files
authored
feat: support for multiple TLDs (#180)
Fixes #140
1 parent 026d821 commit 888411e

File tree

3 files changed

+149
-118
lines changed

3 files changed

+149
-118
lines changed

internal/config/config.go

Lines changed: 45 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -9,19 +9,20 @@ package config
99
import (
1010
"fmt"
1111
"os"
12+
"strings"
1213

1314
"github.com/kelseyhightower/envconfig"
1415
"gopkg.in/yaml.v2"
1516
)
1617

1718
type Config struct {
18-
Logging LoggingConfig `yaml:"logging"`
19-
Metrics MetricsConfig `yaml:"metrics"`
20-
Dns DnsConfig `yaml:"dns"`
21-
Debug DebugConfig `yaml:"debug"`
22-
Indexer IndexerConfig `yaml:"indexer"`
23-
State StateConfig `yaml:"state"`
24-
Profile string `yaml:"profile" envconfig:"PROFILE"`
19+
Logging LoggingConfig `yaml:"logging"`
20+
Metrics MetricsConfig `yaml:"metrics"`
21+
Dns DnsConfig `yaml:"dns"`
22+
Debug DebugConfig `yaml:"debug"`
23+
Indexer IndexerConfig `yaml:"indexer"`
24+
State StateConfig `yaml:"state"`
25+
Profiles []string `yaml:"profiles" envconfig:"PROFILES"`
2526
}
2627

2728
type LoggingConfig struct {
@@ -52,11 +53,8 @@ type IndexerConfig struct {
5253
NetworkMagic uint32 `yaml:"networkMagic" envconfig:"INDEXER_NETWORK_MAGIC"`
5354
Address string `yaml:"address" envconfig:"INDEXER_TCP_ADDRESS"`
5455
SocketPath string `yaml:"socketPath" envconfig:"INDEXER_SOCKET_PATH"`
55-
ScriptAddress string `yaml:"scriptAddress" envconfig:"INDEXER_SCRIPT_ADDRESS"`
5656
InterceptHash string `yaml:"interceptHash" envconfig:"INDEXER_INTERCEPT_HASH"`
5757
InterceptSlot uint64 `yaml:"interceptSlot" envconfig:"INDEXER_INTERCEPT_SLOT"`
58-
Tld string `yaml:"tld" envconfig:"INDEXER_TLD"`
59-
PolicyId string `yaml:"policyId" envconfig:"INDEXER_POLICY_ID"`
6058
Verify bool `yaml:"verify" envconfig:"INDEXER_VERIFY"`
6159
}
6260

@@ -90,13 +88,14 @@ var globalConfig = &Config{
9088
ListenPort: 8081,
9189
},
9290
Indexer: IndexerConfig{
93-
Network: "preprod",
94-
Verify: true,
91+
Verify: true,
9592
},
9693
State: StateConfig{
9794
Directory: "./.state",
9895
},
99-
Profile: "ada-preprod",
96+
Profiles: []string{
97+
"ada-preprod",
98+
},
10099
}
101100

102101
func Load(configFile string) (*Config, error) {
@@ -118,42 +117,44 @@ func Load(configFile string) (*Config, error) {
118117
if err != nil {
119118
return nil, fmt.Errorf("error processing environment: %s", err)
120119
}
121-
// Check profile
122-
profile, ok := Profiles[globalConfig.Profile]
123-
if !ok {
124-
return nil, fmt.Errorf("unknown profile: %s", globalConfig.Profile)
125-
}
126-
// Provide default network
127-
if globalConfig.Indexer.Network != "" {
128-
if profile.Network != "" {
129-
globalConfig.Indexer.Network = profile.Network
130-
} else {
131-
return nil, fmt.Errorf("no built-in network name for specified profile, please provide one")
120+
// Check profiles
121+
availableProfiles := GetAvailableProfiles()
122+
var interceptSlot uint64
123+
var interceptHash string
124+
for _, profile := range globalConfig.Profiles {
125+
foundProfile := false
126+
for _, availableProfile := range availableProfiles {
127+
if profile == availableProfile {
128+
profileData := Profiles[profile]
129+
// Provide default network
130+
if profileData.Network != "" {
131+
if globalConfig.Indexer.Network == "" {
132+
globalConfig.Indexer.Network = profileData.Network
133+
} else {
134+
if globalConfig.Indexer.Network != profileData.Network {
135+
return nil, fmt.Errorf("conflicting networks configured: %s and %s", globalConfig.Indexer.Network, profileData.Network)
136+
}
137+
}
138+
}
139+
// Update intercept slot/hash if earlier than any other profiles so far
140+
if interceptSlot == 0 || profileData.InterceptSlot < interceptSlot {
141+
interceptSlot = profileData.InterceptSlot
142+
interceptHash = profileData.InterceptHash
143+
}
144+
foundProfile = true
145+
break
146+
}
132147
}
133-
}
134-
// Provide default script address from profile
135-
if globalConfig.Indexer.ScriptAddress == "" {
136-
if profile.ScriptAddress != "" {
137-
globalConfig.Indexer.ScriptAddress = profile.ScriptAddress
138-
} else {
139-
return nil, fmt.Errorf("no built-in script address for specified profile, please provide one")
148+
if !foundProfile {
149+
return nil, fmt.Errorf("unknown profile: %s: available profiles: %s", profile, strings.Join(availableProfiles, ","))
140150
}
141151
}
142-
// Provide default intercept point from profile
152+
// Provide default intercept point from profile(s)
143153
if globalConfig.Indexer.InterceptSlot == 0 ||
144154
globalConfig.Indexer.InterceptHash == "" {
145-
if profile.InterceptHash != "" && profile.InterceptSlot > 0 {
146-
globalConfig.Indexer.InterceptHash = profile.InterceptHash
147-
globalConfig.Indexer.InterceptSlot = profile.InterceptSlot
148-
}
149-
}
150-
// Provide default TLD and Policy ID from profile
151-
if globalConfig.Indexer.Tld == "" || globalConfig.Indexer.PolicyId == "" {
152-
if profile.Tld != "" && profile.PolicyId != "" {
153-
globalConfig.Indexer.Tld = profile.Tld
154-
globalConfig.Indexer.PolicyId = profile.PolicyId
155-
} else {
156-
return nil, fmt.Errorf("no built-in TLD and/or policy ID for specified profile, please provide one")
155+
if interceptHash != "" && interceptSlot > 0 {
156+
globalConfig.Indexer.InterceptHash = interceptHash
157+
globalConfig.Indexer.InterceptSlot = interceptSlot
157158
}
158159
}
159160
return globalConfig, nil

internal/config/profile.go

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,27 @@ type Profile struct {
1515
InterceptHash string // Chain-sync initial intercept hash
1616
}
1717

18+
func GetProfiles() []Profile {
19+
var ret []Profile
20+
for k, profile := range Profiles {
21+
for _, tmpProfile := range globalConfig.Profiles {
22+
if k == tmpProfile {
23+
ret = append(ret, profile)
24+
break
25+
}
26+
}
27+
}
28+
return ret
29+
}
30+
31+
func GetAvailableProfiles() []string {
32+
var ret []string
33+
for k := range Profiles {
34+
ret = append(ret, k)
35+
}
36+
return ret
37+
}
38+
1839
var Profiles = map[string]Profile{
1940
// This (default) profile corresponds to the values specified in:
2041
// https://github.com/blinklabs-io/cardano-dns/blob/main/README.md

internal/indexer/indexer.go

Lines changed: 83 additions & 74 deletions
Original file line numberDiff line numberDiff line change
@@ -141,8 +141,12 @@ func (i *Indexer) Start() error {
141141
)
142142
i.pipeline.AddFilter(filterEvent)
143143
// We only care about transactions on a certain address
144+
var filterAddresses []string
145+
for _, profile := range config.GetProfiles() {
146+
filterAddresses = append(filterAddresses, profile.ScriptAddress)
147+
}
144148
filterChainsync := filter_chainsync.New(
145-
filter_chainsync.WithAddresses([]string{cfg.Indexer.ScriptAddress}),
149+
filter_chainsync.WithAddresses(filterAddresses),
146150
)
147151
i.pipeline.AddFilter(filterChainsync)
148152
// Configure pipeline output
@@ -172,102 +176,107 @@ func (i *Indexer) handleEvent(evt event.Event) error {
172176
eventTx := evt.Payload.(input_chainsync.TransactionEvent)
173177
eventCtx := evt.Context.(input_chainsync.TransactionContext)
174178
for _, txOutput := range eventTx.Outputs {
175-
datum := txOutput.Datum()
176-
if datum != nil {
177-
var dnsDomain models.CardanoDnsDomain
178-
if _, err := cbor.Decode(datum.Cbor(), &dnsDomain); err != nil {
179-
logger.Warnf(
180-
"error decoding TX (%s) output datum: %s",
181-
eventCtx.TransactionHash,
182-
err,
183-
)
184-
// Stop processing TX output if we can't parse the datum
179+
for _, profile := range config.GetProfiles() {
180+
if txOutput.Address().String() != profile.ScriptAddress {
185181
continue
186182
}
187-
origin := string(dnsDomain.Origin)
188-
// Convert origin to canonical form for consistency
189-
// This mostly means adding a trailing period if it doesn't have one
190-
domainName := dns.CanonicalName(origin)
191-
// We want an empty value for the TLD root for convenience
192-
if domainName == `.` {
193-
domainName = ``
194-
}
195-
// Append TLD
196-
domainName = dns.CanonicalName(
197-
domainName + cfg.Indexer.Tld,
198-
)
199-
if cfg.Indexer.Verify {
200-
// Look for asset matching domain origin and TLD policy ID
201-
if txOutput.Assets() == nil {
183+
datum := txOutput.Datum()
184+
if datum != nil {
185+
var dnsDomain models.CardanoDnsDomain
186+
if _, err := cbor.Decode(datum.Cbor(), &dnsDomain); err != nil {
202187
logger.Warnf(
203-
"ignoring datum for domain %q with no matching asset",
204-
domainName,
188+
"error decoding TX (%s) output datum: %s",
189+
eventCtx.TransactionHash,
190+
err,
205191
)
192+
// Stop processing TX output if we can't parse the datum
206193
continue
207194
}
208-
foundAsset := false
209-
for _, policyId := range txOutput.Assets().Policies() {
210-
for _, assetName := range txOutput.Assets().Assets(policyId) {
211-
if policyId.String() == cfg.Indexer.PolicyId {
212-
if string(assetName) == string(origin) {
213-
foundAsset = true
195+
origin := string(dnsDomain.Origin)
196+
// Convert origin to canonical form for consistency
197+
// This mostly means adding a trailing period if it doesn't have one
198+
domainName := dns.CanonicalName(origin)
199+
// We want an empty value for the TLD root for convenience
200+
if domainName == `.` {
201+
domainName = ``
202+
}
203+
// Append TLD
204+
domainName = dns.CanonicalName(
205+
domainName + profile.Tld,
206+
)
207+
if cfg.Indexer.Verify {
208+
// Look for asset matching domain origin and TLD policy ID
209+
if txOutput.Assets() == nil {
210+
logger.Warnf(
211+
"ignoring datum for domain %q with no matching asset",
212+
domainName,
213+
)
214+
continue
215+
}
216+
foundAsset := false
217+
for _, policyId := range txOutput.Assets().Policies() {
218+
for _, assetName := range txOutput.Assets().Assets(policyId) {
219+
if policyId.String() == profile.PolicyId {
220+
if string(assetName) == string(origin) {
221+
foundAsset = true
222+
} else {
223+
logger.Warnf(
224+
"ignoring datum for domain %q with no matching asset",
225+
domainName,
226+
)
227+
}
214228
} else {
215229
logger.Warnf(
216230
"ignoring datum for domain %q with no matching asset",
217231
domainName,
218232
)
219233
}
220-
} else {
234+
}
235+
}
236+
if !foundAsset {
237+
continue
238+
}
239+
// Make sure all records are for specified origin domain
240+
badRecordName := false
241+
for _, record := range dnsDomain.Records {
242+
recordName := dns.CanonicalName(
243+
string(record.Lhs),
244+
)
245+
if !strings.HasSuffix(recordName, domainName) {
221246
logger.Warnf(
222-
"ignoring datum for domain %q with no matching asset",
247+
"ignoring datum with record %q outside of origin domain (%s)",
248+
recordName,
223249
domainName,
224250
)
251+
badRecordName = true
252+
break
225253
}
226254
}
255+
if badRecordName {
256+
continue
257+
}
227258
}
228-
if !foundAsset {
229-
continue
230-
}
231-
// Make sure all records are for specified origin domain
232-
badRecordName := false
259+
// Convert domain records into our storage format
260+
tmpRecords := []state.DomainRecord{}
233261
for _, record := range dnsDomain.Records {
234-
recordName := dns.CanonicalName(
235-
string(record.Lhs),
236-
)
237-
if !strings.HasSuffix(recordName, domainName) {
238-
logger.Warnf(
239-
"ignoring datum with record %q outside of origin domain (%s)",
240-
recordName,
241-
domainName,
242-
)
243-
badRecordName = true
244-
break
262+
tmpRecord := state.DomainRecord{
263+
Lhs: string(record.Lhs),
264+
Type: string(record.Type),
265+
Rhs: string(record.Rhs),
245266
}
267+
if record.Ttl.HasValue() {
268+
tmpRecord.Ttl = int(record.Ttl.Value)
269+
}
270+
tmpRecords = append(tmpRecords, tmpRecord)
246271
}
247-
if badRecordName {
248-
continue
249-
}
250-
}
251-
// Convert domain records into our storage format
252-
tmpRecords := []state.DomainRecord{}
253-
for _, record := range dnsDomain.Records {
254-
tmpRecord := state.DomainRecord{
255-
Lhs: string(record.Lhs),
256-
Type: string(record.Type),
257-
Rhs: string(record.Rhs),
258-
}
259-
if record.Ttl.HasValue() {
260-
tmpRecord.Ttl = int(record.Ttl.Value)
272+
if err := state.GetState().UpdateDomain(domainName, tmpRecords); err != nil {
273+
return err
261274
}
262-
tmpRecords = append(tmpRecords, tmpRecord)
263-
}
264-
if err := state.GetState().UpdateDomain(domainName, tmpRecords); err != nil {
265-
return err
275+
logger.Infof(
276+
"found updated registration for domain: %s",
277+
domainName,
278+
)
266279
}
267-
logger.Infof(
268-
"found updated registration for domain: %s",
269-
domainName,
270-
)
271280
}
272281
}
273282
return nil

0 commit comments

Comments
 (0)