Skip to content

Commit

Permalink
Merge pull request #36 from ar-io/PE-6910-primary-names
Browse files Browse the repository at this point in the history
feat(PE-7174): support for primary name protocol with io process
  • Loading branch information
atticusofsparta authored Nov 22, 2024
2 parents d359e4a + 152e3e0 commit b2476f4
Show file tree
Hide file tree
Showing 6 changed files with 178 additions and 12 deletions.
6 changes: 5 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,12 +12,16 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
- Calls the IO Network Process to release the specified ArNS name that is registered to the ANT.
- Reassign-Name Handler
- Calls the IO Network Process to assign a new ANT Process to the respective name - must be a name registered the the ANT in question.
- Set-Decription Handler
- Set-Description Handler
- Allows for setting the description of the ANT
- Set-Keywords Handler
- Allows for setting keywords on the ANT
- Set-Logo Handler
- Allows for setting the logo of the ANT
- Add Approve-Primary-Name handler
- Approves a primary name request on the specified IO process ID
- Add Remove-Primary-Names handler
- Forwards a Remove-Primary-Names action to the specified IO process ID

### Changed

Expand Down
59 changes: 51 additions & 8 deletions src/common/main.lua
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,8 @@ function ant.init()
-- IO Network Contract Handlers
ReleaseName = "Release-Name",
ReassignName = "Reassign-Name",
ApproveName = "Approve-Primary-Name",
RemoveNames = "Remove-Primary-Names",
}

local TokenSpecActionMap = {
Expand All @@ -82,10 +84,6 @@ function ant.init()
Balance = "Balance",
Transfer = "Transfer",
TotalSupply = "Total-Supply",
-- not implemented
-- CreditNotice = "Credit-Notice",
-- Mint = "Mint",
-- Burn = "Burn",
}

createActionHandler(TokenSpecActionMap.Transfer, function(msg)
Expand Down Expand Up @@ -224,7 +222,11 @@ function ant.init()

createActionHandler(ActionMap.ReleaseName, function(msg)
utils.validateOwner(msg.From)
local name = string.lower(msg.Tags["Name"])
utils.validateArweaveId(msg.Tags["IO-Process-Id"])

assert(msg.Tags.Name, "Name is required")

local name = string.lower(msg.Tags.Name)
local ioProcess = msg.Tags["IO-Process-Id"]

-- send the release message to the provided IO Process Id
Expand All @@ -247,7 +249,9 @@ function ant.init()
utils.validateOwner(msg.From)
utils.validateArweaveId(msg.Tags["Process-Id"])

local name = string.lower(msg.Tags["Name"])
assert(msg.Tags.Name, "Name is required")

local name = string.lower(msg.Tags.Name)
local ioProcess = msg.Tags["IO-Process-Id"]
local antProcessIdToReassign = msg.Tags["Process-Id"]

Expand All @@ -262,13 +266,52 @@ function ant.init()

ao.send({
Target = msg.From,
-- This is out of our pattern, should be <action>-Notice
Action = "Reassign-Name-Submit-Notice",
Action = "Reassign-Name-Notice",
Initiator = msg.From,
Name = name,
["Process-Id"] = antProcessIdToReassign,
})
end)

createActionHandler(ActionMap.ApproveName, function(msg)
--- NOTE: this could be modified to allow specific users/controllers to create claims
utils.validateOwner(msg.From)
utils.validateArweaveId(msg.Tags["IO-Process-Id"])
utils.validateArweaveId(msg.Tags.Recipient)

assert(msg.Tags.Name, "Name is required")

local name = string.lower(msg.Tags.Name)
local recipient = msg.Tags.Recipient
local ioProcess = msg.Tags["IO-Process-Id"]

ao.send({
Target = ioProcess,
Action = "Approve-Primary-Name-Request",
Name = name,
Recipient = recipient,
})
end)

createActionHandler(ActionMap.RemoveNames, function(msg)
--- NOTE: this could be modified to allow specific users/controllers to remove primary names
utils.validateOwner(msg.From)
utils.validateArweaveId(msg.Tags["IO-Process-Id"])

assert(msg.Tags.Names, "Names are required")

local ioProcess = msg.Tags["IO-Process-Id"]
local names = utils.splitString(msg.Tags.Names)
for _, name in ipairs(names) do
utils.validateUndername(name)
end

ao.send({
Target = ioProcess,
Action = "Remove-Primary-Names",
Names = json.encode(names),
})
end)
end

return ant
12 changes: 12 additions & 0 deletions src/common/utils.lua
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,18 @@ function utils.deepCopy(original)
return copy
end

--- Splits a string by a delimiter
--- @param input string The string to split
--- @param delimiter string|nil The delimiter to split by, defaults to ","
--- @return table The split string
function utils.splitString(input, delimiter)
delimiter = delimiter or ","
local result = {}
for token in (input or ""):gmatch(string.format("([^%s]+)", delimiter)) do
table.insert(result, token)
end
return result
end
-- NOTE: lua 5.3 has limited regex support, particularly for lookaheads and negative lookaheads or use of {n}
---@param name string
---@description Asserts that the provided name is a valid undername
Expand Down
2 changes: 2 additions & 0 deletions test/info.test.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,8 @@ describe('aos Info', async () => {
'state',
'releaseName',
'reassignName',
'approvePrimaryName',
'removePrimaryNames',
]);
});

Expand Down
5 changes: 2 additions & 3 deletions test/io.test.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -115,14 +115,13 @@ describe('IO Network Updates', async () => {
);
assert(ioProcessMessage, 'Message to IO Process not found');

// Check if there is a message to the sender with Action 'Reassign-Name-Submit-Notice'
// Check if there is a message to the sender with Action 'Reassign-Name-Notice'
const senderMessage = result.Messages.find(
(msg) =>
msg.Target === STUB_ADDRESS &&
msg.Tags.some(
(tag) =>
tag.name === 'Action' &&
tag.value === 'Reassign-Name-Submit-Notice',
tag.name === 'Action' && tag.value === 'Reassign-Name-Notice',
) &&
msg.Tags.some(
(tag) => tag.name === 'Initiator' && tag.value === STUB_ADDRESS,
Expand Down
106 changes: 106 additions & 0 deletions test/primary_names.test.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
import { createAntAosLoader } from './utils.mjs';
import { describe, it } from 'node:test';
import assert from 'node:assert';
import {
AO_LOADER_HANDLER_ENV,
DEFAULT_HANDLE_OPTIONS,
STUB_ADDRESS,
} from '../tools/constants.mjs';

describe('Primary Names', async () => {
const { handle: originalHandle, memory: startMemory } =
await createAntAosLoader();

async function handle(options = {}, mem = startMemory) {
return originalHandle(
mem,
{
...DEFAULT_HANDLE_OPTIONS,
...options,
},
AO_LOADER_HANDLER_ENV,
);
}

it('should send approval for name request', async () => {
const res = await handle({
Tags: [
{
name: 'Action',
value: 'Approve-Primary-Name',
},
{ name: 'IO-Process-Id', value: ''.padEnd(43, '1') },
{ name: 'Recipient', value: ''.padEnd(43, '2') },
{ name: 'Name', value: ''.padEnd(43, '3') },
],
});

const approveNameRequest = res.Messages[0];
const actionTag = approveNameRequest.Tags.find((t) => t.name == 'Action');
const recipientTag = approveNameRequest.Tags.find(
(t) => t.name == 'Recipient',
);
const nameTag = approveNameRequest.Tags.find((t) => t.name == 'Name');

assert.strictEqual(approveNameRequest.Target, ''.padEnd(43, '1'));
assert.strictEqual(actionTag.value, 'Approve-Primary-Name-Request');
assert.strictEqual(recipientTag.value, ''.padEnd(43, '2'));
assert.strictEqual(nameTag.value, ''.padEnd(43, '3'));
});

it('should not approve request if caller not owner', async () => {
const res = await handle({
Owner: 'not-owner'.padEnd(43, '1'),
From: 'not-owner'.padEnd(43, '1'),
Tags: [
{
name: 'Action',
value: 'Approve-Primary-Name',
},
{ name: 'IO-Process-Id', value: ''.padEnd(43, '1') },
{ name: 'Recipient', value: ''.padEnd(43, '2') },
{ name: 'Name', value: ''.padEnd(43, '3') },
],
});
const invalidMessage = res.Messages[0];
const actionTag = invalidMessage.Tags.find((t) => t.name == 'Action');
assert.strictEqual(actionTag.value, 'Invalid-Approve-Primary-Name-Notice');
});

it('should send remove names request', async () => {
const names = ['foo', 'bar', 'baz'];
const res = await handle({
Tags: [
{ name: 'Action', value: 'Remove-Primary-Names' },
{ name: 'Names', value: names.join(',') },
{ name: 'IO-Process-Id', value: ''.padEnd(43, '2') },
],
});

const removePrimaryNamesMsg = res.Messages[0];
const actionTag = removePrimaryNamesMsg.Tags.find(
(t) => t.name == 'Action',
);
const namesTag = removePrimaryNamesMsg.Tags.find((t) => t.name == 'Names');

assert.strictEqual(removePrimaryNamesMsg.Target, ''.padEnd(43, '2'));
assert.strictEqual(actionTag.value, 'Remove-Primary-Names');
assert.strictEqual(namesTag.value, JSON.stringify(names));
});
it('should not send remove names request if caller not owner', async () => {
const names = ['foo', 'bar', 'baz'];
const res = await handle({
Owner: 'not-owner'.padEnd(43, '1'),
From: 'not-owner'.padEnd(43, '1'),
Tags: [
{ name: 'Action', value: 'Remove-Primary-Names' },
{ name: 'Names', value: names.join(',') },
{ name: 'IO-Process-Id', value: ''.padEnd(43, '2') },
],
});

const invalidMessage = res.Messages[0];
const actionTag = invalidMessage.Tags.find((t) => t.name == 'Action');
assert.strictEqual(actionTag.value, 'Invalid-Remove-Primary-Names-Notice');
});
});

0 comments on commit b2476f4

Please sign in to comment.