Skip to content

Commit

Permalink
refactor(cu): parse raw tags into object before passing to process
Browse files Browse the repository at this point in the history
I don't really like this, as this means that the CU has to
make decisions on how to parse the raw tags on the DataItem,
before evaluating the process.

I think it'd be better if the raw { name, value } tags were passed to WASM
then have utilities on the Lua side to extract the tag value based on tag name.
This would make it so the CU never parses tags, it simply extracts them,
then passes them into eval, leaving it up to the caller (Lua) to extract.

But the AOS process is expecting an object, which means the CU has to then
parse the tags into an object, so that's what i'm writing the CU to do.
  • Loading branch information
TillaTheHun0 committed Oct 24, 2023
1 parent d80d09e commit bc65079
Show file tree
Hide file tree
Showing 10 changed files with 68 additions and 88 deletions.
10 changes: 8 additions & 2 deletions servers/cu/src/domain/client/ao-su.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
/* eslint-disable camelcase */

import { fromPromise, of } from 'hyper-async'
import { always, applySpec, compose, evolve, filter, isNotNil, last, map, path, pipe, pluck, prop, transduce } from 'ramda'
import { always, applySpec, compose, evolve, filter, isNotNil, last, map, path, pathOr, pipe, pluck, prop, transduce } from 'ramda'

import { parseTags } from '../lib/utils.js'

export const loadMessagesWith = ({ fetch, SU_URL, logger: _logger, pageSize }) => {
const logger = _logger.child('ao-su:loadMessages')
Expand Down Expand Up @@ -152,7 +154,11 @@ export const loadMessagesWith = ({ fetch, SU_URL, logger: _logger, pageSize }) =
*/
from: mapFrom,
'Forwarded-By': mapForwardedBy,
tags: path(['message', 'tags'])
tags: pipe(
pathOr([], ['message', 'tags']),
// Parse into a key-value pair
parseTags
)
}),
/**
* We need the block metadata per message,
Expand Down
48 changes: 24 additions & 24 deletions servers/cu/src/domain/lib/evaluate.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -24,19 +24,19 @@ describe('evaluate', () => {
{
message: {
owner: 'owner-123',
tags: [
{ name: 'function', value: 'hello' }
]
tags: {
function: 'hello'
}
},
sortKey: 'a',
AoGlobal: {}
},
{
message: {
owner: 'owner-456',
tags: [
{ name: 'function', value: 'world' }
]
tags: {
function: 'world'
}
},
sortKey: 'b',
AoGlobal: {}
Expand All @@ -60,9 +60,9 @@ describe('evaluate', () => {
happy: true,
lastMessage: {
owner: 'owner-456',
tags: [
{ name: 'function', value: 'world' }
]
tags: {
function: 'world'
}
}
}
)
Expand Down Expand Up @@ -119,19 +119,19 @@ describe('evaluate', () => {
{
message: {
owner: 'owner-123',
tags: [
{ name: 'function', value: 'hello' }
]
tags: {
function: 'hello'
}
},
sortKey: 'a',
AoGlobal: {}
},
{
message: {
owner: 'owner-456',
tags: [
{ name: 'function', value: 'world' }
]
tags: {
function: 'world'
}
},
sortKey: 'b',
AoGlobal: {}
Expand Down Expand Up @@ -189,9 +189,9 @@ describe('evaluate', () => {
// Will include an error in result.error
message: {
owner: 'owner-456',
tags: [
{ name: 'function', value: 'errorResult' }
]
tags: {
function: 'errorResult'
}
},
sortKey: 'a',
AoGlobal: {}
Expand Down Expand Up @@ -230,9 +230,9 @@ describe('evaluate', () => {
// Will intentionally throw from the lua contract
message: {
owner: 'owner-456',
tags: [
{ name: 'function', value: 'errorThrow' }
]
tags: {
function: 'errorThrow'
}
},
sortKey: 'a',
AoGlobal: {}
Expand Down Expand Up @@ -271,9 +271,9 @@ describe('evaluate', () => {
// Will unintentionally throw from the lua contract
message: {
owner: 'owner-456',
tags: [
{ name: 'function', value: 'errorUnhandled' }
]
tags: {
function: 'errorUnhandled'
}
},
sortKey: 'a',
AoGlobal: {}
Expand Down
50 changes: 25 additions & 25 deletions servers/cu/src/domain/lib/loadMessages.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,20 +10,20 @@ import { SCHEDULED_INTERVAL, SCHEDULED_MESSAGE, isBlockOnSchedule, isTimestampOn
describe('loadMessages', () => {
describe('parseSchedules', () => {
const [action1, action2, action3] = [
JSON.stringify([
{ name: 'function', value: 'notify' },
{ name: 'notify-function', value: 'transfer' },
{ name: 'from', value: 'SIGNERS_WALLET_ADDRESS' },
{ name: 'qty', value: '1000' }
]),
JSON.stringify([
{ name: 'function', value: 'notify' },
{ name: 'notify-function', value: 'transfer' }
]),
JSON.stringify([
{ name: 'function', value: 'transfer' },
{ name: 'qty', value: '1000' }
])
JSON.stringify({
function: 'notify',
'notify-function': 'transfer',
from: 'SIGNERS_WALLET_ADDRESS',
qty: '1000'
}),
JSON.stringify({
function: 'notify',
'notify-function': 'transfer'
}),
JSON.stringify({
function: 'transfer',
qty: '1000'
})
]
test('parses the schedules from the tags', async () => {
/**
Expand Down Expand Up @@ -274,13 +274,13 @@ describe('loadMessages', () => {

const originHeight = 125000
const originTime = nowSecond - ms('30d')
const mockMessage = [
{ name: 'ao-type', value: 'message' },
{ name: 'function', value: 'notify' },
{ name: 'notify-function', value: 'transfer' },
{ name: 'from', value: 'SIGNERS_WALLET_ADDRESS' },
{ name: 'qty', value: '1000' }
]
const mockScheduledMessage = {
'ao-type': 'message',
function: 'notify',
'notify-function': 'transfer',
from: 'SIGNERS_WALLET_ADDRESS',
qty: '1000'
}

const processId = 'process-123'
const owner = 'owner-123'
Expand All @@ -294,25 +294,25 @@ describe('loadMessages', () => {
interval: '10-minutes',
unit: 'seconds',
value: ms('10m') / 1000,
message: mockMessage
message: mockScheduledMessage
},
{
interval: '2-blocks',
unit: 'blocks',
value: 2,
message: mockMessage
message: mockScheduledMessage
},
{
interval: '15-minutes',
unit: 'seconds',
value: ms('15m') / 1000,
message: mockMessage
message: mockScheduledMessage
},
{
interval: '2-blocks',
unit: 'blocks',
value: 2,
message: mockMessage
message: mockScheduledMessage
}
]

Expand Down
2 changes: 1 addition & 1 deletion servers/cu/src/domain/lib/loadProcess.js
Original file line number Diff line number Diff line change
Expand Up @@ -99,7 +99,7 @@ function loadLatestEvaluationWith ({ findLatestEvaluation, logger }) {
* Initial Process State
*/
() => Resolved({
state: { tags: ctx.tags || [] },
state: { tags: parseTags(ctx.tags || []) },
result: {
error: undefined,
messages: [],
Expand Down
6 changes: 3 additions & 3 deletions servers/cu/src/domain/lib/loadProcess.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import * as assert from 'node:assert'

import { createLogger } from '../logger.js'
import { loadProcessWith } from './loadProcess.js'
import { parseTags } from './utils.js'

const PROCESS = 'process-123-9HdeqeuYQOgMgWucro'
const logger = createLogger('ao-cu:readState')
Expand All @@ -14,8 +15,7 @@ describe('loadProcess', () => {
{ name: 'Contract-Src', value: 'foobar' },
{ name: 'Data-Protocol', value: 'ao' },
{ name: 'ao-type', value: 'process' },
{ name: 'Foo', value: 'Bar' },
{ name: 'Foo', value: 'Buzz' }
{ name: 'Foo', value: 'Bar' }
]
const loadProcess = loadProcessWith({
findProcess: async () => { throw { status: 404 } },
Expand All @@ -33,7 +33,7 @@ describe('loadProcess', () => {
assert.deepStrictEqual(res.tags, tags)
assert.deepStrictEqual(res.owner, 'woohoo')
assert.deepStrictEqual(res.block, { height: 123, timestamp: 1697574792 })
assert.deepStrictEqual(res.state, { tags })
assert.deepStrictEqual(res.state, { tags: parseTags(tags) })
assert.deepStrictEqual(res.result, {
messages: [],
output: [],
Expand Down
4 changes: 3 additions & 1 deletion servers/cu/src/domain/model.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@ export const rawTagSchema = z.object({
value: z.string()
})

export const parsedTagSchema = z.record(z.string())

export const rawBlockSchema = z.object({
height: z.number(),
timestamp: z.number()
Expand All @@ -35,7 +37,7 @@ export const messageSchema = z.object({
anchor: z.string().optional(),
from: z.string().optional(),
'Forwarded-By': z.string().optional(),
tags: z.array(rawTagSchema)
tags: z.array(parsedTagSchema)
}),
AoGlobal: z.object({
process: z.object({
Expand Down
18 changes: 2 additions & 16 deletions servers/cu/test/contracts/happy/contract.lua
Original file line number Diff line number Diff line change
Expand Up @@ -16,15 +16,6 @@ local function assoc(prop, val, obj)
return result
end

local function find(predicate, table) -- find element v of l satisfying f(v)
for _, v in ipairs(table) do
if predicate(v) then
return v
end
end
return nil
end

local function hello(state)
return assoc('heardHello', true, state)
end
Expand All @@ -38,15 +29,10 @@ actions['hello'] = hello
actions['world'] = world

function contract.handle(state, message, AoGlobal)
local func = find(
function (value)
return value.name == 'function'
end,
message.tags
)
local func = message.tags['function']
if func == nil then return error({ code = 500, message = 'no function tag in the message'}) end

local newState = actions[func.value](state, message, AoGlobal)
local newState = actions[func](state, message, AoGlobal)

newState = assoc('lastMessage', message, newState)

Expand Down
Binary file modified servers/cu/test/contracts/happy/contract.wasm
Binary file not shown.
18 changes: 2 additions & 16 deletions servers/cu/test/contracts/sad/contract.lua
Original file line number Diff line number Diff line change
Expand Up @@ -16,15 +16,6 @@ local function assoc(prop, val, obj)
return result
end

local function find(predicate, table) -- find element v of l satisfying f(v)
for _, v in ipairs(table) do
if predicate(v) then
return v
end
end
return nil
end

local function result(res)
return assoc('error', { code = 123, message = "a handled error within the contract" }, res)
end
Expand All @@ -43,16 +34,11 @@ actions['errorThrow'] = throw
actions['errorUnhandled'] = unhandled

function contract.handle(state, message, AoGlobal)
local func = find(
function (value)
return value.name == 'function'
end,
message.tags
)
local func = message.tags['function']

if func == nil then return error({ code = 500, message = 'no function tag in the message'}) end

return { result = actions[func.value](state, message, AoGlobal) }
return { result = actions[func](state, message, AoGlobal) }
end

return contract
Binary file modified servers/cu/test/contracts/sad/contract.wasm
Binary file not shown.

0 comments on commit bc65079

Please sign in to comment.