Skip to content

Conversation

@vishalchangrani
Copy link
Contributor

@vishalchangrani vishalchangrani commented Jun 19, 2025

FLIP PR: onflow/flips#333
FLIP Issue: onflow/flips#332

@vishalchangrani vishalchangrani self-assigned this Jun 19, 2025
Comment on lines 1847 to 1866
// Check if domain is an IP address (contains only numbers and dots)
let domainParts = domain.split(separator: ".")
if domainParts.length == 4 {
var isIP = true
for part in domainParts {
let num = UInt8.fromString(part)
if num == nil || num! > 255 {
isIP = false
break
}
}
if isIP {
return false
}
}

// Check if domain has at least one dot and valid characters
if !domain.contains(".") {
return false
}
Copy link
Contributor

@bluesign bluesign Jun 20, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

here better way is to check last segment is not numeric. ( ip has a lot of quirks: 51.5499081 is a valid IP address for example )

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

excellent suggestion!

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I have update the changes to be more robust.

Comment on lines 1869 to 1882
for char in domain.utf8 {
// Check if character is:
// - a letter (a-z, A-Z)
// - a number (0-9)
// - a dot (.)
// - a hyphen (-)
if !((char >= 48 && char <= 57) || // numbers
(char >= 65 && char <= 90) || // uppercase letters
(char >= 97 && char <= 122) || // lowercase letters
char == 46 || // dot
char == 45) { // hyphen
return false
}
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

maybe something like this ?

var validChars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789.-".utf8
for char in domain.utf8{
    if !validChars.contains(char) {
        return false
    }
}

@vishalchangrani vishalchangrani marked this pull request as ready for review June 21, 2025 00:18
IDTableSigner,
adminID,
fmt.Sprintf("%0128d", admin),
// Invalid Networking Address: IPv4 address instead of domain name
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Unit tests to check that IPv4, IPv6, missing port and invalid domain are caught by the validation.

…ving length check to inside the network address validation
FlowIDTableStaking.isValidNodeID(id): "The node ID must have only numbers and lowercase hex characters"
FlowIDTableStaking.nodes[id] == nil: "The ID cannot already exist in the record"
role >= UInt8(1) && role <= UInt8(5): "The role must be 1, 2, 3, 4, or 5"
networkingAddress.length > 0 && networkingAddress.length <= 510: "The networkingAddress must be less than 510 characters"
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

moved this check inside of the isValidateNetworkingAddress function.

@vishalchangrani vishalchangrani requested a review from bluesign June 21, 2025 20:06
@vishalchangrani vishalchangrani changed the title Draft implementation for FLIP 321: Stricter Validation of Node Network Addresses Implementation for FLIP 321: Stricter Validation of Node Network Addresses Jun 23, 2025
true)
})

t.Run("Shouldn't be able to create Node struct with an invalid networking address", func(t *testing.T) {
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

separated out the test for networking address in it's own unit test.

/// 1. Must not be an IP address
/// 2. Must contain a port number after a colon
/// 3. Must be a valid domain name format
access(contract) view fun isValidNetworkingAddress(address: String): Bool {
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

what do you guys think about making this access(all) instead access(contract) so that any client side apps like FlowPort can use it to verify the networking address before submitting the transaction.
The function does not modify state so should be safe to expose.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yes, we should make this access(all)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

changed it to access(all).

@joshuahannan
Copy link
Member

@vishalchangrani Are you ready to merge this? After we merge it, I can update flow-go and we can schedule the upgrades

Comment on lines +1857 to +1862
let validChars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789.-".utf8
for char in domain.utf8 {
if !validChars.contains(char) {
return false
}
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This might be quite inefficient: The lookup array of valid chars is re-built on each call of the function, and the loop ends up effectively performing a nested linear scan: first over all characters of the domain, and then again nested over all valid characters.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The original implementation was way more efficient: 62f22a4 (#484)

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@turbolent Actually I suggested this one for readability mostly, didn't seem to be too inefficient at first sight. ( ~ maybe half efficient ) and I assumed this would be called once ofc.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah, I see now! Nice 👍 Yeah, this is definitely more readable, the original implementation is not very obvious. I don't know how often this will be called. In case it is a heavily called function, we might want to trade some readability / "straight-forwardness" code for performance.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This validation will only be called when a new node is registered or when the networking address of an existing node is update. Both of those actions don't happen frequently - once or twice every couple of epoch at most. Hence, I would rather have more readability than performance. Open to suggestions tho.

@vishalchangrani
Copy link
Contributor Author

@vishalchangrani Are you ready to merge this? After we merge it, I can update flow-go and we can schedule the upgrades

Do we generally first make a release of flow-go and then test it on testnet or the other way round? I was planning on testing this on testnet this week.

@vishalchangrani vishalchangrani changed the title Implementation for FLIP 321: Stricter Validation of Node Network Addresses Implementation for FLIP 332: Stricter Validation of Node Network Addresses Jul 9, 2025
@joshuahannan
Copy link
Member

@vishalchangrani We try to merge right before we upgrade on testnet and create a release for flow-go before we upgrade on mainnet.

@vishalchangrani
Copy link
Contributor Author

@vishalchangrani We try to merge right before we upgrade on testnet and create a release for flow-go before we upgrade on mainnet.

I plan to upgrade on testnet by EOD tomorrow. Will merge this in before that.

@joshuahannan joshuahannan merged commit 9a2b5eb into master Jul 11, 2025
2 checks passed
@joshuahannan joshuahannan deleted the vishal/networaddress_validation branch July 11, 2025 18:42
@vishalchangrani
Copy link
Contributor Author

Updated staking contract on testnet with this change and tested it out. Works as expected.

  1. IP address without port - fails
    https://testnet.flowscan.io/tx/7a1f5ebcde374f69447850280e76fd51638bd9f1f73559a624061be42e2ec6fe?tab=script

  2. IP address with port - fails
    https://testnet.flowscan.io/tx/33ac6e2c5b3885b39294fe96e65ce4145010ad69d6e708a2191fa5edd25efb3b?tab=script

  3. Domain name but missing port - fails
    https://testnet.flowscan.io/tx/7f3b728d924f53998de1e3294fd228a3b54e981171b2f85c09d740b624319593?tab=script

  4. Valid networking address (happy path) - succeeds
    https://testnet.flowscan.io/tx/b433402bde3dc7638052b57840271f48d568c19400cac6edbfa33c02a44fd8cc?tab=script

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants