-
Notifications
You must be signed in to change notification settings - Fork 33
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
[P2P] KISS 4 - Basic nodes bootstrapping [Merge me after #522] - (Iss…
…ue: #498) (#523) ## Description This PR has been extracted from #491 and is, hopefully, more digestible from a code-review and scope point of view. This simply adds entrypoints and basic logic for node bootstrapping leveraging the finite state machine ## Issue Fixes #498 ## Type of change Please mark the relevant option(s): - [x] New feature, functionality or library - [ ] Bug fix - [ ] Code health or cleanup - [ ] Major breaking change - [ ] Documentation - [ ] Other <!-- add details here if it a different type of change --> ## List of changes - Ability to fetch an addressbook from a bootstrap node(s) (by convention the first validator in LocalNet) - It's possible to override bootstrap nodes via CLI flag ## Testing - [x] `make develop_test` - [x] [LocalNet](https://github.com/pokt-network/pocket/blob/main/docs/development/README.md) w/ all of the steps outlined in the `README` ## Required Checklist - [x] I have performed a self-review of my own code - [x] I have commented my code, particularly in hard-to-understand areas - [x] I have tested my changes using the available tooling - [x] I have updated the corresponding CHANGELOG ### If Applicable Checklist - [ ] I have updated the corresponding README(s); local and/or global - [ ] I have added tests that prove my fix is effective or that my feature works - [ ] I have added, or updated, [mermaid.js](https://mermaid-js.github.io) diagrams in the corresponding README(s) - [ ] I have added, or updated, documentation and [mermaid.js](https://mermaid-js.github.io) diagrams in `shared/docs/*` if I updated `shared/*`README(s) --------- Signed-off-by: Alessandro De Blasis <alex@deblasis.net> Co-authored-by: Dmitry K <okdas@pm.me> Co-authored-by: Dmitry Knyazev <okdas@users.noreply.github.com> Co-authored-by: Daniel Olshansky <olshansky@pokt.network> Co-authored-by: Daniel Olshansky <olshansky.daniel@gmail.com>
- Loading branch information
1 parent
6da420c
commit b3ed290
Showing
16 changed files
with
318 additions
and
12 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,105 @@ | ||
package p2p | ||
|
||
import ( | ||
"context" | ||
"encoding/csv" | ||
"fmt" | ||
"net/http" | ||
"regexp" | ||
"strconv" | ||
"strings" | ||
|
||
rpcABP "github.com/pokt-network/pocket/p2p/providers/addrbook_provider/rpc" | ||
rpcCHP "github.com/pokt-network/pocket/p2p/providers/current_height_provider/rpc" | ||
typesP2P "github.com/pokt-network/pocket/p2p/types" | ||
"github.com/pokt-network/pocket/rpc" | ||
"github.com/pokt-network/pocket/runtime/defaults" | ||
) | ||
|
||
// configureBootstrapNodes parses the bootstrap nodes from the config and validates them | ||
func (m *p2pModule) configureBootstrapNodes() error { | ||
p2pCfg := m.GetBus().GetRuntimeMgr().GetConfig().P2P | ||
|
||
bootstrapNodesCsv := strings.Trim(p2pCfg.BootstrapNodesCsv, " ") | ||
if bootstrapNodesCsv == "" { | ||
bootstrapNodesCsv = defaults.DefaultP2PBootstrapNodesCsv | ||
} | ||
csvReader := csv.NewReader(strings.NewReader(bootstrapNodesCsv)) | ||
bootStrapNodes, err := csvReader.Read() | ||
if err != nil { | ||
return fmt.Errorf("error parsing bootstrap nodes: %w", err) | ||
} | ||
|
||
// validate the bootstrap nodes | ||
for i, node := range bootStrapNodes { | ||
bootStrapNodes[i] = strings.Trim(node, " ") | ||
if !isValidHostnamePort(bootStrapNodes[i]) { | ||
return fmt.Errorf("invalid bootstrap node: %s", bootStrapNodes[i]) | ||
} | ||
} | ||
m.bootstrapNodes = bootStrapNodes | ||
return nil | ||
} | ||
|
||
// bootstrap attempts to bootstrap from a bootstrap node | ||
func (m *p2pModule) bootstrap() error { | ||
var addrBook typesP2P.AddrBook | ||
|
||
for _, bootstrapNode := range m.bootstrapNodes { | ||
m.logger.Info().Str("endpoint", bootstrapNode).Msg("Attempting to bootstrap from bootstrap node") | ||
|
||
client, err := rpc.NewClientWithResponses(bootstrapNode) | ||
if err != nil { | ||
continue | ||
} | ||
healthCheck, err := client.GetV1Health(context.TODO()) | ||
if err != nil || healthCheck == nil || healthCheck.StatusCode != http.StatusOK { | ||
m.logger.Warn().Str("bootstrapNode", bootstrapNode).Msg("Error getting a green health check from bootstrap node") | ||
continue | ||
} | ||
|
||
addressBookProvider := rpcABP.NewRPCAddrBookProvider( | ||
rpcABP.WithP2PConfig( | ||
m.GetBus().GetRuntimeMgr().GetConfig().P2P, | ||
), | ||
rpcABP.WithCustomRPCUrl(bootstrapNode), | ||
) | ||
|
||
currentHeightProvider := rpcCHP.NewRPCCurrentHeightProvider(rpcCHP.WithCustomRPCUrl(bootstrapNode)) | ||
|
||
addrBook, err = addressBookProvider.GetStakedAddrBookAtHeight(currentHeightProvider.CurrentHeight()) | ||
if err != nil { | ||
m.logger.Warn().Err(err).Str("endpoint", bootstrapNode).Msg("Error getting address book from bootstrap node") | ||
continue | ||
} | ||
} | ||
|
||
if len(addrBook) == 0 { | ||
return fmt.Errorf("bootstrap failed") | ||
} | ||
|
||
for _, peer := range addrBook { | ||
m.logger.Debug().Str("address", peer.Address.String()).Msg("Adding peer to addrBook") | ||
if err := m.network.AddPeerToAddrBook(peer); err != nil { | ||
return err | ||
} | ||
} | ||
return nil | ||
} | ||
|
||
func isValidHostnamePort(str string) bool { | ||
pattern := regexp.MustCompile(`^(https?)://([a-zA-Z0-9.-]+):(\d{1,5})$`) | ||
matches := pattern.FindStringSubmatch(str) | ||
if len(matches) != 4 { | ||
return false | ||
} | ||
protocol := matches[1] | ||
if protocol != "http" && protocol != "https" { | ||
return false | ||
} | ||
port, err := strconv.Atoi(matches[3]) | ||
if err != nil || port < 0 || port > 65535 { | ||
return false | ||
} | ||
return true | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,129 @@ | ||
package p2p | ||
|
||
import ( | ||
"strings" | ||
"testing" | ||
|
||
"github.com/golang/mock/gomock" | ||
"github.com/pokt-network/pocket/runtime/configs" | ||
"github.com/pokt-network/pocket/runtime/defaults" | ||
cryptoPocket "github.com/pokt-network/pocket/shared/crypto" | ||
"github.com/pokt-network/pocket/shared/modules" | ||
mockModules "github.com/pokt-network/pocket/shared/modules/mocks" | ||
"github.com/stretchr/testify/require" | ||
) | ||
|
||
func Test_Create_configureBootstrapNodes(t *testing.T) { | ||
defaultBootstrapNodes := strings.Split(defaults.DefaultP2PBootstrapNodesCsv, ",") | ||
key := cryptoPocket.GetPrivKeySeed(1) | ||
|
||
type args struct { | ||
initialBootstrapNodesCsv string | ||
} | ||
tests := []struct { | ||
name string | ||
args args | ||
wantBootstrapNodes []string | ||
wantErr bool | ||
}{ | ||
{ | ||
name: "unset boostrap nodes should yield no error and return DefaultP2PBootstrapNodes", | ||
args: args{}, | ||
wantErr: false, | ||
wantBootstrapNodes: defaultBootstrapNodes, | ||
}, | ||
{ | ||
name: "empty string boostrap nodes should yield no error and return DefaultP2PBootstrapNodes", | ||
args: args{ | ||
initialBootstrapNodesCsv: "", | ||
}, | ||
wantErr: false, | ||
wantBootstrapNodes: defaultBootstrapNodes, | ||
}, | ||
{ | ||
name: "untrimmed empty string boostrap nodes should yield no error and return DefaultP2PBootstrapNodes", | ||
args: args{ | ||
initialBootstrapNodesCsv: " ", | ||
}, | ||
wantErr: false, | ||
wantBootstrapNodes: defaultBootstrapNodes, | ||
}, | ||
{ | ||
name: "untrimmed string boostrap nodes should yield no error and return the trimmed urls", | ||
args: args{ | ||
initialBootstrapNodesCsv: " http://somenode:50832 , http://someothernode:50832 ", | ||
}, | ||
wantErr: false, | ||
wantBootstrapNodes: []string{"http://somenode:50832", "http://someothernode:50832"}, | ||
}, | ||
{ | ||
name: "custom bootstrap nodes should yield no error and return the custom bootstrap node", | ||
args: args{ | ||
initialBootstrapNodesCsv: "http://somenode:50832,http://someothernode:50832", | ||
}, | ||
wantBootstrapNodes: []string{"http://somenode:50832", "http://someothernode:50832"}, | ||
wantErr: false, | ||
}, | ||
{ | ||
name: "malformed bootstrap nodes string should yield an error and return nil", | ||
args: args{ | ||
initialBootstrapNodesCsv: "\n\n", | ||
}, | ||
wantBootstrapNodes: []string(nil), | ||
wantErr: true, | ||
}, | ||
{ | ||
name: "port number too high yields an error and empty list of bootstrap nodes", | ||
args: args{ | ||
initialBootstrapNodesCsv: "http://somenode:99999", | ||
}, | ||
wantBootstrapNodes: []string(nil), | ||
wantErr: true, | ||
}, | ||
{ | ||
name: "negative port number yields an error and empty list of bootstrap nodes", | ||
args: args{ | ||
initialBootstrapNodesCsv: "udp://somenode:-8080", | ||
}, | ||
wantBootstrapNodes: []string(nil), | ||
wantErr: true, | ||
}, | ||
{ | ||
name: "wrong protocol yields an error and empty list of bootstrap nodes", | ||
args: args{ | ||
initialBootstrapNodesCsv: "udp://somenode:58884", | ||
}, | ||
wantBootstrapNodes: []string(nil), | ||
wantErr: true, | ||
}, | ||
} | ||
|
||
for _, tt := range tests { | ||
t.Run(tt.name, func(t *testing.T) { | ||
ctrl := gomock.NewController(t) | ||
mockRuntimeMgr := mockModules.NewMockRuntimeMgr(ctrl) | ||
mockBus := createMockBus(t, mockRuntimeMgr) | ||
mockConsensusModule := mockModules.NewMockConsensusModule(ctrl) | ||
mockBus.EXPECT().GetConsensusModule().Return(mockConsensusModule).AnyTimes() | ||
mockRuntimeMgr.EXPECT().GetConfig().Return(&configs.Config{ | ||
PrivateKey: key.String(), | ||
P2P: &configs.P2PConfig{ | ||
BootstrapNodesCsv: tt.args.initialBootstrapNodesCsv, | ||
PrivateKey: key.String(), | ||
}, | ||
}).AnyTimes() | ||
mockBus.EXPECT().GetRuntimeMgr().Return(mockRuntimeMgr).AnyTimes() | ||
|
||
var p2pMod modules.Module | ||
p2pMod, err := Create(mockBus) | ||
if (err != nil) != tt.wantErr { | ||
t.Errorf("p2pModule.Create() error = %v, wantErr %v", err, tt.wantErr) | ||
} | ||
|
||
if !tt.wantErr { | ||
actualBootstrapNodes := p2pMod.(*p2pModule).bootstrapNodes | ||
require.EqualValues(t, tt.wantBootstrapNodes, actualBootstrapNodes) | ||
} | ||
}) | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.