実用的なコード例
この包括的なガイドでは、AO開発の一般的なパターンに対する完全で本番環境対応のコード例を提供します。各例は、実際のプロジェクトで直接適用できる実世界の実装技術、セキュリティ考慮事項、ベストプラクティスを示しています。
基本的なスマートコントラクト - カウンター例
実装概要
カウンターコントラクトは、ステート初期化、Message処理、クエリ操作を含むAOの基本的な概念を示しています。この例は、AOのProcess指向アーキテクチャを理解するための基盤として機能します。
完全なカウンターコントラクト# 実用的なコード例
この包括的なガイドでは、AO開発の一般的なパターンに対する完全で本番環境対応のコード例を提供します。各例は、実際のプロジェクトで直接適用できる実世界の実装技術、セキュリティ考慮事項、ベストプラクティスを示しています。
基本的なスマートコントラクト - カウンター例
実装概要
カウンターコントラクトは、ステート初期化、Message処理、クエリ操作を含むAOの基本的な概念を示しています。この例は、AOのProcess指向アーキテクチャを理解するための基盤として機能します。
完全なカウンターコントラクト
-- Process Name and Version
Name = "Counter"
Version = "1.0.0"
-- State Initialization
State = State or {
counter = 0,
owner = nil,
createdAt = nil,
totalIncrements = 0,
totalDecrements = 0,
history = {}
}
-- Initialize state on first run
if not State.createdAt then
State.createdAt = os.time()
State.owner = ao.id
print("Counter initialized at " .. State.createdAt)
end
-- Increment Handler
Handlers.add(
"increment",
Handlers.utils.hasMatchingTag("Action", "Increment"),
function(msg)
-- Validate input
local amount = tonumber(msg.Data) or 1
if amount <= 0 then
Send({
Target = msg.From,
Action = "Error",
Data = "Invalid increment amount",
Tags = {
Error = "Amount must be positive",
Received = msg.Data or "nil"
}
})
return
end
-- Update state
State.counter = State.counter + amount
State.totalIncrements = State.totalIncrements + amount
-- Add to history
table.insert(State.history, {
action = "increment",
amount = amount,
timestamp = os.time(),
from = msg.From,
newValue = State.counter
})
-- Limit history size
if #State.history > 100 then
table.remove(State.history, 1)
end
-- Send success response
Send({
Target = msg.From,
Action = "IncrementSuccess",
Data = tostring(State.counter),
Tags = {
PreviousValue = tostring(State.counter - amount),
NewValue = tostring(State.counter),
IncrementAmount = tostring(amount)
}
})
print("Counter incremented by " .. amount .. " to " .. State.counter)
end
)
-- Decrement Handler
Handlers.add(
"decrement",
Handlers.utils.hasMatchingTag("Action", "Decrement"),
function(msg)
-- Validate input
local amount = tonumber(msg.Data) or 1
if amount <= 0 then
Send({
Target = msg.From,
Action = "Error",
Data = "Invalid decrement amount",
Tags = {
Error = "Amount must be positive",
Received = msg.Data or "nil"
}
})
return
end
-- Check if decrement would make counter negative
if State.counter - amount < 0 then
Send({
Target = msg.From,
Action = "Error",
Data = "Cannot decrement below zero",
Tags = {
Error = "Underflow protection",
CurrentValue = tostring(State.counter),
RequestedDecrement = tostring(amount)
}
})
return
end
-- Update state
State.counter = State.counter - amount
State.totalDecrements = State.totalDecrements + amount
-- Add to history
table.insert(State.history, {
action = "decrement",
amount = amount,
timestamp = os.time(),
from = msg.From,
newValue = State.counter
})
-- Send success response
Send({
Target = msg.From,
Action = "DecrementSuccess",
Data = tostring(State.counter),
Tags = {
PreviousValue = tostring(State.counter + amount),
NewValue = tostring(State.counter),
DecrementAmount = tostring(amount)
}
})
print("Counter decremented by " .. amount .. " to " .. State.counter)
end
)
-- Query Handler
Handlers.add(
"getValue",
Handlers.utils.hasMatchingTag("Action", "GetValue"),
function(msg)
Send({
Target = msg.From,
Action = "ValueResponse",
Data = tostring(State.counter),
Tags = {
Counter = tostring(State.counter),
TotalIncrements = tostring(State.totalIncrements),
TotalDecrements = tostring(State.totalDecrements),
Owner = State.owner,
CreatedAt = tostring(State.createdAt)
}
})
end
)
-- History Query Handler
Handlers.add(
"getHistory",
Handlers.utils.hasMatchingTag("Action", "GetHistory"),
function(msg)
local count = tonumber(msg.Tags.Count) or 10
local history = {}
-- Get last N entries
local start = math.max(1, #State.history - count + 1)
for i = start, #State.history do
table.insert(history, State.history[i])
end
Send({
Target = msg.From,
Action = "HistoryResponse",
Data = json.encode(history),
Tags = {
Count = tostring(#history),
TotalHistory = tostring(#State.history)
}
})
end
)
-- Reset Handler (Owner only)
Handlers.add(
"reset",
Handlers.utils.hasMatchingTag("Action", "Reset"),
function(msg)
if msg.From ~= State.owner then
Send({
Target = msg.From,
Action = "Error",
Data = "Access denied",
Tags = {
Error = "Only owner can reset counter"
}
})
return
end
-- Reset counter but preserve history
local oldValue = State.counter
State.counter = 0
-- Add reset to history
table.insert(State.history, {
action = "reset",
amount = oldValue,
timestamp = os.time(),
from = msg.From,
newValue = 0
})
Send({
Target = msg.From,
Action = "ResetSuccess",
Data = "Counter reset to 0",
Tags = {
PreviousValue = tostring(oldValue),
NewValue = "0"
}
})
print("Counter reset from " .. oldValue .. " to 0")
end
)
カウンターコントラクトのテスト
-- Test messages to send to the counter process
-- 1. Basic increment
Send({
Target = "counter_process_id",
Action = "Increment",
Data = "5"
})
-- Expected: IncrementSuccess with Data = "5"
-- 2. Query current value
Send({
Target = "counter_process_id",
Action = "GetValue"
})
-- Expected: ValueResponse with counter value
-- 3. Decrement
Send({
Target = "counter_process_id",
Action = "Decrement",
Data = "3"
})
-- Expected: DecrementSuccess with Data = "2"
-- 4. Invalid operation (negative increment)
Send({
Target = "counter_process_id",
Action = "Increment",
Data = "-5"
})
-- Expected: Error response
-- 5. Get history
Send({
Target = "counter_process_id",
Action = "GetHistory",
Tags = {Count = "5"}
})
-- Expected: HistoryResponse with JSON array
トークン実装例
ERC20スタイルトークンコントラクト
この包括的なトークン実装は、高度なステート管理、アクセス制御、プロセス間通信パターンを示しています。
-- Token Contract Implementation
Name = "MyToken"
Symbol = "MTK"
Decimals = 18
Version = "1.0.0"
-- State Initialization
State = State or {
totalSupply = 0,
balances = {},
allowances = {},
owner = nil,
minters = {},
frozen = false,
metadata = {
name = "MyToken",
symbol = "MTK",
decimals = 18,
description = "A sample token implementation for AO"
}
}
-- Initialize on first run
if not State.owner then
State.owner = ao.id
State.minters[ao.id] = true
print("Token initialized with owner: " .. State.owner)
end
-- Utility functions
local function isValidAddress(address)
return address and type(address) == "string" and #address > 0
end
local function hasPermission(address, permission)
if permission == "owner" then
return address == State.owner
elseif permission == "minter" then
return State.minters[address] == true
end
return false
end
local function formatBalance(amount)
return tostring(amount or 0)
end
-- Balance Query Handler
Handlers.add(
"balanceOf",
Handlers.utils.hasMatchingTag("Action", "BalanceOf"),
function(msg)
local target = msg.Tags.Target or msg.From
if not isValidAddress(target) then
Send({
Target = msg.From,
Action = "Error",
Data = "Invalid address",
Tags = {Error = "InvalidAddress"}
})
return
end
local balance = State.balances[target] or 0
Send({
Target = msg.From,
Action = "BalanceResponse",
Data = formatBalance(balance),
Tags = {
Account = target,
Balance = formatBalance(balance),
Symbol = Symbol
}
})
end
)
-- Transfer Handler
Handlers.add(
"transfer",
Handlers.utils.hasMatchingTag("Action", "Transfer"),
function(msg)
-- Check if contract is frozen
if State.frozen then
Send({
Target = msg.From,
Action = "Error",
Data = "Contract is frozen",
Tags = {Error = "ContractFrozen"}
})
return
end
-- Validate recipient
local recipient = msg.Tags.Recipient
if not isValidAddress(recipient) then
Send({
Target = msg.From,
Action = "Error",
Data = "Invalid recipient address",
Tags = {Error = "InvalidRecipient"}
})
return
end
-- Validate amount
local amount = tonumber(msg.Data)
if not amount or amount <= 0 then
Send({
Target = msg.From,
Action = "Error",
Data = "Invalid transfer amount",
Tags = {
Error = "InvalidAmount",
Received = msg.Data or "nil"
}
})
return
end
-- Check sender balance
local senderBalance = State.balances[msg.From] or 0
if senderBalance < amount then
Send({
Target = msg.From,
Action = "Error",
Data = "Insufficient balance",
Tags = {
Error = "InsufficientBalance",
Balance = formatBalance(senderBalance),
Requested = formatBalance(amount)
}
})
return
end
-- Prevent self-transfer
if msg.From == recipient then
Send({
Target = msg.From,
Action = "Error",
Data = "Cannot transfer to self",
Tags = {Error = "SelfTransfer"}
})
return
end
-- Execute transfer
State.balances[msg.From] = senderBalance - amount
State.balances[recipient] = (State.balances[recipient] or 0) + amount
-- Send success response to sender
Send({
Target = msg.From,
Action = "TransferSuccess",
Data = formatBalance(amount),
Tags = {
From = msg.From,
To = recipient,
Amount = formatBalance(amount),
NewBalance = formatBalance(State.balances[msg.From])
}
})
-- Notify recipient
Send({
Target = recipient,
Action = "TransferNotification",
Data = formatBalance(amount),
Tags = {
From = msg.From,
To = recipient,
Amount = formatBalance(amount),
NewBalance = formatBalance(State.balances[recipient])
}
})
print("Transfer: " .. formatBalance(amount) .. " from " .. msg.From .. " to " .. recipient)
end
)
-- Mint Handler
Handlers.add(
"mint",
Handlers.utils.hasMatchingTag("Action", "Mint"),
function(msg)
-- Check minter permission
if not hasPermission(msg.From, "minter") then
Send({
Target = msg.From,
Action = "Error",
Data = "Access denied",
Tags = {Error = "NotMinter"}
})
return
end
-- Validate recipient
local recipient = msg.Tags.Recipient or msg.From
if not isValidAddress(recipient) then
Send({
Target = msg.From,
Action = "Error",
Data = "Invalid recipient address",
Tags = {Error = "InvalidRecipient"}
})
return
end
-- Validate amount
local amount = tonumber(msg.Data)
if not amount or amount <= 0 then
Send({
Target = msg.From,
Action = "Error",
Data = "Invalid mint amount",
Tags = {
Error = "InvalidAmount",
Received = msg.Data or "nil"
}
})
return
end
-- Execute mint
State.balances[recipient] = (State.balances[recipient] or 0) + amount
State.totalSupply = State.totalSupply + amount
-- Send success response
Send({
Target = msg.From,
Action = "MintSuccess",
Data = formatBalance(amount),
Tags = {
Recipient = recipient,
Amount = formatBalance(amount),
NewBalance = formatBalance(State.balances[recipient]),
TotalSupply = formatBalance(State.totalSupply)
}
})
-- Notify recipient if different from minter
if recipient ~= msg.From then
Send({
Target = recipient,
Action = "MintNotification",
Data = formatBalance(amount),
Tags = {
Minter = msg.From,
Amount = formatBalance(amount),
NewBalance = formatBalance(State.balances[recipient])
}
})
end
print("Minted: " .. formatBalance(amount) .. " to " .. recipient)
end
)
-- Approval Handler
Handlers.add(
"approve",
Handlers.utils.hasMatchingTag("Action", "Approve"),
function(msg)
-- Validate spender
local spender = msg.Tags.Spender
if not isValidAddress(spender) then
Send({
Target = msg.From,
Action = "Error",
Data = "Invalid spender address",
Tags = {Error = "InvalidSpender"}
})
return
end
-- Validate amount
local amount = tonumber(msg.Data)
if not amount or amount < 0 then
Send({
Target = msg.From,
Action = "Error",
Data = "Invalid approval amount",
Tags = {
Error = "InvalidAmount",
Received = msg.Data or "nil"
}
})
return
end
-- Set approval
State.allowances[msg.From] = State.allowances[msg.From] or {}
State.allowances[msg.From][spender] = amount
-- Send success response
Send({
Target = msg.From,
Action = "ApprovalSuccess",
Data = formatBalance(amount),
Tags = {
Owner = msg.From,
Spender = spender,
Amount = formatBalance(amount)
}
})
-- Notify spender
Send({
Target = spender,
Action = "ApprovalNotification",
Data = formatBalance(amount),
Tags = {
Owner = msg.From,
Spender = spender,
Amount = formatBalance(amount)
}
})
print("Approval: " .. msg.From .. " approved " .. spender .. " for " .. formatBalance(amount))
end
)
-- TransferFrom Handler
Handlers.add(
"transferFrom",
Handlers.utils.hasMatchingTag("Action", "TransferFrom"),
function(msg)
-- Check if contract is frozen
if State.frozen then
Send({
Target = msg.From,
Action = "Error",
Data = "Contract is frozen",
Tags = {Error = "ContractFrozen"}
})
return
end
-- Validate parameters
local owner = msg.Tags.Owner
local recipient = msg.Tags.Recipient
local amount = tonumber(msg.Data)
if not isValidAddress(owner) or not isValidAddress(recipient) then
Send({
Target = msg.From,
Action = "Error",
Data = "Invalid addresses",
Tags = {Error = "InvalidAddresses"}
})
return
end
if not amount or amount <= 0 then
Send({
Target = msg.From,
Action = "Error",
Data = "Invalid transfer amount",
Tags = {
Error = "InvalidAmount",
Received = msg.Data or "nil"
}
})
return
end
-- Check allowance
local allowance = 0
if State.allowances[owner] and State.allowances[owner][msg.From] then
allowance = State.allowances[owner][msg.From]
end
if allowance < amount then
Send({
Target = msg.From,
Action = "Error",
Data = "Insufficient allowance",
Tags = {
Error = "InsufficientAllowance",
Allowance = formatBalance(allowance),
Requested = formatBalance(amount)
}
})
return
end
-- Check owner balance
local ownerBalance = State.balances[owner] or 0
if ownerBalance < amount then
Send({
Target = msg.From,
Action = "Error",
Data = "Insufficient balance",
Tags = {
Error = "InsufficientBalance",
Balance = formatBalance(ownerBalance),
Requested = formatBalance(amount)
}
})
return
end
-- Execute transfer
State.balances[owner] = ownerBalance - amount
State.balances[recipient] = (State.balances[recipient] or 0) + amount
-- Update allowance
State.allowances[owner][msg.From] = allowance - amount
-- Send success response
Send({
Target = msg.From,
Action = "TransferFromSuccess",
Data = formatBalance(amount),
Tags = {
Owner = owner,
Spender = msg.From,
Recipient = recipient,
Amount = formatBalance(amount),
RemainingAllowance = formatBalance(State.allowances[owner][msg.From])
}
})
-- Notify owner and recipient
Send({
Target = owner,
Action = "TransferFromNotification",
Data = formatBalance(amount),
Tags = {
Spender = msg.From,
Recipient = recipient,
Amount = formatBalance(amount),
NewBalance = formatBalance(State.balances[owner])
}
})
Send({
Target = recipient,
Action = "TransferNotification",
Data = formatBalance(amount),
Tags = {
From = owner,
To = recipient,
Amount = formatBalance(amount),
NewBalance = formatBalance(State.balances[recipient])
}
})
print("TransferFrom: " .. formatBalance(amount) .. " from " .. owner .. " to " .. recipient .. " by " .. msg.From)
end
)
-- Total Supply Query
Handlers.add(
"totalSupply",
Handlers.utils.hasMatchingTag("Action", "TotalSupply"),
function(msg)
Send({
Target = msg.From,
Action = "TotalSupplyResponse",
Data = formatBalance(State.totalSupply),
Tags = {
TotalSupply = formatBalance(State.totalSupply),
Symbol = Symbol
}
})
end
)
-- Allowance Query
Handlers.add(
"allowance",
Handlers.utils.hasMatchingTag("Action", "Allowance"),
function(msg)
local owner = msg.Tags.Owner
local spender = msg.Tags.Spender
if not isValidAddress(owner) or not isValidAddress(spender) then
Send({
Target = msg.From,
Action = "Error",
Data = "Invalid addresses",
Tags = {Error = "InvalidAddresses"}
})
return
end
local allowance = 0
if State.allowances[owner] and State.allowances[owner][spender] then
allowance = State.allowances[owner][spender]
end
Send({
Target = msg.From,
Action = "AllowanceResponse",
Data = formatBalance(allowance),
Tags = {
Owner = owner,
Spender = spender,
Allowance = formatBalance(allowance)
}
})
end
)
-- Add Minter Handler (Owner only)
Handlers.add(
"addMinter",
Handlers.utils.hasMatchingTag("Action", "AddMinter"),
function(msg)
if not hasPermission(msg.From, "owner") then
Send({
Target = msg.From,
Action = "Error",
Data = "Access denied",
Tags = {Error = "NotOwner"}
})
return
end
local newMinter = msg.Tags.Minter
if not isValidAddress(newMinter) then
Send({
Target = msg.From,
Action = "Error",
Data = "Invalid minter address",
Tags = {Error = "InvalidMinter"}
})
return
end
State.minters[newMinter] = true
Send({
Target = msg.From,
Action = "MinterAdded",
Data = "Minter added successfully",
Tags = {
Minter = newMinter
}
})
print("Added minter: " .. newMinter)
end
)
トークンコントラクトのテスト
-- Token testing examples
-- 1. Check initial balance
Send({
Target = "token_process_id",
Action = "BalanceOf",
Tags = {Target = "user_address"}
})
-- 2. Mint tokens
Send({
Target = "token_process_id",
Action = "Mint",
Data = "1000000000000000000000", -- 1000 tokens (18 decimals)
Tags = {Recipient = "user_address"}
})
-- 3. Transfer tokens
Send({
Target = "token_process_id",
Action = "Transfer",
Data = "500000000000000000000", -- 500 tokens
Tags = {Recipient = "recipient_address"}
})
-- 4. Approve spending
Send({
Target = "token_process_id",
Action = "Approve",
Data = "1000000000000000000000", -- 1000 tokens
Tags = {Spender = "spender_address"}
})
-- 5. Transfer from approved amount
Send({
Target = "token_process_id",
Action = "TransferFrom",
Data = "250000000000000000000", -- 250 tokens
Tags = {
Owner = "owner_address",
Recipient = "recipient_address"
}
})
-- 6. Query total supply
Send({
Target = "token_process_id",
Action = "TotalSupply"
})
Message処理パターン
複雑なワークフローを持つマルチアクションProcess
この例は、検証、条件分岐、プロセス間協調を含む洗練されたMessage処理パターンを示しています。
-- Advanced Message Processing Contract
Name = "WorkflowProcessor"
Version = "1.0.0"
-- State initialization
State = State or {
workflows = {},
processors = {},
results = {},
config = {
maxWorkflowSteps = 10,
timeoutSeconds = 300,
maxConcurrentWorkflows = 100
}
}
-- Utility functions
local function generateId()
return "wf_" .. os.time() .. "_" .. math.random(10000, 99999)
end
local function validateWorkflowData(data)
if not data or type(data) ~= "table" then
return false, "Invalid workflow data format"
end
if not data.steps or type(data.steps) ~= "table" or #data.steps == 0 then
return false, "Workflow must have at least one step"
end
if #data.steps > State.config.maxWorkflowSteps then
return false, "Too many workflow steps"
end
for i, step in ipairs(data.steps) do
if not step.action or not step.processor then
return false, "Step " .. i .. " missing required fields"
end
end
return true, "Valid"
end
local function isWorkflowComplete(workflowId)
local workflow = State.workflows[workflowId]
if not workflow then return false end
for _, step in ipairs(workflow.steps) do
if step.status ~= "completed" and step.status ~= "skipped" then
return false
end
end
return true
end
local function getNextStep(workflowId)
local workflow = State.workflows[workflowId]
if not workflow then return nil end
for i, step in ipairs(workflow.steps) do
if step.status == "pending" then
return i, step
end
end
return nil
end
-- Start Workflow Handler
Handlers.add(
"startWorkflow",
Handlers.utils.hasMatchingTag("Action", "StartWorkflow"),
function(msg)
-- Check workflow limit
local activeCount = 0
for _, workflow in pairs(State.workflows) do
if workflow.status == "running" then
activeCount = activeCount + 1
end
end
if activeCount >= State.config.maxConcurrentWorkflows then
Send({
Target = msg.From,
Action = "Error",
Data = "Maximum concurrent workflows reached",
Tags = {
Error = "WorkflowLimitExceeded",
MaxConcurrent = tostring(State.config.maxConcurrentWorkflows)
}
})
return
end
-- Parse workflow data
local success, workflowData = pcall(json.decode, msg.Data)
if not success then
Send({
Target = msg.From,
Action = "Error",
Data = "Invalid JSON format",
Tags = {Error = "InvalidJSON"}
})
return
end
-- Validate workflow
local isValid, errorMsg = validateWorkflowData(workflowData)
if not isValid then
Send({
Target = msg.From,
Action = "Error",
Data = errorMsg,
Tags = {Error = "ValidationFailed"}
})
return
end
-- Create workflow
local workflowId = generateId()
local workflow = {
id = workflowId,
initiator = msg.From,
status = "running",
createdAt = os.time(),
steps = {},
currentStep = 1,
data = workflowData.data or {}
}
-- Initialize steps
for i, stepData in ipairs(workflowData.steps) do
workflow.steps[i] = {
id = i,
action = stepData.action,
processor = stepData.processor,
status = "pending",
data = stepData.data or {},
retries = 0,
maxRetries = stepData.maxRetries or 3
}
end
State.workflows[workflowId] = workflow
-- Send success response
Send({
Target = msg.From,
Action = "WorkflowStarted",
Data = workflowId,
Tags = {
WorkflowId = workflowId,
StepCount = tostring(#workflow.steps)
}
})
-- Start first step
processNextStep(workflowId)
print("Started workflow: " .. workflowId)
end
)
-- Process next step function
function processNextStep(workflowId)
local stepIndex, step = getNextStep(workflowId)
if not stepIndex then
-- Workflow complete
completeWorkflow(workflowId)
return
end
local workflow = State.workflows[workflowId]
step.status = "processing"
step.startTime = os.time()
-- Send step to processor
Send({
Target = step.processor,
Action = "ProcessStep",
Data = json.encode({
workflowId = workflowId,
stepId = stepIndex,
stepData = step.data,
workflowData = workflow.data
}),
Tags = {
WorkflowId = workflowId,
StepId = tostring(stepIndex),
StepAction = step.action,
ResponseTarget = ao.id
}
})
print("Processing step " .. stepIndex .. " of workflow " .. workflowId)
end
-- Step completion handler
Handlers.add(
"stepComplete",
Handlers.utils.hasMatchingTag("Action", "StepComplete"),
function(msg)
local workflowId = msg.Tags.WorkflowId
local stepId = tonumber(msg.Tags.StepId)
local workflow = State.workflows[workflowId]
if not workflow then
Send({
Target = msg.From,
Action = "Error",
Data = "Workflow not found",
Tags = {Error = "WorkflowNotFound"}
})
return
end
local step = workflow.steps[stepId]
if not step then
Send({
Target = msg.From,
Action = "Error",
Data = "Step not found",
Tags = {Error = "StepNotFound"}
})
return
end
-- Update step status
step.status = "completed"
step.endTime = os.time()
step.result = msg.Data
-- Update workflow data if provided
if msg.Tags.UpdateData then
local success, updateData = pcall(json.decode, msg.Tags.UpdateData)
if success then
for key, value in pairs(updateData) do
workflow.data[key] = value
end
end
end
-- Process next step
processNextStep(workflowId)
end
)
-- Step failure handler
Handlers.add(
"stepFailed",
Handlers.utils.hasMatchingTag("Action", "StepFailed"),
function(msg)
local workflowId = msg.Tags.WorkflowId
local stepId = tonumber(msg.Tags.StepId)
local workflow = State.workflows[workflowId]
if not workflow then return end
local step = workflow.steps[stepId]
if not step then return end
step.retries = step.retries + 1
step.lastError = msg.Data
if step.retries < step.maxRetries then
-- Retry step
step.status = "pending"
print("Retrying step " .. stepId .. " of workflow " .. workflowId .. " (attempt " .. (step.retries + 1) .. ")")
-- Add delay before retry
processNextStep(workflowId)
else
-- Max retries reached
step.status = "failed"
step.endTime = os.time()
-- Fail entire workflow
workflow.status = "failed"
workflow.endTime = os.time()
workflow.error = "Step " .. stepId .. " failed after " .. step.maxRetries .. " attempts"
-- Notify initiator
Send({
Target = workflow.initiator,
Action = "WorkflowFailed",
Data = workflow.error,
Tags = {
WorkflowId = workflowId,
FailedStep = tostring(stepId),
Error = step.lastError
}
})
print("Workflow " .. workflowId .. " failed at step " .. stepId)
end
end
)
-- Complete workflow function
function completeWorkflow(workflowId)
local workflow = State.workflows[workflowId]
if not workflow then return end
workflow.status = "completed"
workflow.endTime = os.time()
-- Prepare result
local result = {
workflowId = workflowId,
status = "completed",
duration = workflow.endTime - workflow.createdAt,
stepCount = #workflow.steps,
finalData = workflow.data
}
-- Store result
State.results[workflowId] = result
-- Notify initiator
Send({
Target = workflow.initiator,
Action = "WorkflowComplete",
Data = json.encode(result),
Tags = {
WorkflowId = workflowId,
Duration = tostring(result.duration),
StepCount = tostring(result.stepCount)
}
})
print("Workflow " .. workflowId .. " completed successfully")
end
-- Query workflow status
Handlers.add(
"getWorkflowStatus",
Handlers.utils.hasMatchingTag("Action", "GetWorkflowStatus"),
function(msg)
local workflowId = msg.Tags.WorkflowId
local workflow = State.workflows[workflowId]
if not workflow then
Send({
Target = msg.From,
Action = "Error",
Data = "Workflow not found",
Tags = {Error = "WorkflowNotFound"}
})
return
end
-- Prepare status response
local status = {
id = workflow.id,
status = workflow.status,
createdAt = workflow.createdAt,
currentStep = workflow.currentStep,
totalSteps = #workflow.steps,
steps = {}
}
-- Add step details
for i, step in ipairs(workflow.steps) do
status.steps[i] = {
id = step.id,
action = step.action,
status = step.status,
retries = step.retries
}
end
Send({
Target = msg.From,
Action = "WorkflowStatusResponse",
Data = json.encode(status),
Tags = {
WorkflowId = workflowId,
Status = workflow.status
}
})
end
)
-- Cancel workflow
Handlers.add(
"cancelWorkflow",
Handlers.utils.hasMatchingTag("Action", "CancelWorkflow"),
function(msg)
local workflowId = msg.Tags.WorkflowId
local workflow = State.workflows[workflowId]
if not workflow then
Send({
Target = msg.From,
Action = "Error",
Data = "Workflow not found",
Tags = {Error = "WorkflowNotFound"}
})
return
end
-- Check authorization
if msg.From ~= workflow.initiator then
Send({
Target = msg.From,
Action = "Error",
Data = "Only workflow initiator can cancel",
Tags = {Error = "NotAuthorized"}
})
return
end
-- Cancel workflow
workflow.status = "cancelled"
workflow.endTime = os.time()
Send({
Target = msg.From,
Action = "WorkflowCancelled",
Data = "Workflow cancelled successfully",
Tags = {
WorkflowId = workflowId
}
})
print("Workflow " .. workflowId .. " cancelled")
end
)
ワークフロープロセッサーのテスト
-- Workflow processor testing examples
-- 1. Start a simple workflow
Send({
Target = "workflow_processor_id",
Action = "StartWorkflow",
Data = json.encode({
steps = {
{
action = "ValidateData",
processor = "validator_process_id",
data = {input = "test_data"}
},
{
action = "ProcessData",
processor = "data_processor_id",
data = {format = "json"}
},
{
action = "StoreResult",
processor = "storage_process_id",
data = {table = "results"}
}
},
data = {
userId = "user123",
timestamp = os.time()
}
})
})
-- 2. Query workflow status
Send({
Target = "workflow_processor_id",
Action = "GetWorkflowStatus",
Tags = {WorkflowId = "workflow_id_here"}
})
-- 3. Cancel workflow
Send({
Target = "workflow_processor_id",
Action = "CancelWorkflow",
Tags = {WorkflowId = "workflow_id_here"}
})
セキュリティ考慮事項とベストプラクティス
入力検証パターン
-- Comprehensive input validation
function validateMessage(msg)
local errors = {}
-- Validate sender
if not msg.From or #msg.From == 0 then
table.insert(errors, "Invalid sender")
end
-- Validate action
local validActions = {"Transfer", "Mint", "Burn", "Approve"}
if not msg.Tags.Action or not contains(validActions, msg.Tags.Action) then
table.insert(errors, "Invalid action")
end
-- Validate data based on action
if msg.Tags.Action == "Transfer" or msg.Tags.Action == "Mint" then
local amount = tonumber(msg.Data)
if not amount or amount <= 0 then
table.insert(errors, "Invalid amount")
end
end
return #errors == 0, errors
end
-- Use validation in handlers
Handlers.add(
"secureHandler",
Handlers.utils.hasMatchingTag("Action", "SecureOperation"),
function(msg)
local isValid, errors = validateMessage(msg)
if not isValid then
Send({
Target = msg.From,
Action = "ValidationError",
Data = "Validation failed",
Tags = {
Errors = json.encode(errors)
}
})
return
end
-- Process valid message
processSecureOperation(msg)
end
)
レート制限
-- Rate limiting implementation
State.rateLimits = State.rateLimits or {}
function checkRateLimit(address, action, limit, window)
local key = address .. ":" .. action
local now = os.time()
local windowStart = now - window
-- Initialize if not exists
if not State.rateLimits[key] then
State.rateLimits[key] = {}
end
-- Clean old entries
local cleaned = {}
for _, timestamp in ipairs(State.rateLimits[key]) do
if timestamp > windowStart then
table.insert(cleaned, timestamp)
end
end
State.rateLimits[key] = cleaned
-- Check limit
if #State.rateLimits[key] >= limit then
return false, "Rate limit exceeded"
end
-- Add current request
table.insert(State.rateLimits[key], now)
return true, "OK"
end
この包括的なガイドでは、AOの機能とベストプラクティスを示す本番環境対応のコード例を提供しています。各例には、詳細なエラーハンドリング、セキュリティ考慮事項、実際のアプリケーションに適用できる拡張可能な設計パターンが含まれています。
【Arweave Japan とは】
Arweave Japan は Arweave / AO の日本語ビルダーエコシステム構築を目的とした分散型組織です。
【Arweave / AO とは?】
Arweave は無制限にスケール可能な分散型ストレージであり、AO は Arweave 上に構築された無制限にスケール可能な分散型スーパーコンピュータです。Arweave と AO を使って既存のブロックチェーンでは実現不可能であった実用的なプロダクトが開発できます。
イーサリアム L1 のリステーキングによってセキュリティが担保され、TVL はローンチ数ヶ月で 1000 億円近くまで上がり、今後数兆円規模の市場が期待されます。完全フェアローンチされた AO のトークン設計によって、この流動性は AO 上のプロジェクトが活用することができ、ビットコインと同じ半減スケジュールでミントされる AO トークンは開発者やプロジェクトが受け取れる仕組みとなっています。
Web2 を置き換えるレベルで実用的なプロジェクトが構築できる唯一無二の分散型プロトコル AO で開発することはグローバルの第一線で活躍する非常に大きなチャンスとなっています。
【Social Links】
【Blog】