From 747a60b2f252835e0558ff9902fe0a65faf4e449 Mon Sep 17 00:00:00 2001 From: dtfiedler Date: Mon, 24 Jun 2024 22:49:59 -0600 Subject: [PATCH] fix(handlers): ensure json.encode everything in data and update records --- src/constants.lua | 4 +- src/demand.lua | 4 ++ src/main.lua | 121 +++++++++++++++++++++++++++++++++++----------- 3 files changed, 98 insertions(+), 31 deletions(-) diff --git a/src/constants.lua b/src/constants.lua index 256f5b5..fbae895 100644 --- a/src/constants.lua +++ b/src/constants.lua @@ -16,8 +16,8 @@ constants.ANNUAL_PERCENTAGE_FEE = 0.2 constants.ARNS_NAME_DOES_NOT_EXIST_MESSAGE = "Name does not exist in the ArNS Registry!" constants.ARNS_MAX_UNDERNAME_MESSAGE = "Name has reached undername limit of 10000" constants.MAX_ALLOWED_UNDERNAMES = 10000 -constants.UNDERNAME_LEASE_FEE_PERCENTAGE = 0.001 -constants.UNDERNAME_PERMABUY_FEE_PERCENTAGE = 0.005 +constants.UNDERNAME_LEASE_FEE_PERCENTAGE = 0.001 -- 0.1% +constants.UNDERNAME_PERMABUY_FEE_PERCENTAGE = 0.005 -- 0.5% constants.oneYearMs = 31536000 * 1000 constants.gracePeriodMs = 3 * 7 * 24 * 60 * 60 * 1000 constants.maxLeaseLengthYears = 5 diff --git a/src/demand.lua b/src/demand.lua index 8dbdcac..cab4421 100644 --- a/src/demand.lua +++ b/src/demand.lua @@ -68,6 +68,10 @@ end -- update at the end of the demand if the current timestamp results in a period greater than our current state function demand.shouldUpdateDemandFactor(currentTimestamp) local settings = demand.getSettings() + -- if current timestamp is before the start timestamp, return false + if currentTimestamp < DemandFactor.startTimestamp then + return false + end local calculatedPeriod = math.floor((currentTimestamp - DemandFactor.startTimestamp) / settings.periodLengthMs) + 1 return calculatedPeriod > demand.getCurrentPeriod() end diff --git a/src/main.lua b/src/main.lua index 13592af..807e9b1 100644 --- a/src/main.lua +++ b/src/main.lua @@ -1,6 +1,5 @@ -- Adjust package.path to include the current directory local process = { _version = "0.0.1" } -local constants = require("constants") Name = Name or "Testnet IO" Ticker = Ticker or "tIO" @@ -19,7 +18,7 @@ Vaults = Vaults or {} GatewayRegistry = GatewayRegistry or {} NameRegistry = NameRegistry or {} Epochs = Epochs or {} -LastTickedEpoch = LastTickedEpoch or 0 +LastTickedEpochIndex = LastTickedEpochIndex or -1 local utils = require("utils") local json = require("json") @@ -172,7 +171,7 @@ Handlers.add(ActionMap.CreateVault, utils.hasMatchingTag("Action", ActionMap.Cre ao.send({ Target = msg.From, Tags = { Action = "Vault-Created-Notice" }, - Data = tostring(json.encode(result)), + Data = json.encode(result), }) end end) @@ -221,12 +220,12 @@ Handlers.add(ActionMap.VaultedTransfer, utils.hasMatchingTag("Action", ActionMap Recipient = msg.Tags.Recipient, Quantity = msg.Tags.Quantity, Tags = { Action = "Debit-Notice" }, - Data = tostring(json.encode(result)), + Data = json.encode(result), }) ao.send({ Target = msg.Tags.Recipient, Tags = { Action = "Vaulted-Credit-Notice" }, - Data = tostring(json.encode(result)), + Data = json.encode(result), }) end end) @@ -262,7 +261,7 @@ Handlers.add(ActionMap.ExtendVault, utils.hasMatchingTag("Action", ActionMap.Ext ao.send({ Target = msg.From, Tags = { Action = "Vault-Extended-Notice" }, - Data = tostring(json.encode(result)), + Data = json.encode(result), }) end end) @@ -298,7 +297,7 @@ Handlers.add(ActionMap.IncreaseVault, utils.hasMatchingTag("Action", ActionMap.I ao.send({ Target = msg.From, Tags = { Action = "Vault-Increased-Notice" }, - Data = tostring(json.encode(result)), + Data = json.encode(result), }) end end) @@ -348,7 +347,7 @@ Handlers.add(ActionMap.BuyRecord, utils.hasMatchingTag("Action", ActionMap.BuyRe ao.send({ Target = msg.From, Tags = { Action = "Buy-Record-Notice", Name = msg.Tags.Name }, - Data = tostring(json.encode(result)), + Data = json.encode(result), }) end end) @@ -384,7 +383,7 @@ Handlers.add(ActionMap.ExtendLease, utils.hasMatchingTag("Action", ActionMap.Ext ao.send({ Target = msg.From, Tags = { Action = "Extend-Lease-Notice", Name = msg.Tags.Name }, - Data = tostring(json.encode(result)), + Data = json.encode(result), }) end end) @@ -426,7 +425,7 @@ Handlers.add( ao.send({ Target = msg.From, Tags = { Action = "Increase-Undername-Limit-Notice", Name = msg.Tags.Name }, - Data = tostring(json.encode(result)), + Data = json.encode(result), }) end end @@ -472,7 +471,7 @@ Handlers.add(ActionMap.TokenCost, utils.hasMatchingTag("Action", ActionMap.Token years = tonumber(msg.Tags.Years) or 1, quantity = tonumber(msg.Tags.Quantity), purchaseType = msg.Tags["Purchase-Type"] or "lease", - currentTimestamp = tonumber(msg.Timestamp), + currentTimestamp = tonumber(msg.Timestamp) or tonumber(msg.Tags.Timestamp), }) if not status then ao.send({ @@ -483,8 +482,8 @@ Handlers.add(ActionMap.TokenCost, utils.hasMatchingTag("Action", ActionMap.Token else ao.send({ Target = msg.From, - Tags = { Action = "Token-Cost-Notice", TokenCost = tostring(result) }, - Data = tostring(json.encode(result)), + Tags = { Action = "Token-Cost-Notice", ["Token-Cost"] = tostring(result) }, + Data = json.encode(result), }) end end) @@ -522,7 +521,7 @@ Handlers.add(ActionMap.JoinNetwork, utils.hasMatchingTag("Action", ActionMap.Joi ao.send({ Target = msg.From, Tags = { Action = "Join-Network-Notice" }, - Data = tostring(json.encode(result)), + Data = json.encode(result), }) end end) @@ -539,7 +538,7 @@ Handlers.add(ActionMap.LeaveNetwork, utils.hasMatchingTag("Action", ActionMap.Le ao.send({ Target = msg.From, Tags = { Action = "Leave-Network-Notice" }, - Data = tostring(json.encode(result)), + Data = json.encode(result), }) end end) @@ -578,7 +577,7 @@ Handlers.add( ao.send({ Target = msg.From, Tags = { Action = "Increase-Operator-Stake-Notice" }, - Data = tostring(json.encode(result)), + Data = json.encode(result), }) end end @@ -617,7 +616,7 @@ Handlers.add( ao.send({ Target = msg.From, Tags = { Action = "Decrease-Operator-Stake-Notice" }, - Data = tostring(json.encode(result)), + Data = json.encode(result), }) end end @@ -649,13 +648,13 @@ Handlers.add(ActionMap.DelegateStake, utils.hasMatchingTag("Action", ActionMap.D ao.send({ Target = msg.From, Tags = { Action = "Invalid-Delegate-Stake-Notice", Error = "Invalid-Delegate-Stake", Message = result }, - Data = tostring(json.encode(result)), + Data = json.encode(result), }) else ao.send({ Target = msg.From, Tags = { Action = "Delegate-Stake-Notice", Gateway = msg.Tags.Target }, - Data = tostring(json.encode(result)), + Data = json.encode(result), }) end end) @@ -789,24 +788,79 @@ Handlers.add(ActionMap.SaveObservations, utils.hasMatchingTag("Action", ActionMa end end) +Handlers.add("Epoch-Settings", utils.hasMatchingTag("Action", "Epoch-Settings"), function(msg) + local epochSettings = epochs.getSettings() + ao.send({ + Target = msg.From, + Action = "Epoch-Settings-Notice", + Data = json.encode(epochSettings), + }) +end) + -- TICK HANDLER Handlers.add("tick", utils.hasMatchingTag("Action", "Tick"), function(msg) - local timestamp = tonumber(msg.Timestamp) - -- TODO: how do we make this update atomic so that the state is changed all or nothing (should we?) - local lastTickedEpochIndex = LastTickedEpoch - local currentEpochIndex = epochs.getEpochIndexForTimestamp(timestamp) - local function tickState(timestamp, blockHeight, hashchain) + -- assert this is a write interaction and we have a timetsamp + assert(msg.Timestamp, "Timestamp is required for a tick interaction") + -- tick the things that only require timestamp and don't need to happen for every epoch + local function tickState(timestamp) arns.pruneRecords(timestamp) arns.pruneReservedNames(timestamp) vaults.pruneVaults(timestamp) gar.pruneGateways(timestamp) + end + + local previousState = { + Balances = utils.deepCopy(Balances), + Vaults = utils.deepCopy(Vaults), + GatewayRegistry = utils.deepCopy(GatewayRegistry), + NameRegistry = utils.deepCopy(NameRegistry), + Epochs = utils.deepCopy(Epochs), + DemandFactor = utils.deepCopy(DemandFactor), + LastTickedEpochIndex = utils.deepCopy(LastTickedEpochIndex), + } + local msgTimestamp = tonumber(msg.Timestamp) + + -- tick the state and demand factor using just the timestamp + local stateStatus, stateResult = pcall(tickState, msgTimestamp) + if not stateStatus then + -- reset the state to previous state + Balances = previousState.Balances + Vaults = previousState.Vaults + GatewayRegistry = previousState.GatewayRegistry + NameRegistry = previousState.NameRegistry + Epochs = previousState.Epochs + DemandFactor = previousState.DemandFactor + LastTickedEpochIndex = previousState.LastTickedEpochIndex + ao.send({ + Target = msg.From, + Action = "Invalid-Tick-Notice", + Error = "Invalid-Tick", + Data = json.encode(stateResult), + }) + end + + -- tick and distribute rewards for every index between the last ticked epoch and the current epoch + local function tickEpochs(timestamp, blockHeight, hashchain) + -- update demand factor if necessary demand.updateDemandFactor(timestamp) epochs.distributeRewardsForEpoch(timestamp) epochs.createEpoch(timestamp, tonumber(blockHeight), hashchain) end + local lastTickedEpochIndex = LastTickedEpochIndex + local currentEpochIndex = epochs.getEpochIndexForTimestamp(msgTimestamp) + -- if epoch index is -1 then we are before the genesis epoch and we should not tick + if currentEpochIndex < 0 then + ao.send({ + Target = msg.From, + Action = "Invalid-Tick-Notice", + Error = "Invalid-Tick", + Data = json.encode("Cannot tick before genesis epoch"), + }) + end + -- tick and distribute rewards for every index between the last ticked epoch and the current epoch - for i = lastTickedEpochIndex + 1, currentEpochIndex - 1 do + for i = lastTickedEpochIndex + 1, currentEpochIndex do local previousState = { Balances = utils.deepCopy(Balances), Vaults = utils.deepCopy(Vaults), @@ -814,13 +868,21 @@ Handlers.add("tick", utils.hasMatchingTag("Action", "Tick"), function(msg) NameRegistry = utils.deepCopy(NameRegistry), Epochs = utils.deepCopy(Epochs), DemandFactor = utils.deepCopy(DemandFactor), + LastTickedEpochIndex = utils.deepCopy(LastTickedEpochIndex), } local _, _, epochDistributionTimestamp = epochs.getEpochTimestampsForIndex(i) - -- TODO: if we need to "recover" epochs, we can't rely on just the current message hashchain and block height - local status, result = pcall(tickState, epochDistributionTimestamp, msg["Block-Height"], msg["Hash-Chain"]) + -- use the minimum of the msg timestamp or the epoch distribution timestamp, this ensures an epoch gets created for the genesis block and that we don't try and distribute before an epoch is created + local tickTimestamp = math.min(msgTimestamp or 0, epochDistributionTimestamp) + -- TODO: if we need to "recover" epochs, we can't rely on just the current message hashchain and block height, we should set the prescribed observers and names to empty arrays and distribute rewards accordingly + local status, result = pcall(tickEpochs, tickTimestamp, msg["Block-Height"], msg["Hash-Chain"]) if status then - ao.send({ Target = msg.From, Action = "Tick-Notice", Data = json.encode(result) }) - LastTickedEpoch = i -- update the last ticked state + LastTickedEpochIndex = i -- update the last ticked state + ao.send({ + Target = msg.From, + Action = "Tick-Notice", + LastTickedEpochIndex = LastTickedEpochIndex, + Data = json.encode(result), + }) else -- reset the state to previous state Balances = previousState.Balances @@ -829,6 +891,7 @@ Handlers.add("tick", utils.hasMatchingTag("Action", "Tick"), function(msg) NameRegistry = previousState.NameRegistry Epochs = previousState.Epochs DemandFactor = previousState.DemandFactor + LastTickedEpochIndex = previousState.LastTickedEpochIndex ao.send({ Target = msg.From, Action = "Invalid-Tick-Notice",