diff --git a/src/common/main.lua b/src/common/main.lua index e3c1812..a561e20 100644 --- a/src/common/main.lua +++ b/src/common/main.lua @@ -1,4 +1,4 @@ -local ant = {} +local ant = { _version = "0.0.1" } function ant.init() -- main.lua @@ -13,6 +13,7 @@ function ant.init() local initialize = require(".common.initialize") local records = require(".common.records") local controllers = require(".common.controllers") + local validate = require(".common.validate") ---@alias Owner string ---@description The owner of the ANT @@ -70,7 +71,7 @@ function ant.init() Record = "Record", Records = "Records", State = "State", - Evolve = "Evolve", + ValidateHandlers = "Validate-Handlers", -- IO Network Contract Handlers ReleaseName = "Release-Name", ReassignName = "Reassign-Name", @@ -313,6 +314,10 @@ function ant.init() Names = msg.Tags.Names, }) end) + + createActionHandler(ActionMap.ValidateHandlers, function(msg) + return validate.validateHandlers(msg, ao) + end) end return ant diff --git a/src/common/utils.lua b/src/common/utils.lua index a619844..8e464b4 100644 --- a/src/common/utils.lua +++ b/src/common/utils.lua @@ -19,10 +19,11 @@ function utils.hasMatchingTag(tag, value) return Handlers.utils.hasMatchingTag(tag, value) end ---- Deep copies a table +--- Deep copies a table, handling circular references --- @param original table The table to copy +--- @param seen table|nil Internal table to track already copied references --- @return table|nil The deep copy of the table or nil if the original is nil -function utils.deepCopy(original) +function utils.deepCopy(original, seen) if not original then return nil end @@ -31,14 +32,23 @@ function utils.deepCopy(original) return original end + -- Use the `seen` table to track circular references + seen = seen or {} + if seen[original] then + return seen[original] -- Return the already copied table for circular references + end + local copy = {} + seen[original] = copy -- Track the copy of this table + for key, value in pairs(original) do if type(value) == "table" then - copy[key] = utils.deepCopy(value) -- Recursively copy the nested table + copy[key] = utils.deepCopy(value, seen) -- Recursively copy the nested table else copy[key] = value end end + return copy end diff --git a/src/common/validate.lua b/src/common/validate.lua new file mode 100644 index 0000000..37b07dd --- /dev/null +++ b/src/common/validate.lua @@ -0,0 +1,29 @@ +local utils = require(".common.utils") +local json = require(".common.json") +local validate = {} + +---@param msg AoMessage AO messages to evaluate +---@param env table the AO env object +function validate.validateHandlers(msg, env) + local messages = json.decode(msg.Data) + + local results = {} + + local stateBackup = json.encode(utils.getState()) + + for name, message in pairs(messages) do + local process = package.loaded[".process"] + local _, res = pcall(process.handle, message, env) + ao.clearOutbox() + results[name] = { message = message, result = res } + + --- reset state + for k, v in pairs(json.decode(stateBackup)) do + _G[k] = v + end + end + + return results +end + +return validate diff --git a/test/info.test.mjs b/test/info.test.mjs index c01d6fe..cbed540 100644 --- a/test/info.test.mjs +++ b/test/info.test.mjs @@ -38,7 +38,7 @@ describe('aos Info', async () => { assert(processInfo.Handlers); assert.deepStrictEqual(processInfo.Handlers, [ '_eval', - '_default', + '_default', 'transfer', 'balance', 'balances', diff --git a/test/validation.test.mjs b/test/validation.test.mjs new file mode 100644 index 0000000..0536eee --- /dev/null +++ b/test/validation.test.mjs @@ -0,0 +1,50 @@ +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('aos Validate', 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 validate Transfer', async () => { + const messages = { + Transfer: { + ...DEFAULT_HANDLE_OPTIONS, + Tags: [ + { name: 'Action', value: 'Transfer' }, + { name: 'Recipient', value: 'STUB_ADDRESS'.padEnd(43, '1') }, + ], + }, + Transfer2: { + ...DEFAULT_HANDLE_OPTIONS, + Tags: [ + { name: 'Action', value: 'Transfer' }, + { name: 'Recipient', value: 'STUB_ADDRESS'.padEnd(43, '1') }, + ], + }, + }; + + const res = await handle({ + Tags: [{ name: 'Action', value: 'Validate-Handlers' }], + Data: JSON.stringify(messages), + }); + + console.dir(JSON.parse(res.Messages[0].Data), { depth: null }); + }); +});