diff --git a/AllTests-mainnet.md b/AllTests-mainnet.md index 56b8e5dc6..4437aba75 100644 --- a/AllTests-mainnet.md +++ b/AllTests-mainnet.md @@ -570,6 +570,11 @@ OK: 24/24 Fail: 0/24 Skip: 0/24 + BeaconBlockType OK ``` OK: 1/1 Fail: 0/1 Skip: 0/1 +## Validator Client test suite +```diff ++ normalizeUri() test vectors OK +``` +OK: 1/1 Fail: 0/1 Skip: 0/1 ## Validator change pool testing suite ```diff + addValidatorChangeMessage/getAttesterSlashingMessage OK @@ -680,4 +685,4 @@ OK: 2/2 Fail: 0/2 Skip: 0/2 OK: 9/9 Fail: 0/9 Skip: 0/9 ---TOTAL--- -OK: 389/394 Fail: 0/394 Skip: 5/394 +OK: 390/395 Fail: 0/395 Skip: 5/395 diff --git a/beacon_chain/validator_client/common.nim b/beacon_chain/validator_client/common.nim index a3e7385e9..b53b1fa01 100644 --- a/beacon_chain/validator_client/common.nim +++ b/beacon_chain/validator_client/common.nim @@ -501,44 +501,44 @@ proc parseRoles*(data: string): Result[set[BeaconNodeRole], cstring] = return err("Invalid beacon node role string found") ok(res) -proc normalizeUri*(remoteUri: Uri): Uri = - var r = remoteUri - if (len(r.scheme) == 0) and (len(r.username) == 0) and - (len(r.password) == 0) and (len(r.hostname) == 0) and - (len(r.port) == 0) and (len(r.path) > 0): - # When `scheme` is not specified and `port` is not specified - whole - # hostname is stored in `path`.`query` and `anchor` could still be present. - # test.com - # test.com?q=query - # test.com?q=query#anchor=anchor - parseUri("http://" & $remoteUri & ":" & $defaultEth2RestPort) - elif (len(r.scheme) > 0) and (len(r.username) == 0) and - (len(r.password) == 0) and (len(r.hostname) == 0) and - (len(r.port) == 0) and (len(r.path) > 0): - # When `scheme` is not specified but `port` is specified - whole - # hostname is stored in `scheme` and `port` is in `path`. - # 192.168.0.1:5052 - # test.com:5052 - # test.com:5052?q=query - # test.com:5052?q=query#anchor=anchor - parseUri("http://" & $remoteUri) - elif (len(r.scheme) > 0) and (len(r.hostname) > 0): - # When `scheme` is specified, but `port` is not we use default. - # http://192.168.0.1 - # http://test.com - # http://test.com?q=query - # http://test.com?q=query#anchor=anchor - if len(r.port) == 0: r.port = $defaultEth2RestPort - r - else: - remoteUri +proc normalizeUri*(r: Uri): Result[Uri, cstring] = + const + MissingPortNumber = cstring("Missing port number") + MissingHostname = cstring("Missing hostname") + UnknownScheme = cstring("Unknown scheme value") + + if ($r).toLowerAscii().startsWith("http://") or + ($r).toLowerAscii().startsWith("https://"): + # When a scheme is provided, only a hostname is required + if len(r.hostname) == 0: return err(MissingHostname) + return ok(r) + + # Check for unknown scheme + if ($r).contains("://"): + return err(UnknownScheme) + + # Add the default scheme (http) + let normalized = + if ($r).startsWith("//"): + parseUri("http:" & $r) + else: + parseUri("http://" & $r) + + if len(normalized.hostname) == 0: + return err(MissingHostname) + + if len(normalized.port) == 0: + return err(MissingPortNumber) + + ok(normalized) proc init*(t: typedesc[BeaconNodeServerRef], remote: Uri, index: int): Result[BeaconNodeServerRef, string] = doAssert(index >= 0) let flags = {RestClientFlag.CommaSeparatedArray} - remoteUri = normalizeUri(remote) + remoteUri = normalizeUri(remote).valueOr: + return err("Invalid URL: " & $error) client = block: let res = RestClientRef.new($remoteUri, flags = flags) diff --git a/tests/all_tests.nim b/tests/all_tests.nim index a37985d65..e49a51e77 100644 --- a/tests/all_tests.nim +++ b/tests/all_tests.nim @@ -49,7 +49,8 @@ import # Unit test ./test_signing_node, ./consensus_spec/all_tests as consensus_all_tests, ./slashing_protection/test_fixtures, - ./slashing_protection/test_slashing_protection_db + ./slashing_protection/test_slashing_protection_db, + ./test_validator_client when not defined(i386): # Avoids "Out of memory" CI failures diff --git a/tests/test_validator_client.nim b/tests/test_validator_client.nim new file mode 100644 index 000000000..e798dc896 --- /dev/null +++ b/tests/test_validator_client.nim @@ -0,0 +1,151 @@ +# beacon_chain +# Copyright (c) 2018-2023 Status Research & Development GmbH +# Licensed and distributed under either of +# * MIT license (license terms in the root directory or at https://opensource.org/licenses/MIT). +# * Apache v2 license (license terms in the root directory or at https://www.apache.org/licenses/LICENSE-2.0). +# at your option. This file may not be copied, modified, or distributed except according to those terms. + +{.push raises: [].} +{.used.} + +import std/strutils +import unittest2 +import ../beacon_chain/validator_client/common + +const + HostNames = [ + "[2001:db8::1]", + "127.0.0.1", + "hostname.com", + "localhost", + "username:password@[2001:db8::1]", + "username:password@127.0.0.1", + "username:password@hostname.com", + "username:password@localhost", + ] + + GoodTestVectors = [ + ("http://$1", + "ok(http://$1)"), + ("http://$1?q=query", + "ok(http://$1?q=query)"), + ("http://$1?q=query#anchor", + "ok(http://$1?q=query#anchor)"), + ("http://$1/subpath/", + "ok(http://$1/subpath/)"), + ("http://$1/subpath/q=query", + "ok(http://$1/subpath/q=query)"), + ("http://$1/subpath/q=query#anchor", + "ok(http://$1/subpath/q=query#anchor)"), + ("http://$1/subpath", + "ok(http://$1/subpath)"), + ("http://$1/subpath?q=query", + "ok(http://$1/subpath?q=query)"), + ("http://$1/subpath?q=query#anchor", + "ok(http://$1/subpath?q=query#anchor)"), + + ("https://$1", + "ok(https://$1)"), + ("https://$1?q=query", + "ok(https://$1?q=query)"), + ("https://$1?q=query#anchor", + "ok(https://$1?q=query#anchor)"), + ("https://$1/subpath/", + "ok(https://$1/subpath/)"), + ("https://$1/subpath/q=query", + "ok(https://$1/subpath/q=query)"), + ("https://$1/subpath/q=query#anchor", + "ok(https://$1/subpath/q=query#anchor)"), + ("https://$1/subpath", + "ok(https://$1/subpath)"), + ("https://$1/subpath?q=query", + "ok(https://$1/subpath?q=query)"), + ("https://$1/subpath?q=query#anchor", + "ok(https://$1/subpath?q=query#anchor)"), + + ("$1:5052", + "ok(http://$1:5052)"), + ("$1:5052?q=query", + "ok(http://$1:5052?q=query)"), + ("$1:5052?q=query#anchor", + "ok(http://$1:5052?q=query#anchor)"), + ("$1:5052/subpath/", + "ok(http://$1:5052/subpath/)"), + ("$1:5052/subpath/q=query", + "ok(http://$1:5052/subpath/q=query)"), + ("$1:5052/subpath/q=query#anchor", + "ok(http://$1:5052/subpath/q=query#anchor)"), + ("$1:5052/subpath", + "ok(http://$1:5052/subpath)"), + ("$1:5052/subpath?q=query", + "ok(http://$1:5052/subpath?q=query)"), + ("$1:5052/subpath?q=query#anchor", + "ok(http://$1:5052/subpath?q=query#anchor)"), + + ("bnode://$1:5052", + "err(Unknown scheme value)"), + ("bnode://$1:5052?q=query", + "err(Unknown scheme value)"), + ("bnode://$1:5052?q=query#anchor", + "err(Unknown scheme value)"), + ("bnode://$1:5052/subpath/", + "err(Unknown scheme value)"), + ("bnode://$1:5052/subpath/q=query", + "err(Unknown scheme value)"), + ("bnode://$1:5052/subpath/q=query#anchor", + "err(Unknown scheme value)"), + ("bnode://$1:5052/subpath", + "err(Unknown scheme value)"), + ("bnode://$1:5052/subpath?q=query", + "err(Unknown scheme value)"), + ("bnode://$1:5052/subpath?q=query#anchor", + "err(Unknown scheme value)"), + + ("//$1:5052", + "ok(http://$1:5052)"), + ("//$1:5052?q=query", + "ok(http://$1:5052?q=query)"), + ("//$1:5052?q=query#anchor", + "ok(http://$1:5052?q=query#anchor)"), + ("//$1:5052/subpath/", + "ok(http://$1:5052/subpath/)"), + ("//$1:5052/subpath/q=query", + "ok(http://$1:5052/subpath/q=query)"), + ("//$1:5052/subpath/q=query#anchor", + "ok(http://$1:5052/subpath/q=query#anchor)"), + ("//$1:5052/subpath", + "ok(http://$1:5052/subpath)"), + ("//$1:5052/subpath?q=query", + "ok(http://$1:5052/subpath?q=query)"), + ("//$1:5052/subpath?q=query#anchor", + "ok(http://$1:5052/subpath?q=query#anchor)"), + + ("//$1", "err(Missing port number)"), + ("//$1?q=query", "err(Missing port number)"), + ("//$1?q=query#anchor", "err(Missing port number)"), + ("//$1/subpath/", "err(Missing port number)"), + ("//$1/subpath/q=query", "err(Missing port number)"), + ("//$1/subpath/q=query#anchor", "err(Missing port number)"), + ("//$1/subpath", "err(Missing port number)"), + ("//$1/subpath?q=query", "err(Missing port number)"), + ("//$1/subpath?q=query#anchor", "err(Missing port number)"), + + ("$1", "err(Missing port number)"), + ("$1?q=query", "err(Missing port number)"), + ("$1?q=query#anchor", "err(Missing port number)"), + ("$1/subpath/", "err(Missing port number)"), + ("$1/subpath/q=query", "err(Missing port number)"), + ("$1/subpath/q=query#anchor", "err(Missing port number)"), + ("$1/subpath", "err(Missing port number)"), + ("$1/subpath?q=query", "err(Missing port number)"), + ("$1/subpath?q=query#anchor", "err(Missing port number)"), + + ("", "err(Missing hostname)") + ] + +suite "Validator Client test suite": + test "normalizeUri() test vectors": + for hostname in HostNames: + for vector in GoodTestVectors: + let expect = vector[1] % (hostname) + check $normalizeUri(parseUri(vector[0] % (hostname))) == expect