-
Notifications
You must be signed in to change notification settings - Fork 71
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
chore(docs): ADR for enforcing ArNS undername limits #272
Merged
Merged
Changes from all commits
Commits
Show all changes
15 commits
Select commit
Hold shift + click to select a range
4d4e539
chore(docs): ADR for enforcing ArNS undername limits
f9b61b6
chore(docs): update ArNS ADR with details about existing response types
eace912
chore(docs): add additional pro cons to options
e922573
chore(docs): cleanup ADR
2b39590
chore(docs): add comment about TTL
974b07d
chore(docs): clarify deceision and cons, update example
a6b63e0
chore(docs): consolidate examples for options
7284e89
chore(docs): consolidate pro/cons
c12db69
chore(docs): clarify deceision
61d298f
chore(docs): add next steps for required work
b1fa414
chore(docs): fix typo
72c6049
chore(docs): update return code to 402
6566c42
chore(docs): address typos
90be780
chore(adr): cleanup ADR comments, update links
377a1db
chore(docs): update example enforcement snippet
dtfiedler File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,235 @@ | ||
# ArNS Undername Limit Enforcement | ||
|
||
- Status: accepted | ||
- Deciders: [Ariel], [Dylan], [Atticus], [Phil], [David], [Karl] | ||
- Date: 2025-01-14 | ||
- Authors: [Dylan] | ||
|
||
## Context and Problem Statement | ||
|
||
ArNS names have a supported undername limit defined by the ARIO network contract. Increasing this limit requires payment in $ARIO tokens to compensate gateway operators for the additional computational resources needed to serve undernames and promote responsible usage of ArNS. AR.IO gateways must enforce this limit when resolving ArNS names to ensure operators are fairly rewarded for their services through fees paid by increasing a name's undername limit and providing a consistent experience across the network. | ||
|
||
Currently, the `getRecords` API for an ANT returns **a table of records** allowing for efficient undername lookups. Lua does not guarantee the order of keys in a table, making it difficult to know what undernames to enforce. Sorting needs to either 1. be done by the ANT and provided as an array of records, or 2. be done by the [ar-io-node] using additional information provided by the ANT. Additionally, caching within the ar-io-node needs to properly handle these priority order changes. | ||
|
||
It's also important to note that ANTs can not generally be trusted to return the same values all the time. This is due to the fact that ANTs are not deployed as part of the AR.IO network and are instead deployed by third parties. This means that ANTs can be updated by their owners and may return different values for the same name on different gateways. ar-io-nodes must be able to handle these changes gracefully and ensure that undername limits are enforced consistently across the network. | ||
|
||
## Decision Outcome | ||
|
||
### Priority Resolution Flow | ||
|
||
The following sequence diagram is used to demonstrate how AR.IO gateways resolve ArNS names and enforce undername limits. | ||
|
||
```mermaid | ||
sequenceDiagram | ||
participant Client | ||
participant AR.IO Gateway | ||
participant ARIOContract | ||
participant ANTContract | ||
|
||
Client->>AR.IO Gateway: ArNS Name Request | ||
AR.IO Gateway->>ANTContract: Check ANT records with priority | ||
ANTContract->>AR.IO Gateway: Return records with priority data | ||
AR.IO Gateway->>ARIOContract: Get undername limit | ||
ARIOContract->>AR.IO Gateway: Return undername limit | ||
note over AR.IO Gateway: Sort records by ANT priority, fallback to alphabetical | ||
note over AR.IO Gateway: Validate undernames against undername limit | ||
alt Undername limit exceeded | ||
AR.IO Gateway->>Client: Return 402 (Payment required) | ||
else Undername limit not exceeded | ||
AR.IO Gateway->>Client: Resolve undername | ||
end | ||
``` | ||
|
||
## Decision Drivers | ||
|
||
- Honor ANT priority ordering when available | ||
- Consistent enforcement of undername limits across AR.IO gateways | ||
- Simple fallback mechanism if ANT does not return or contain priority data | ||
- Respects existing ANT records object type (minimal changes to [ar-io-node] & [ar-io-sdk]) | ||
|
||
## Considered Options | ||
|
||
### Option 1: ANTs provide sorted array of records | ||
|
||
Lua does not guarantee the order of keys in a table. This raises some issues with the existing `getRecords` API that returns a table of records on an ANT. To circumvent this, we could modify the return type of `getRecords` or introduce a new API to a sorted array of records, based on the ANT priority. This would require a change to the [ar-io-sdk] and [ar-io-node] to leverage this updated API. | ||
|
||
ANT process with updated logic: | ||
|
||
```lua | ||
Records = ( | ||
'@': { | ||
transactionId: '0x123', | ||
ttlSeconds: 900 | ||
} | ||
) | ||
function getRecords(name: string) | ||
return { | ||
-- order of keys is not guaranteed in lua table | ||
["@"] = { transactionId = "0x123", ttlSeconds = 1000000, priority = 0 }, | ||
["undername1"] = { transactionId = "0x123", ttlSeconds = 1000000, priority = 1 }, | ||
["undername2"] = { transactionId = "0x123", ttlSeconds = 1000000, priority = 2 } | ||
} | ||
end | ||
|
||
--- new logic to sort records based on some sort attributes | ||
function getSortedRecords(name: string): | ||
local sortedRecords = {} | ||
-- ANT decides how to sort records | ||
for key, value in pairs(Records) do | ||
table.insert(sortedRecords, { | ||
record = key, | ||
transactionId = value.transactionId, | ||
ttlSeconds = value.ttlSeconds, | ||
}) | ||
end | ||
-- apply sort based on some attribute of the ANT (e.g. alphabetical) | ||
table.sort(sortedRecords, function(a, b) | ||
-- '@' record should always be first | ||
if a.record == "@" then return true end | ||
if b.record == "@" then return false end | ||
return a.record > b.record | ||
end) | ||
return sortedRecords | ||
end | ||
``` | ||
|
||
This array of sorted records would then be what the [ar-io-node] receives and enforces against the undername limit. | ||
|
||
### Option 2: ANTS provide priority data in records | ||
dtfiedler marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
||
ANTs store additional information in the state for each record, indicating the priority of a name. The [ar-io-node] would respect this priority when resolving undernames. If the ANT does not return priority attribute for a record, the [ar-io-node] would sort undernames lexicographically (alphabetically, character by character). Any record with a priority will sort higher than any record without a priority. | ||
|
||
Example (in the [ar-io-node]): | ||
|
||
|
||
### Option 3: ANT provides sort attributes via separate API | ||
|
||
ANTs provide a global `sortOrder` and `sortKey` field to determine how names are sorted on existing records keys. | ||
|
||
Example: | ||
|
||
ANT process: | ||
|
||
```lua | ||
UndernamePriorityAttributes = { | ||
sortOrder = 'desc', | ||
sortKey = "name" | ||
} | ||
|
||
function getRecords(name: string) | ||
return { | ||
-- order of keys is not guaranteed in lua table | ||
["@"] = { transactionId = "0x123", ttlSeconds = 1000000, priority = 0 }, | ||
["undername1"] = { transactionId = "0x123", ttlSeconds = 1000000, priority = 1 }, | ||
["undername2"] = { transactionId = "0x123", ttlSeconds = 1000000, priority = 2 } | ||
} | ||
end | ||
``` | ||
|
||
Gateway enforcement: | ||
|
||
```typescript | ||
const records = ant.getRecords(name); | ||
const { sortOrder, sortKey } = ant.getPriorityAttributes(); | ||
const sortedRecords = Object.entries(records).sort(([a], [b]) => { | ||
if (sortOrder in a && sortOrder in b) { | ||
if (sortOrder === 'desc') { | ||
return a[sortKey] - b[sortKey]; | ||
} else { | ||
return b[sortKey] - a[sortKey]; | ||
} | ||
} | ||
return a.localeCompare(b); | ||
}); | ||
|
||
// enforce undername limit against sorted records, using the priority field, fallback to alphabetical | ||
``` | ||
dtfiedler marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
||
## Pros and Cons of Options | ||
|
||
### Option 1: ANTs provide sorted array of records | ||
|
||
- `+` Sort order is controlled entirely by ANTs | ||
- `+` Minimal changes to gateways (use new API) | ||
- `-` Additional changes in ar-io-sdk to support non updated ANTs | ||
- `-` Managing priority order when updating records (handling collisions) | ||
|
||
### Option 2: ANTs provide priority data in records | ||
|
||
- `+` Extends existing ANT records object type | ||
- `+` Minimal changes to [ar-io-node] & [ar-io-sdk] (easy to make backwards compatible since object response type is unchanged) | ||
- `+` Supports implementing Option #1 at a later time, if desired | ||
- `-` Managing priority order when updating records (handling collisions) | ||
- `-` Gateways must fetch full ANT records and sort them by priority order | ||
- `-` Gateways must compute the sorted array of records | ||
|
||
### Option 3: ANT provides sort attributes via separate API | ||
|
||
- `+` Same pros as Option #2 | ||
- `-` Same cons as Option #2 | ||
- `-` Additional api calls to get sort attributes | ||
|
||
## Decision | ||
|
||
We will implement Option #2: **Additional priority attribute in ANT records** | ||
|
||
Ultimately, we could choose to implement both Option #1 and Option #2 as both require additional changes to the ANT contract, and the [ar-io-node]. Option #1 requires additional modifications to return the new object type. | ||
|
||
Option #2 requires the least amount of changes, while satisfying the requirements for enforcing undername limits. | ||
|
||
Example sorting applied to ANT records with priority data: | ||
|
||
```json | ||
{ | ||
"@": { | ||
"transactionId": "0x123", | ||
"ttlSeconds": 1000000, | ||
"priority": 0 | ||
}, | ||
"undername1": { | ||
"transactionId": "0x123", | ||
"ttlSeconds": 1000000, | ||
"priority": 1 | ||
}, | ||
"undername2": { | ||
"transactionId": "0x123", | ||
"ttlSeconds": 1000000, | ||
"priority": 2 | ||
} | ||
"undername3": { | ||
"transactionId": "0x123", | ||
"ttlSeconds": 1000000, | ||
"priority": undefined // records without a priority order fall to the bottom of the list | ||
} | ||
} | ||
``` | ||
dtfiedler marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
||
The [ar-io-node] will sort the records by priority, and fallback to lexicographical sorting when the priority attribute for any record in which the priority is not present. It will enforce the undername limit against the sorted records against the undername limit provided by the ARIO contract. If ANTs provide invalid priority order (conflicting records with same priority), **operators running [ar-io-node] can provide a sort order preference (alphabetical, reverse alphabetical, etc.), defaulting to lexicographical sorting.** | ||
|
||
Gateways will leverage existing caching logic defined in [./002-arns-cache-timing.md]. As such, the [ar-io-node] will cache the records (including priority order) for the provided TTLs. If the ANT contract is updated, the [ar-io-node] will fetch the updated records after the provided TTL and subsequently enforce any changes in priority order against the updated records. | ||
|
||
Next steps: | ||
|
||
- Update [ANT source code] to set priority order when updating/appending records | ||
dtfiedler marked this conversation as resolved.
Show resolved
Hide resolved
|
||
- `priority` is represented as a number, not a position - similar to priority order of ALB rules | ||
- Modify [ar-io-sdk] `getRecords()` API return type to return include `priority` as `number | undefined` | ||
- Update [ar-io-node] to use updated `getRecords()` API, applying sorting logic mentioned above and enforcing undername limits | ||
- Update arns.app to allow owners to upgrade ANTs to latest source code; add interface for managing priority order of ANTs | ||
dtfiedler marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
||
dtfiedler marked this conversation as resolved.
Show resolved
Hide resolved
|
||
## Links | ||
|
||
- [ANT Contract Documentation](https://ar.io/ant) | ||
- [ARIO Whitepaper](https://whitepaper.ar.io) | ||
- [ARIO SDK](https://github.com/ar-io/ar-io-sdk) | ||
- [ANT Source Code](https://github.com/ar-io/ar-io-ant-process) | ||
|
||
[Ariel]: https://github.com/arielmelendez | ||
[David]: https://github.com/djwhitt | ||
[Dylan]: https://github.com/dtfiedler | ||
[Atticus]: https://github.com/atticusofsparta | ||
[Phil]: https://github.com/vilenarios | ||
[Karl]: https://github.com/karlprieb | ||
[ar-io-sdk]: https://github.com/ar-io/ar-io-sdk?tab=readme-ov-file#getrecords | ||
[ar-io-node]: https://github.com/ar-io/ar-io-node/blob/fbbd4112d7024311f775969569ccfd9857bff9fe/src/resolution/composite-arns-resolver.ts#L127-L135 | ||
[./002-arns-cache-timing.md]: ./002-arns-cache-timing.md | ||
[ANT Source Code]: https://github.com/ar-io/ar-io-ant-process |
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is a wacky scenario, but what if an ANT doesn't consistently return its map and also doesn't provide prioritization ordering such that different gateways might end up with different valid undernames? Obviously this primarily hurts the users of the undernames, but I'm trying to decide whether this negatively impacts gateways in a way that hurts the protocol. Since the observers don't yet check undernames I think we'd be ok?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think that's something worth pointing out in this ADR: ANTs should be considered untrustworthy 🤔
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
good call out - will add
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
90be780