Skip to content

Commit

Permalink
feat:adds load state
Browse files Browse the repository at this point in the history
  • Loading branch information
vilenarios committed May 10, 2024
1 parent 4704d3c commit 60b3633
Show file tree
Hide file tree
Showing 5 changed files with 195 additions and 33 deletions.
6 changes: 4 additions & 2 deletions contract/spec/arns_spec.lua
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
local testProcessId = "NdZ3YRwMB2AMwwFYjKn1g88Y9nRybTo0qhS1ORq_E7g"
local arns = require("arns")
local constants = require("constants")
require("state")

describe("arns", function()
local timestamp = os.clock()
Expand Down Expand Up @@ -41,7 +42,8 @@ describe("arns", function()
end)

it("should validate a permabuy request and add the record to global state and deduct balance from caller", function()
local status, result = pcall(arns.buyRecord, "test-permabuy", "permabuy", 1, "Bob", false, timestamp, testProcessId)
local status, result = pcall(arns.buyRecord, "test-permabuy", "permabuy", 1, "Bob", false, timestamp,
testProcessId)
assert.is_true(status)
assert.are.same({
purchasePrice = 3000,
Expand Down Expand Up @@ -78,7 +80,7 @@ describe("arns", function()
}
local status, result = pcall(arns.buyRecord, "test-name", "lease", 1, "Bob", false, timestamp, testProcessId)
assert.is_false(status)
assert.match("Name is already registered", result)
assert.match("Name is already registered", result)
end)

it('should throw an error if the user does not have enough funds', function()
Expand Down
60 changes: 60 additions & 0 deletions contract/spec/state_spec.lua
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
local state = require("state")
local startTimestamp = 0


local json = require("json") -- Assuming json library for encoding/decoding

describe("State loading functionality", function()
-- Sample data for testing
local validJson, invalidJson, emptyJson
local startTimestamp = os.time()

-- Setup function to initialize test data
before_each(function()
validJson = json.encode({
records = { key1 = { processId = 123, value = "Record1" } },
gateways = { key2 = { details = "Gateway1" } },
observations = { key3 = { observation = "Observation1" } },
distributions = { distribution1 = "Data1" },
demandFactoring = { demandKey = "Demand1" }
})
end)

-- Tests for each specific scenario
it("should correctly load state from valid JSON data", function()
local reply, err = state.loadState(validJson, startTimestamp)
assert.is_nil(err)
assert.is_true((string.find(reply, "Records Updated: 1")) > 0)
assert.is_true((string.find(reply, "Gateways Updated: 1")) > 0)
assert.is_true((string.find(reply, "Observations Updated: 1")) > 0)
assert.is_true((string.find(reply, "Distributions Updated: 1")) > 0)
assert.is_true((string.find(reply, "Demand Updated: 1")) > 0)
end)

it("should handle partial state update", function()
local partialJson = json.encode({ records = { key1 = { processId = 123, value = "Record2" } } })
local reply, err = state.loadState(partialJson, startTimestamp)

assert.is_nil(err)
assert.is_true((string.find(reply, "Records Updated: 1")) > 0)
Records = {}
end)


it("should handle JSON with missing expected fields", function()
local incompleteJson = json.encode({ records = 100, gateways = "gateways" }) -- No processId
local reply, err = state.loadState(incompleteJson, startTimestamp)

assert.is_false(reply)
assert.are.same("The 'records' field is missing or not in the expected format.", err)
end)

after_each(function()
-- Reset or clear global or shared variables if necessary
Records = nil
Gateways = nil
Observations = nil
Distributions = nil
Demand = nil
end)
end)
5 changes: 0 additions & 5 deletions contract/src/arns.lua
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,6 @@ local utils = require("utils")
local constants = require("constants")
local arns = {}

Balances = Balances or {}
Records = Records or {}
Auctions = Auctions or {}
Reserved = Reserved or {}


-- Needs auctions
-- Needs demand factor
Expand Down
36 changes: 33 additions & 3 deletions contract/src/main.lua
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
local process = { _version = "0.0.1" }

require("state")
local state = require("state")
local token = require("token")
local ao = require("ao")
local arns = require("arns")
Expand Down Expand Up @@ -181,7 +182,7 @@ Handlers.add(ActionMap.IncreaseVault, utils.hasMatchingTag("Action", ActionMap.I
end)

Handlers.add(ActionMap.BuyRecord, utils.hasMatchingTag("Action", ActionMap.BuyRecord), function(msg)
local success, result = pcall(arns.buyRecord,
local success, result = pcall(arns.buyRecord,
msg.Tags.Name,
msg.Tags.PurchaseType,
msg.Tags.Years,
Expand All @@ -200,7 +201,7 @@ Handlers.add(ActionMap.BuyRecord, utils.hasMatchingTag("Action", ActionMap.BuyRe
},
Data = tostring(result),
})
return
return
else
ao.send({
Target = msg.From,
Expand Down Expand Up @@ -248,7 +249,8 @@ end)
Handlers.add(ActionMap.IncreaseUndernameCount,
utils.hasMatchingTag("Action", ActionMap.IncreaseUndernameCount),
function(msg)
local status, result = pcall(arns.increaseUndernameCount, msg.From, msg.Tags.Name, msg.Tags.Quantity, msg.Timestamp)
local status, result = pcall(arns.increaseUndernameCount, msg.From, msg.Tags.Name, msg.Tags.Quantity,
msg.Timestamp)
if not status then
ao.send({
Target = msg.From,
Expand Down Expand Up @@ -442,4 +444,32 @@ Handlers.add(ActionMap.DemandFactor, utils.hasMatchingTag("Action", ActionMap.De
end
end)

Handlers.add(ActionMap.LoadState, utils.hasMatchingTag("Action", ActionMap.LoadState), function(msg, env)
-- Validate if the message is from the process owner to ensure that only authorized updates are processed.
if msg.From ~= env.Process.Id and msg.From ~= Owner then
print("Unauthorized data update attempt detected from: " .. msg.From)
-- Sending an error notice back to the sender might be a security concern in some contexts, consider this based on your application's requirements.
ao.send({
Target = msg.From,
Tags = { Action = "Load-State-Error", Error = "Unauthorized attempt detected" },
})
return
end
local result, err = state.loadState(msg.Data, msg.Timestamp)
if err then
ao.send({
Target = msg.From,
Tags = { Action = 'Invalid-State-Load' },
Data = tostring(err)
})
else
-- Notify the process owner about the successful update.
ao.send({
Target = msg.From,
Tags = { Action = "Loaded-State" },
Data = tostring(result)
})
end
end)

return process;
121 changes: 98 additions & 23 deletions contract/src/state.lua
Original file line number Diff line number Diff line change
@@ -1,40 +1,115 @@
local state = { _version = '0.0.0' }
local demand = require('demand')
local json = require('json')

Name = Name or "Test IO"
Ticker = Ticker or "tIO"
Logo = Logo or "Sie_26dvgyok0PZD_-iQAFOhOd5YxDTkczOLoqTTL_A"

if not Denomination then
Denomination = 6
end
LastStateLoad = LastStateLoad or nil

if not Balances then
Balances = {}
end
Balances = Balances or {}
Records = Records or {}
Auctions = Auctions or {}
Reserved = Reserved or {}
Denomination = Denomination or 6
Gateways = Gateways or {}
Vaults = Vaults or {}
PrescribedObservers = PrescribedObservers or {}
Observations = Observations or {}
Distributions = Distributions or {}
Demand = Demand or {}

if not Gateways then
Gateways = {}
end
function state.loadState(data, currentTimestamp)
local data, err = json.decode(data)
if not data or err then
-- Handle error (e.g., send an error response)
return false, "Error decoding JSON data"
end

if not Vaults then
Vaults = {}
end
-- Counter for added or updated records.
local recordsAddedOrUpdated = 0
-- Ensure 'data.records' is present and iterate through the decoded data to update the Records table accordingly.
if data.records then
if type(data.records) == "table" then
for key, value in pairs(data.records) do
-- Preserve the existing processId if the record already exists.
local existingProcessId = Records[key] and Records[key].processId

if not PrescribedObservers then
PrescribedObservers = {}
end
-- Check if the record either doesn't exist or differs from the new value.
if not Records[key] or (Records[key] and json.encode(Records[key]) ~= json.encode(value)) then
recordsAddedOrUpdated = recordsAddedOrUpdated + 1
Records[key] = value
Records[key].processId = existingProcessId or value.processId -- Preserve or initialize processId.
end
end
else
-- Handle the case where 'data.records' is not in the expected format.
return false, "The 'records' field is missing or not in the expected format."
end
end

if not Observations then
Observations = {}
end
local gatewaysAddedOrUpdated = 0
if data.gateways then
if type(data.gateways) == "table" then
for key, value in pairs(data.gateways) do
if not Gateways[key] or (Gateways[key] and json.encode(Gateways[key]) ~= json.encode(value)) then
gatewaysAddedOrUpdated = gatewaysAddedOrUpdated + 1
Gateways[key] = value
end
end
else
-- Handle the case where 'data.gateways' is not in the expected format.
return false, "The 'gateways' field is missing or not in the expected format."
end
end

if not Distributions then
Distributions = {}
end
local observationsAddedOrUpdated = 0
if data.observations then
if type(data.observations) == "table" then
for key, value in pairs(data.observations) do
if not Observations[key] or (Observations[key] and json.encode(Observations[key]) ~= json.encode(value)) then
observationsAddedOrUpdated = observationsAddedOrUpdated + 1
Observations[key] = value
end
end
else
-- Handle the case where 'data.observations' is not in the expected format.
return false, "The 'gatewobservationsays' field is missing or not in the expected format."
end
end

local distributionsUpdated = 0
if data.distributions then
if type(data.distributions) == "table" then
Distributions = data.distributions
distributionsUpdated = distributionsUpdated + 1
else
-- Handle the case where 'data.distributions' is not in the expected format.
return false, "The 'distributions' field is missing or not in the expected format."
end
end

local demandUpdated = 0
if data.demandFactoring then
if type(data.demandFactoring) == "table" then
Demand = data.demandFactoring
demandUpdated = demandUpdated + 1
else
-- Handle the case where 'data.demandFactoring' is not in the expected format.
return false, "The 'demandFactoring' field is missing or not in the expected format."
end
end

LastStateLoad = currentTimestamp

if not Demand then
Demand = demand
return "Records Updated: " ..
recordsAddedOrUpdated ..
" Gateways Updated: " ..
gatewaysAddedOrUpdated ..
" Observations Updated: " ..
observationsAddedOrUpdated ..
" Distributions Updated: " .. distributionsUpdated .. " Demand Updated: " .. demandUpdated
end

return state

0 comments on commit 60b3633

Please sign in to comment.