4
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

Arweave AOのLuaサンプル: カウンター・トークン [AO Lua ガイド 3/3]

Last updated at Posted at 2025-07-08

実用的なコード例

この包括的なガイドでは、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】

1080x360.jpeg

4
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
4
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?