Skip to content

Commit

Permalink
Network: Archival node DNS Resolution (algorand#5940)
Browse files Browse the repository at this point in the history
  • Loading branch information
gmalouf authored Feb 23, 2024
1 parent 52964ed commit 787f758
Show file tree
Hide file tree
Showing 7 changed files with 51 additions and 55 deletions.
4 changes: 2 additions & 2 deletions catchup/peerSelector.go
Original file line number Diff line number Diff line change
Expand Up @@ -29,12 +29,12 @@ import (
)

const (
// peerRankInitialFirstPriority is the high-priority peers group ( typically, archivers )
// peerRankInitialFirstPriority is the high-priority peers group
peerRankInitialFirstPriority = 0
peerRank0LowBlockTime = 1
peerRank0HighBlockTime = 199

// peerRankInitialSecondPriority is the second priority peers group ( typically, relays )
// peerRankInitialSecondPriority is the second priority peers group
peerRankInitialSecondPriority = 200
peerRank1LowBlockTime = 201
peerRank1HighBlockTime = 399
Expand Down
1 change: 1 addition & 0 deletions config/localTemplate.go
Original file line number Diff line number Diff line change
Expand Up @@ -171,6 +171,7 @@ type Local struct {
RestWriteTimeoutSeconds int `version[4]:"120"`

// DNSBootstrapID specifies the names of a set of DNS SRV records that identify the set of nodes available to connect to.
// This is applicable to both relay and archival nodes - they are assumed to use the same DNSBootstrapID today.
// When resolving the bootstrap ID <network> will be replaced by the genesis block's network name. This string uses a URL
// parsing library and supports optional backup and dedup parameters. 'backup' is used to provide a second DNS entry to use
// in case the primary is unavailable. dedup is intended to be used to deduplicate SRV records returned from the primary
Expand Down
2 changes: 0 additions & 2 deletions network/gossipNode.go
Original file line number Diff line number Diff line change
Expand Up @@ -42,8 +42,6 @@ const (
PeersPhonebookRelays PeerOption = iota
// PeersPhonebookArchivalNodes specifies all archival nodes (relay or p2p)
PeersPhonebookArchivalNodes PeerOption = iota
// PeersPhonebookArchivers specifies all archivers in the phonebook
PeersPhonebookArchivers PeerOption = iota
)

// GossipNode represents a node in the gossip network
Expand Down
6 changes: 3 additions & 3 deletions network/phonebook.go
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ import (
const getAllAddresses = math.MaxInt32

// PhoneBookEntryRoles defines the roles that a single entry on the phonebook can take.
// currently, we have two roles : relay role and archiver role, which are mutually exclusive.
// currently, we have two roles : relay role and archival role, which are mutually exclusive.
//
//msgp:ignore PhoneBookEntryRoles
type PhoneBookEntryRoles int
Expand All @@ -39,8 +39,8 @@ type PhoneBookEntryRoles int
// or via a configuration file.
const PhoneBookEntryRelayRole = 1

// PhoneBookEntryArchiverRole used for all the archivers that are provided via the archive SRV record.
const PhoneBookEntryArchiverRole = 2
// PhoneBookEntryArchivalRole used for all the archival nodes that are provided via the archive SRV record.
const PhoneBookEntryArchivalRole = 2

// Phonebook stores or looks up addresses of nodes we might contact
type Phonebook interface {
Expand Down
6 changes: 3 additions & 3 deletions network/phonebook_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -346,19 +346,19 @@ func TestPhonebookRoles(t *testing.T) {

ph := MakePhonebook(1, 1).(*phonebookImpl)
ph.ReplacePeerList(relaysSet, "default", PhoneBookEntryRelayRole)
ph.ReplacePeerList(archiverSet, "default", PhoneBookEntryArchiverRole)
ph.ReplacePeerList(archiverSet, "default", PhoneBookEntryArchivalRole)
require.Equal(t, len(relaysSet)+len(archiverSet), len(ph.data))
require.Equal(t, len(relaysSet)+len(archiverSet), ph.Length())

for _, role := range []PhoneBookEntryRoles{PhoneBookEntryRelayRole, PhoneBookEntryArchiverRole} {
for _, role := range []PhoneBookEntryRoles{PhoneBookEntryRelayRole, PhoneBookEntryArchivalRole} {
for k := 0; k < 100; k++ {
for l := 0; l < 3; l++ {
entries := ph.GetAddresses(l, role)
if role == PhoneBookEntryRelayRole {
for _, entry := range entries {
require.Contains(t, entry, "relay")
}
} else if role == PhoneBookEntryArchiverRole {
} else if role == PhoneBookEntryArchivalRole {
for _, entry := range entries {
require.Contains(t, entry, "archiver")
}
Expand Down
60 changes: 28 additions & 32 deletions network/wsNetwork.go
Original file line number Diff line number Diff line change
Expand Up @@ -548,15 +548,7 @@ func (wn *WebsocketNetwork) GetPeers(options ...PeerOption) []Peer {
}
case PeersPhonebookArchivalNodes:
var addrs []string
addrs = wn.phonebook.GetAddresses(1000, PhoneBookEntryRelayRole)
for _, addr := range addrs {
peerCore := makePeerCore(wn.ctx, wn, wn.log, wn.handler.readBuffer, addr, wn.GetRoundTripper(), "" /*origin address*/)
outPeers = append(outPeers, &peerCore)
}
case PeersPhonebookArchivers:
// return copy of phonebook, which probably also contains peers we're connected to, but if it doesn't maybe we shouldn't be making new connections to those peers (because they disappeared from the directory)
var addrs []string
addrs = wn.phonebook.GetAddresses(1000, PhoneBookEntryArchiverRole)
addrs = wn.phonebook.GetAddresses(1000, PhoneBookEntryArchivalRole)
for _, addr := range addrs {
peerCore := makePeerCore(wn.ctx, wn, wn.log, wn.handler.readBuffer, addr, wn.GetRoundTripper(), "" /*origin address*/)
outPeers = append(outPeers, &peerCore)
Expand Down Expand Up @@ -1607,15 +1599,17 @@ func (wn *WebsocketNetwork) refreshRelayArchivePhonebookAddresses() {
dnsBootstrapArray := wn.config.DNSBootstrapArray(wn.NetworkID)

for _, dnsBootstrap := range dnsBootstrapArray {
primaryRelayAddrs, primaryArchiveAddrs := wn.getDNSAddrs(dnsBootstrap.PrimarySRVBootstrap)
primaryRelayAddrs, primaryArchivalAddrs := wn.getDNSAddrs(dnsBootstrap.PrimarySRVBootstrap)

if dnsBootstrap.BackupSRVBootstrap != "" {
backupRelayAddrs, backupArchiveAddrs := wn.getDNSAddrs(dnsBootstrap.BackupSRVBootstrap)
dedupedRelayAddresses := wn.mergePrimarySecondaryRelayAddressSlices(wn.NetworkID, primaryRelayAddrs,
backupRelayAddrs, backupArchivalAddrs := wn.getDNSAddrs(dnsBootstrap.BackupSRVBootstrap)
dedupedRelayAddresses := wn.mergePrimarySecondaryAddressSlices(primaryRelayAddrs,
backupRelayAddrs, dnsBootstrap.DedupExp)
wn.updatePhonebookAddresses(dedupedRelayAddresses, append(primaryArchiveAddrs, backupArchiveAddrs...))
dedupedArchivalAddresses := wn.mergePrimarySecondaryAddressSlices(primaryArchivalAddrs,
backupArchivalAddrs, dnsBootstrap.DedupExp)
wn.updatePhonebookAddresses(dedupedRelayAddresses, dedupedArchivalAddresses)
} else {
wn.updatePhonebookAddresses(primaryRelayAddrs, primaryArchiveAddrs)
wn.updatePhonebookAddresses(primaryRelayAddrs, primaryArchivalAddrs)
}
}
}
Expand All @@ -1628,7 +1622,9 @@ func (wn *WebsocketNetwork) updatePhonebookAddresses(relayAddrs []string, archiv
wn.log.Infof("got no relay DNS addrs for network %s", wn.NetworkID)
}
if len(archiveAddrs) > 0 {
wn.phonebook.ReplacePeerList(archiveAddrs, string(wn.NetworkID), PhoneBookEntryArchiverRole)
wn.phonebook.ReplacePeerList(archiveAddrs, string(wn.NetworkID), PhoneBookEntryArchivalRole)
} else {
wn.log.Infof("got no archive DNS addrs for network %s", wn.NetworkID)
}
}

Expand Down Expand Up @@ -1846,46 +1842,46 @@ func (wn *WebsocketNetwork) prioWeightRefresh() {
}
}

// This logic assumes that the relay address suffixes
// This logic assumes that the address suffixes
// correspond to the primary/backup network conventions. If this proves to be false, i.e. one network's
// suffix is a substring of another network's suffix, then duplicates can end up in the merged slice.
func (wn *WebsocketNetwork) mergePrimarySecondaryRelayAddressSlices(network protocol.NetworkID,
primaryRelayAddresses []string, secondaryRelayAddresses []string, dedupExp *regexp.Regexp) (dedupedRelayAddresses []string) {
func (wn *WebsocketNetwork) mergePrimarySecondaryAddressSlices(
primaryAddresses []string, secondaryAddresses []string, dedupExp *regexp.Regexp) (dedupedAddresses []string) {

if dedupExp == nil {
// No expression provided, so just append the slices without deduping
return append(primaryRelayAddresses, secondaryRelayAddresses...)
return append(primaryAddresses, secondaryAddresses...)
}

var relayAddressPrefixToValue = make(map[string]string, 2*len(primaryRelayAddresses))
var addressPrefixToValue = make(map[string]string, 2*len(primaryAddresses))

for _, pra := range primaryRelayAddresses {
for _, pra := range primaryAddresses {
var normalizedPra = strings.ToLower(pra)

var pfxKey = dedupExp.ReplaceAllString(normalizedPra, "")
if _, exists := relayAddressPrefixToValue[pfxKey]; !exists {
relayAddressPrefixToValue[pfxKey] = normalizedPra
if _, exists := addressPrefixToValue[pfxKey]; !exists {
addressPrefixToValue[pfxKey] = normalizedPra
}
}

for _, sra := range secondaryRelayAddresses {
for _, sra := range secondaryAddresses {
var normalizedSra = strings.ToLower(sra)
var pfxKey = dedupExp.ReplaceAllString(normalizedSra, "")

if _, exists := relayAddressPrefixToValue[pfxKey]; !exists {
relayAddressPrefixToValue[pfxKey] = normalizedSra
if _, exists := addressPrefixToValue[pfxKey]; !exists {
addressPrefixToValue[pfxKey] = normalizedSra
}
}

dedupedRelayAddresses = make([]string, 0, len(relayAddressPrefixToValue))
for _, value := range relayAddressPrefixToValue {
dedupedRelayAddresses = append(dedupedRelayAddresses, value)
dedupedAddresses = make([]string, 0, len(addressPrefixToValue))
for _, value := range addressPrefixToValue {
dedupedAddresses = append(dedupedAddresses, value)
}

return
}

func (wn *WebsocketNetwork) getDNSAddrs(dnsBootstrap string) (relaysAddresses []string, archiverAddresses []string) {
func (wn *WebsocketNetwork) getDNSAddrs(dnsBootstrap string) (relaysAddresses []string, archivalAddresses []string) {
var err error
relaysAddresses, err = wn.resolveSRVRecords(wn.ctx, "algobootstrap", "tcp", dnsBootstrap, wn.config.FallbackDNSResolverAddress, wn.config.DNSSecuritySRVEnforced())
if err != nil {
Expand All @@ -1896,13 +1892,13 @@ func (wn *WebsocketNetwork) getDNSAddrs(dnsBootstrap string) (relaysAddresses []
relaysAddresses = nil
}

archiverAddresses, err = wn.resolveSRVRecords(wn.ctx, "archive", "tcp", dnsBootstrap, wn.config.FallbackDNSResolverAddress, wn.config.DNSSecuritySRVEnforced())
archivalAddresses, err = wn.resolveSRVRecords(wn.ctx, "archive", "tcp", dnsBootstrap, wn.config.FallbackDNSResolverAddress, wn.config.DNSSecuritySRVEnforced())
if err != nil {
// only log this warning on testnet or devnet
if wn.NetworkID == config.Devnet || wn.NetworkID == config.Testnet {
wn.log.Warnf("Cannot lookup archive SRV record for %s: %v", dnsBootstrap, err)
}
archiverAddresses = nil
archivalAddresses = nil
}
return
}
Expand Down
27 changes: 14 additions & 13 deletions network/wsNetwork_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -1183,6 +1183,9 @@ func TestGetPeers(t *testing.T) {

phbMulti.ReplacePeerList([]string{"a", "b", "c"}, "ph", PhoneBookEntryRelayRole)

// A few for archival node roles
phbMulti.ReplacePeerList([]string{"d", "e", "f"}, "ph", PhoneBookEntryArchivalRole)

//addrB, _ := netB.Address()

// A has only an inbound connection from B
Expand All @@ -1206,14 +1209,13 @@ func TestGetPeers(t *testing.T) {
sort.Strings(expectAddrs)
assert.Equal(t, expectAddrs, peerAddrs)

// For now, PeersPhonebookArchivalNodes and PeersPhonebookRelays will return the same set of nodes
bPeers2 := netB.GetPeers(PeersPhonebookArchivalNodes)
peerAddrs2 := make([]string, len(bPeers2))
for pi2, peer2 := range bPeers2 {
peerAddrs2[pi2] = peer2.(HTTPPeer).GetAddress()
}
sort.Strings(peerAddrs2)
assert.Equal(t, expectAddrs, peerAddrs2)
assert.Equal(t, []string{"d", "e", "f"}, peerAddrs2)

}

Expand Down Expand Up @@ -4176,7 +4178,7 @@ func TestRefreshRelayArchivePhonebookAddresses(t *testing.T) {
relayPeers := netA.GetPeers(PeersPhonebookRelays)
assert.Equal(t, 0, len(relayPeers))

archivePeers := netA.GetPeers(PeersPhonebookArchivers)
archivePeers := netA.GetPeers(PeersPhonebookArchivalNodes)
assert.Equal(t, 0, len(archivePeers))

netA.refreshRelayArchivePhonebookAddresses()
Expand All @@ -4191,17 +4193,16 @@ func TestRefreshRelayArchivePhonebookAddresses(t *testing.T) {

assert.ElementsMatch(t, primaryRelayResolvedRecords, relayAddrs)

archivePeers = netA.GetPeers(PeersPhonebookArchivers)
archivePeers = netA.GetPeers(PeersPhonebookArchivalNodes)

// TODO: For the time being, we do not dedup resolved archive nodes
assert.Equal(t, len(primaryArchiveResolvedRecords)+len(secondaryArchiveResolvedRecords), len(archivePeers))
assert.Equal(t, 3, len(archivePeers))

archiveAddrs := make([]string, 0, len(archivePeers))
for _, peer := range archivePeers {
archiveAddrs = append(archiveAddrs, peer.(HTTPPeer).GetAddress())
}

assert.ElementsMatch(t, append(primaryArchiveResolvedRecords, secondaryArchiveResolvedRecords...), archiveAddrs)
assert.ElementsMatch(t, primaryArchiveResolvedRecords, archiveAddrs)

})
}
Expand All @@ -4219,7 +4220,7 @@ func TestUpdatePhonebookAddresses(t *testing.T) {
relayPeers := netA.GetPeers(PeersPhonebookRelays)
assert.Equal(t, 0, len(relayPeers))

archivePeers := netA.GetPeers(PeersPhonebookArchivers)
archivePeers := netA.GetPeers(PeersPhonebookArchivalNodes)
assert.Equal(t, 0, len(archivePeers))

domainGen := rapidgen.Domain()
Expand Down Expand Up @@ -4248,7 +4249,7 @@ func TestUpdatePhonebookAddresses(t *testing.T) {

assert.ElementsMatch(t, dedupedRelayDomains, relayAddrs)

archivePeers = netA.GetPeers(PeersPhonebookArchivers)
archivePeers = netA.GetPeers(PeersPhonebookArchivalNodes)
assert.Equal(t, len(dedupedArchiveDomains), len(archivePeers))

archiveAddrs := make([]string, 0, len(archivePeers))
Expand Down Expand Up @@ -4288,7 +4289,7 @@ func TestUpdatePhonebookAddresses(t *testing.T) {

assert.ElementsMatch(t, dedupedRelayDomains, relayAddrs)

archivePeers = netA.GetPeers(PeersPhonebookArchivers)
archivePeers = netA.GetPeers(PeersPhonebookArchivalNodes)
assert.Equal(t, len(dedupedArchiveDomains), len(archivePeers))

archiveAddrs = nil
Expand Down Expand Up @@ -4349,7 +4350,7 @@ func TestMergePrimarySecondaryRelayAddressListsMinOverlap(t *testing.T) {
primaryRelayAddresses := domainsGen.Draw(t1, "primaryRelayAddresses")
secondaryRelayAddresses := domainsGen.Draw(t1, "secondaryRelayAddresses")

mergedRelayAddresses := netA.mergePrimarySecondaryRelayAddressSlices(protocol.NetworkID(network),
mergedRelayAddresses := netA.mergePrimarySecondaryAddressSlices(
primaryRelayAddresses, secondaryRelayAddresses, dedupExp)

expectedRelayAddresses := removeDuplicateStr(append(primaryRelayAddresses, secondaryRelayAddresses...), true)
Expand Down Expand Up @@ -4402,7 +4403,7 @@ func TestMergePrimarySecondaryRelayAddressListsPartialOverlap(t *testing.T) {
}
secondaryRelayAddresses = append(secondaryRelayAddresses, extraSecondaryRelayAddresses...)

mergedRelayAddresses := netA.mergePrimarySecondaryRelayAddressSlices(network,
mergedRelayAddresses := netA.mergePrimarySecondaryAddressSlices(
primaryRelayAddresses, secondaryRelayAddresses, dedupExp)

// We expect the primary addresses to take precedence over a "matching" secondary address, extra non-duplicate
Expand Down Expand Up @@ -4445,7 +4446,7 @@ func TestMergePrimarySecondaryRelayAddressListsNoDedupExp(t *testing.T) {
generatedSecondaryRelayAddresses := secondaryDomainsGen.Draw(t1, "secondaryRelayAddresses")
secondaryRelayAddresses = append(secondaryRelayAddresses, generatedSecondaryRelayAddresses...)

mergedRelayAddresses := netA.mergePrimarySecondaryRelayAddressSlices(protocol.NetworkID(network),
mergedRelayAddresses := netA.mergePrimarySecondaryAddressSlices(
primaryRelayAddresses, secondaryRelayAddresses, nil)

// We expect non deduplication, so all addresses _should_ be present (note that no lower casing happens either)
Expand Down

0 comments on commit 787f758

Please sign in to comment.