diff --git a/db/init.js b/db/init.js index c3d86c8..4a88350 100644 --- a/db/init.js +++ b/db/init.js @@ -18,9 +18,9 @@ const ormconfig = { require('typeorm').createConnection(ormconfig).then(async con => { try { - await con.runMigrations({transaction: 'all'}) + await con.runMigrations({ transaction: 'all' }) } finally { - await con.close().catch(err => null) + await con.destroy().catch(err => null) } }).then( () => process.exit(), diff --git a/db/migrations/1694605589133-denormalize_data.js b/db/migrations/1694605589133-denormalize_data.js new file mode 100644 index 0000000..e41755c --- /dev/null +++ b/db/migrations/1694605589133-denormalize_data.js @@ -0,0 +1,65 @@ +module.exports = class denormalize_data1694605589133 { + name = 'denormalize_data1694605589133' + + async up(db) { + await db.query(`CREATE TABLE "contract" ("id" character varying NOT NULL, "grid_version" integer NOT NULL, "contract_id" numeric NOT NULL, "twin_id" integer NOT NULL, "node_id" integer, "deployment_data" text, "deployment_hash" text, "number_of_public_i_ps" integer, "state" character varying(11) NOT NULL, "created_at" numeric NOT NULL, "solution_provider_id" integer, "used_cru" numeric, "used_mru" numeric, "used_sru" numeric, "used_hru" numeric, "name" text, "type" character varying(4) NOT NULL, CONSTRAINT "PK_17c3a89f58a2997276084e706e8" PRIMARY KEY ("id"))`) + await db.query(`CREATE INDEX "IDX_2f25fae55a3bd80337501b310e" ON "contract" ("contract_id") `) + await db.query(`CREATE INDEX "IDX_e9713e5dd661bc93d698d725e8" ON "contract" ("state") `) + await db.query(`CREATE INDEX "IDX_59d5a7aca69d7ea58c101a44c6" ON "contract" ("type") `) + await db.query(`ALTER TABLE "farm" ADD "total_ips" integer NOT NULL`) + await db.query(`ALTER TABLE "farm" ADD "free_ips" integer NOT NULL`) + await db.query(`ALTER TABLE "node" ADD "total_cru" numeric NOT NULL`) + await db.query(`ALTER TABLE "node" ADD "total_hru" numeric NOT NULL`) + await db.query(`ALTER TABLE "node" ADD "total_sru" numeric NOT NULL`) + await db.query(`ALTER TABLE "node" ADD "total_mru" numeric NOT NULL`) + await db.query(`ALTER TABLE "node" ADD "free_mru" numeric NOT NULL`) + await db.query(`ALTER TABLE "node" ADD "free_hru" numeric NOT NULL`) + await db.query(`ALTER TABLE "node" ADD "free_sru" numeric NOT NULL`) + await db.query(`ALTER TABLE "node" ADD "rented_by" integer`) + await db.query(`ALTER TABLE "public_ip" ALTER COLUMN "contract_id" DROP NOT NULL`) + await db.query(`CREATE INDEX "IDX_a162f043823849732649d40627" ON "twin" ("twin_id") `) + await db.query(`CREATE INDEX "IDX_5f29012fe3403c0cf194457ecd" ON "public_ip" ("contract_id") `) + await db.query(`CREATE INDEX "IDX_a09d726d0ff006b47ff4d25428" ON "farm" ("farm_id") `) + await db.query(`CREATE INDEX "IDX_e6523793483b172e422dc225ad" ON "node" ("node_id") `) + await db.query(`CREATE INDEX "IDX_4d4375648edaa07add3df9d876" ON "node" ("free_mru") `) + await db.query(`CREATE INDEX "IDX_b6ff61e1f0b66b70374d1168d4" ON "node" ("free_hru") `) + await db.query(`CREATE INDEX "IDX_1d93b5617168669d707d52077d" ON "node" ("free_sru") `) + await db.query(`CREATE INDEX "IDX_23c232066e25d61f3a703b1c08" ON "contract_bill_report" ("contract_id") `) + await db.query(`DROP TABLE "rent_contract"`) + await db.query(`DROP TABLE "name_contract"`) + await db.query(`DROP TABLE "contract_resources" cascade`) + await db.query(`DROP TABLE "node_contract" cascade`) + } + + async down(db) { + await db.query(`DROP TABLE "contract"`) + await db.query(`DROP INDEX "public"."IDX_2f25fae55a3bd80337501b310e"`) + await db.query(`DROP INDEX "public"."IDX_e9713e5dd661bc93d698d725e8"`) + await db.query(`DROP INDEX "public"."IDX_59d5a7aca69d7ea58c101a44c6"`) + await db.query(`ALTER TABLE "farm" DROP COLUMN "total_ips"`) + await db.query(`ALTER TABLE "farm" DROP COLUMN "free_ips"`) + await db.query(`ALTER TABLE "node" DROP COLUMN "total_cru"`) + await db.query(`ALTER TABLE "node" DROP COLUMN "total_hru"`) + await db.query(`ALTER TABLE "node" DROP COLUMN "total_sru"`) + await db.query(`ALTER TABLE "node" DROP COLUMN "total_mru"`) + await db.query(`ALTER TABLE "node" DROP COLUMN "free_mru"`) + await db.query(`ALTER TABLE "node" DROP COLUMN "free_hru"`) + await db.query(`ALTER TABLE "node" DROP COLUMN "free_sru"`) + await db.query(`ALTER TABLE "node" DROP COLUMN "rented_by"`) + await db.query(`ALTER TABLE "public_ip" ALTER COLUMN "contract_id" SET NOT NULL`) + await db.query(`DROP INDEX "public"."IDX_a162f043823849732649d40627"`) + await db.query(`DROP INDEX "public"."IDX_5f29012fe3403c0cf194457ecd"`) + await db.query(`DROP INDEX "public"."IDX_a09d726d0ff006b47ff4d25428"`) + await db.query(`DROP INDEX "public"."IDX_e6523793483b172e422dc225ad"`) + await db.query(`DROP INDEX "public"."IDX_4d4375648edaa07add3df9d876"`) + await db.query(`DROP INDEX "public"."IDX_b6ff61e1f0b66b70374d1168d4"`) + await db.query(`DROP INDEX "public"."IDX_1d93b5617168669d707d52077d"`) + await db.query(`DROP INDEX "public"."IDX_23c232066e25d61f3a703b1c08"`) + await db.query(`CREATE TABLE "node_contract" ("id" character varying NOT NULL, "grid_version" integer NOT NULL, "contract_id" numeric NOT NULL, "twin_id" integer NOT NULL, "node_id" integer NOT NULL, "deployment_data" text NOT NULL, "deployment_hash" text NOT NULL, "number_of_public_i_ps" integer NOT NULL, "state" character varying(11) NOT NULL, "created_at" numeric NOT NULL, "resources_used_id" character varying, "solution_provider_id" integer, CONSTRAINT "PK_a5f90b17f504ffcd79d1f66574a" PRIMARY KEY ("id"))`) + await db.query(`ALTER TABLE "node_contract" ADD CONSTRAINT "FK_f294cfb50bb7c7b976d86c08fda" FOREIGN KEY ("resources_used_id") REFERENCES "contract_resources"("id") ON DELETE NO ACTION ON UPDATE NO ACTION`) + await db.query(`CREATE TABLE "rent_contract" ("id" character varying NOT NULL, "grid_version" integer NOT NULL, "contract_id" numeric NOT NULL, "twin_id" integer NOT NULL, "node_id" integer NOT NULL, "state" character varying(11) NOT NULL, "created_at" numeric NOT NULL, "solution_provider_id" integer, CONSTRAINT "PK_3c99766b627604d5950d704e33a" PRIMARY KEY ("id"))`) + await db.query(`CREATE TABLE "name_contract" ("id" character varying NOT NULL, "grid_version" integer NOT NULL, "contract_id" numeric NOT NULL, "twin_id" integer NOT NULL, "name" text NOT NULL, "state" character varying(11) NOT NULL, "created_at" numeric NOT NULL, "solution_provider_id" integer, CONSTRAINT "PK_7b4cd056bbb83602d211996360f" PRIMARY KEY ("id"))`) + await db.query(`CREATE TABLE "contract_resources" ("id" character varying NOT NULL, "hru" numeric NOT NULL, "sru" numeric NOT NULL, "cru" numeric NOT NULL, "mru" numeric NOT NULL, "contract_id" character varying, CONSTRAINT "PK_557de19994fcca90916e8c6582f" PRIMARY KEY ("id"))`) + await db.query(`ALTER TABLE "contract_resources" ADD CONSTRAINT "FK_621238dffde9099b2233650235d" FOREIGN KEY ("contract_id") REFERENCES "node_contract"("id") ON DELETE NO ACTION ON UPDATE NO ACTION`) + } +} diff --git a/schema.graphql b/schema.graphql index 27b0c1f..d609e6a 100644 --- a/schema.graphql +++ b/schema.graphql @@ -9,7 +9,7 @@ type Entity @entity { type Twin @entity { gridVersion: Int! - twinID: Int! + twinID: Int! @index accountID: String! relay: String publicKey: String @@ -23,12 +23,14 @@ type EntityProof @entity { type Farm @entity { gridVersion: Int! - farmID: Int! + farmID: Int! @index name: String! twinID: Int! pricingPolicyID: Int! certification: FarmCertification publicIPs: [PublicIp!] @derivedFrom(field: "farm") + totalIps: Int! + freeIps: Int! stellarAddress: String dedicatedFarm: Boolean } @@ -37,19 +39,18 @@ type PublicIp @entity { farm: Farm! gateway: String! ip: String! - contractId: BigInt! + contractId: BigInt @index } type Node @entity { gridVersion: Int! - nodeID: Int! + nodeID: Int! @index farmID: Int! twinID: Int! location: Location! country: String city: String publicConfig: PublicConfig @derivedFrom(field: "node") - resourcesTotal: NodeResourcesTotal @derivedFrom(field: "node") uptime: BigInt created: Int! farmingPolicyId: Int! @@ -64,6 +65,14 @@ type Node @entity { power: NodePower dedicated: Boolean! extraFee: BigInt + totalCRU: BigInt! + totalHRU: BigInt! + totalSRU: BigInt! + totalMRU: BigInt! + freeMRU: BigInt! @index + freeHRU: BigInt! @index + freeSRU: BigInt! @index + rentedBy: Int } type NodePower { @@ -81,14 +90,6 @@ enum Power { Down } -type NodeResourcesTotal @entity { - node: Node! @unique - hru: BigInt! - sru: BigInt! - cru: BigInt! - mru: BigInt! -} - type Interfaces @entity { node: Node! name: String! @@ -154,46 +155,23 @@ type City @entity { name: String! } -type NodeContract @entity { - gridVersion: Int! - contractID: BigInt! - twinID: Int! - nodeID: Int! - deploymentData: String! - deploymentHash: String! - numberOfPublicIPs: Int! - state: ContractState! - resourcesUsed: ContractResources - createdAt: BigInt! - solutionProviderID: Int -} - -type ContractResources @entity { - contract: NodeContract! - hru: BigInt! - sru: BigInt! - cru: BigInt! - mru: BigInt! -} - -type NameContract @entity { - gridVersion: Int! - contractID: BigInt! - twinID: Int! - name: String! - state: ContractState! - createdAt: BigInt! - solutionProviderID: Int -} - -type RentContract @entity { +type Contract @entity{ gridVersion: Int! - contractID: BigInt! + contractID: BigInt! @index twinID: Int! - nodeID: Int! - state: ContractState! + nodeID: Int + deploymentData: String + deploymentHash: String + numberOfPublicIPs: Int + state: ContractState! @index createdAt: BigInt! solutionProviderID: Int + usedCRU: BigInt + usedMRU: BigInt + usedSRU: BigInt + usedHRU: BigInt + name: String + type: ContractType! @index } type SolutionProvider @entity { @@ -216,6 +194,12 @@ enum ContractState { GracePeriod } +enum ContractType { + Node + Name + Rent +} + type NruConsumption @entity { contractID: BigInt! timestamp: BigInt! @@ -224,7 +208,7 @@ type NruConsumption @entity { } type ContractBillReport @entity { - contractID: BigInt! + contractID: BigInt! @index discountReceived: DiscountLevel! amountBilled: BigInt! timestamp: BigInt! diff --git a/src/mappings/contracts.ts b/src/mappings/contracts.ts index d626c85..f0c1e73 100644 --- a/src/mappings/contracts.ts +++ b/src/mappings/contracts.ts @@ -4,9 +4,9 @@ import { EventItem } from '@subsquid/substrate-processor/lib/interfaces/dataSele import { In } from 'typeorm' import { - ContractState, PublicIp, NameContract, - NodeContract, ContractBillReport, DiscountLevel, - ContractResources, Node, RentContract, NruConsumption + ContractState, PublicIp, Contract, + ContractBillReport, DiscountLevel, + Node, NruConsumption, ContractType, Farm } from "../model"; import { SmartContractModuleContractCreatedEvent, SmartContractModuleContractUpdatedEvent, @@ -44,8 +44,9 @@ export async function contractCreated( let contract if (contractEvent.contractType.__kind === "NameContract") { - let newNameContract = new NameContract() + let newNameContract = new Contract() newNameContract.id = item.event.id + newNameContract.type = ContractType.Name contract = contractEvent.contractType.value newNameContract.name = contract.name.toString() newNameContract.contractID = contractEvent.contractId @@ -57,11 +58,13 @@ export async function contractCreated( contractEvent = contractCreatedEvent.asV105 newNameContract.solutionProviderID = Number(contractEvent.solutionProviderId) || 0 } - await ctx.store.save(newNameContract) + + await ctx.store.save(newNameContract) } else if (contractEvent.contractType.__kind === "NodeContract") { - let newNodeContract = new NodeContract() + let newNodeContract = new Contract() newNodeContract.id = item.event.id + newNodeContract.type = ContractType.Node contract = contractEvent.contractType.value @@ -97,10 +100,15 @@ export async function contractCreated( }, relations: { farm: true } }) - touchedIps = touchedIps.map(ip => { + touchedIps = await Promise.all(touchedIps.map(async (ip) => { ip.contractId = newNodeContract.contractID + let farm = await ctx.store.get(Farm, { where: { farmID: ip.farm.farmID } }) + if (farm) { + farm.freeIps -= 1 + await ctx.store.save(farm) + } return ip - }) + })) if (contract.publicIps > 0 && touchedIps.length == 0) { console.log(`something went wrong with contract ${contractEvent.contractId}`) @@ -113,11 +121,11 @@ export async function contractCreated( } await ctx.store.save(touchedIps) - await ctx.store.save(newNodeContract) + await ctx.store.save(newNodeContract) } else if (contractEvent.contractType.__kind === "RentContract") { - let newRentContract = new RentContract() + let newRentContract = new Contract() newRentContract.id = item.event.id - + newRentContract.type = ContractType.Rent contract = contractEvent.contractType.value newRentContract.contractID = contractEvent.contractId @@ -130,12 +138,13 @@ export async function contractCreated( contractEvent = contractCreatedEvent.asV105 newRentContract.solutionProviderID = Number(contractEvent.solutionProviderId) || 0 } - await ctx.store.save(newRentContract) + await ctx.store.save(newRentContract) // Update node to dedicated if it is rented const savedNode = await ctx.store.get(Node, { where: { nodeID: contract.nodeId }, relations: { location: true, interfaces: true } }) if (savedNode) { savedNode.dedicated = true + savedNode.rentedBy = newRentContract.twinID await ctx.store.save(savedNode) } } @@ -162,18 +171,20 @@ export async function contractUpdated( if (!contractEvent) return - const SavedNodeContract = await ctx.store.get(NodeContract, { where: { contractID: contractEvent.contractId } }) + const SavedNodeContract = await ctx.store.get(Contract, { where: { contractID: contractEvent.contractId } }) if (SavedNodeContract) { await updateNodeContract(contractEvent, SavedNodeContract, ctx.store) + return } - const SavedNameContract = await ctx.store.get(NameContract, { where: { contractID: contractEvent.contractId } }) + const SavedNameContract = await ctx.store.get(Contract, { where: { contractID: contractEvent.contractId } }) if (SavedNameContract) { await updateNameContract(contractEvent, SavedNameContract, ctx.store) + return } } -async function updateNodeContract(ctr: any, contract: NodeContract, store: Store) { +async function updateNodeContract(ctr: any, contract: Contract, store: Store) { if (ctr.contractType.__kind !== "NodeContract") return const parsedNodeContract = ctr.contractType.value @@ -183,17 +194,8 @@ async function updateNodeContract(ctr: any, contract: NodeContract, store: Store contract.twinID = ctr.twinId contract.nodeID = parsedNodeContract.nodeId contract.numberOfPublicIPs = parsedNodeContract.publicIps - - if (contract.deploymentData.toString().indexOf('\x00') >= 0) { - contract.deploymentData = "" - } else { - contract.deploymentData = contract.deploymentData.toString() - } - if (contract.deploymentHash.toString().indexOf('\x00') >= 0) { - contract.deploymentHash = "" - } else { - contract.deploymentHash = contract.deploymentHash.toString() - } + contract.deploymentData = parsedNodeContract.deploymentData + contract.deploymentHash = parsedNodeContract.deploymentHash let state = ContractState.OutOfFunds switch (ctr.state.__kind) { @@ -205,10 +207,10 @@ async function updateNodeContract(ctr: any, contract: NodeContract, store: Store break } contract.state = state - await store.save(contract) + await store.save(contract) } -async function updateNameContract(ctr: any, contract: NameContract, store: Store) { +async function updateNameContract(ctr: any, contract: Contract, store: Store) { if (ctr.contractType.__kind !== "NameContract") return const parsedNameContract = ctr.contractType.value @@ -228,7 +230,7 @@ async function updateNameContract(ctr: any, contract: NameContract, store: Store break } contract.state = state - await store.save(contract) + await store.save(contract) } export async function nodeContractCanceled( @@ -246,16 +248,32 @@ export async function nodeContractCanceled( if (contractID === BigInt(0)) return - const savedContract = await ctx.store.get(NodeContract, { where: { contractID } }) + const savedContract = await ctx.store.get(Contract, { where: { contractID } }) if (!savedContract) return savedContract.state = ContractState.Deleted - await ctx.store.save(savedContract) + await ctx.store.save(savedContract) + + let savedPublicIPs: PublicIp[] = await ctx.store.find(PublicIp, { where: { contractId: contractID }, relations: { farm: true } }) + Promise.all(savedPublicIPs.map(async (ip) => { + ip.contractId = null + const farm = await ctx.store.get(Farm, { where: { farmID: ip.farm.farmID } }) + if (farm) { + farm.freeIps += 1 + await ctx.store.save(farm) + } + + await ctx.store.save(ip) + })) + + if (savedContract.nodeID) { + const savedNode = await ctx.store.get(Node, { where: { nodeID: savedContract.nodeID } }) + if (!savedNode) return + savedNode.freeMRU += savedContract.usedMRU || BigInt(0) + savedNode.freeSRU += savedContract.usedSRU || BigInt(0) + savedNode.freeHRU += savedContract.usedHRU || BigInt(0) - const savedPublicIP = await ctx.store.get(PublicIp, { where: { contractId: contractID }, relations: { farm: true } }) - if (savedPublicIP) { - savedPublicIP.contractId = BigInt(0) - await ctx.store.save(savedPublicIP) + await ctx.store.save(savedNode) } } @@ -274,13 +292,13 @@ export async function nameContractCanceled( if (contractID === BigInt(0)) return - const savedContract = await ctx.store.get(NameContract, { where: { contractID } }) + const savedContract = await ctx.store.get(Contract, { where: { contractID } }) if (!savedContract) return savedContract.state = ContractState.Deleted - await ctx.store.save(savedContract) + await ctx.store.save(savedContract) } export async function rentContractCanceled( @@ -298,19 +316,22 @@ export async function rentContractCanceled( if (contractID === BigInt(0)) return - const savedContract = await ctx.store.get(RentContract, { where: { contractID } }) + const savedContract = await ctx.store.get(Contract, { where: { contractID } }) if (!savedContract) return savedContract.state = ContractState.Deleted - await ctx.store.save(savedContract) + await ctx.store.save(savedContract) // Update node dedicated status, if the node has an extra fee set, it means it's dedicated - const savedNode = await ctx.store.get(Node, { where: { nodeID: savedContract.nodeID }, relations: { location: true, interfaces: true } }) - if (savedNode) { - savedNode.dedicated = savedNode.extraFee !== null - await ctx.store.save(savedNode) + if (savedContract.nodeID) { + const savedNode = await ctx.store.get(Node, { where: { nodeID: savedContract.nodeID }, relations: { location: true, interfaces: true } }) + if (savedNode) { + savedNode.dedicated = savedNode.extraFee !== null + savedNode.rentedBy = null + await ctx.store.save(savedNode) + } } } @@ -390,33 +411,26 @@ export async function contractUpdateUsedResources( ) { const usedResources = new SmartContractModuleUpdatedUsedResourcesEvent(ctx, item.event).asV49 - const contractUsedResources = new ContractResources() - - const savedContract = await ctx.store.get(NodeContract, { where: { contractID: usedResources.contractId } }) + const savedContract = await ctx.store.get(Contract, { where: { contractID: usedResources.contractId } }) if (!savedContract) return - const savedContractResources = await ctx.store.get(ContractResources, { where: { contract: { contractID: savedContract.contractID } }, relations: { contract: true } }) - if (savedContractResources) { - savedContractResources.cru = usedResources.used.cru - savedContractResources.sru = usedResources.used.sru - savedContractResources.hru = usedResources.used.hru - savedContractResources.mru = usedResources.used.mru - await ctx.store.save(savedContractResources) - - savedContract.resourcesUsed = savedContractResources - await ctx.store.save(savedContract) - } else { - contractUsedResources.id = item.event.id - contractUsedResources.cru = usedResources.used.cru - contractUsedResources.sru = usedResources.used.sru - contractUsedResources.hru = usedResources.used.hru - contractUsedResources.mru = usedResources.used.mru - contractUsedResources.contract = savedContract - await ctx.store.save(contractUsedResources) - - savedContract.resourcesUsed = contractUsedResources - await ctx.store.save(savedContract) + if (savedContract.nodeID) { + const savedNode = await ctx.store.get(Node, { where: { nodeID: savedContract.nodeID } }) + if (!savedNode) return + + savedNode.freeMRU += (savedContract.usedMRU || BigInt(0)) - usedResources.used.mru + savedNode.freeHRU += (savedContract.usedHRU || BigInt(0)) - usedResources.used.hru + savedNode.freeSRU += (savedContract.usedSRU || BigInt(0)) - usedResources.used.sru + + await ctx.store.save(savedNode) } + + savedContract.usedCRU = usedResources.used.cru + savedContract.usedHRU = usedResources.used.hru + savedContract.usedMRU = usedResources.used.mru + savedContract.usedSRU = usedResources.used.sru + + await ctx.store.save(savedContract) } export async function nruConsumptionReportReceived( @@ -449,24 +463,10 @@ export async function contractGracePeriodStarted( contractID = contractGracePeriodStartedEvent.asV105.contractId } - const savedNodeContract = await ctx.store.get(NodeContract, { where: { contractID } }) + const savedNodeContract = await ctx.store.get(Contract, { where: { contractID } }) if (savedNodeContract) { savedNodeContract.state = ContractState.GracePeriod - await ctx.store.save(savedNodeContract) - return - } - - const savedRentContract = await ctx.store.get(RentContract, { where: { contractID } }) - if (savedRentContract) { - savedRentContract.state = ContractState.GracePeriod - await ctx.store.save(savedRentContract) - return - } - - const savedNameContract = await ctx.store.get(NameContract, { where: { contractID } }) - if (savedNameContract) { - savedNameContract.state = ContractState.GracePeriod - await ctx.store.save(savedNameContract) + await ctx.store.save(savedNodeContract) return } } @@ -485,24 +485,10 @@ export async function contractGracePeriodEnded( contractID = contractGracePeriodEnded.asV105.contractId } - const savedNodeContract = await ctx.store.get(NodeContract, { where: { contractID } }) - if (savedNodeContract) { - savedNodeContract.state = ContractState.Created - await ctx.store.save(savedNodeContract) - return - } - - const savedRentContract = await ctx.store.get(RentContract, { where: { contractID } }) - if (savedRentContract) { - savedRentContract.state = ContractState.Created - await ctx.store.save(savedRentContract) - return - } - - const savedNameContract = await ctx.store.get(NameContract, { where: { contractID } }) - if (savedNameContract) { - savedNameContract.state = ContractState.Created - await ctx.store.save(savedNameContract) + const contract = await ctx.store.get(Contract, { where: { contractID } }) + if (contract) { + contract.state = ContractState.Created + await ctx.store.save(contract) return } } diff --git a/src/mappings/farms.ts b/src/mappings/farms.ts index 542d4a1..f54e3d6 100644 --- a/src/mappings/farms.ts +++ b/src/mappings/farms.ts @@ -46,6 +46,8 @@ export async function farmStored( newFarm.pricingPolicyID = farmStoredEventParsed.pricingPolicyId newFarm.dedicatedFarm = false newFarm.certification = FarmCertification.NotCertified + newFarm.totalIps = 0 + newFarm.freeIps = 0 newFarm.publicIPs = [] @@ -70,10 +72,14 @@ export async function farmStored( newIP.farm = newFarm newFarm.publicIPs?.push(newIP) - + newFarm.totalIps += 1 return ctx.store.save(newIP) }) + + await Promise.all(ipPromises) + + newFarm.freeIps = newFarm.totalIps await ctx.store.save(newFarm) } @@ -118,18 +124,25 @@ export async function farmUpdated( savedFarm.twinID = farmUpdatedEventParsed.twinId savedFarm.pricingPolicyID = farmUpdatedEventParsed.pricingPolicyId savedFarm.certification = certification - + savedFarm.totalIps = 0 + savedFarm.freeIps = 0 let eventPublicIPs = farmUpdatedEventParsed.publicIps - await farmUpdatedEventParsed.publicIps.forEach(async ip => { + await Promise.all(farmUpdatedEventParsed.publicIps.map(async ip => { if (ip.ip.toString().indexOf('\x00') >= 0) { return } const savedIP = await ctx.store.get(PublicIp, { where: { ip: ip.ip.toString() }, relations: { farm: true } }) // ip is already there in storage, don't save it again + + savedFarm.totalIps += 1 if (savedIP) { savedIP.ip = ip.ip.toString() savedIP.gateway = ip.gateway.toString() + if (savedIP.contractId === BigInt(0)) { + savedFarm.freeIps += 1 + } + await ctx.store.save(savedIP) } else { const newIP = new PublicIp() @@ -138,16 +151,14 @@ export async function farmUpdated( newIP.gateway = ip.gateway.toString() newIP.contractId = ip.contractId newIP.farm = savedFarm - await ctx.store.save(newIP) if (!savedFarm.publicIPs) { savedFarm.publicIPs = [] } savedFarm.publicIPs.push(newIP) + savedFarm.freeIps += 1 } - }) - - await ctx.store.save(savedFarm) + })) const publicIPsOfFarm = await ctx.store.find(PublicIp, { where: { farm: { id: savedFarm.id } }, relations: { farm: true } }) publicIPsOfFarm.forEach(async ip => { @@ -160,8 +171,9 @@ export async function farmUpdated( let farm = item.event.args as Farm if (farm.dedicatedFarm) { savedFarm.dedicatedFarm = farm.dedicatedFarm - await ctx.store.save(savedFarm) } + + await ctx.store.save(savedFarm) } export async function farmDeleted( diff --git a/src/mappings/nodes.ts b/src/mappings/nodes.ts index df698de..63df8c2 100644 --- a/src/mappings/nodes.ts +++ b/src/mappings/nodes.ts @@ -1,8 +1,8 @@ -import { Node, Location, PublicConfig, NodeCertification, Interfaces, UptimeEvent, NodeResourcesTotal, NodePower, PowerState, Power } from "../model"; -import { - TfgridModuleNodeCertificationSetEvent, TfgridModuleNodeDeletedEvent, - TfgridModuleNodePublicConfigStoredEvent, TfgridModuleNodeStoredEvent, - TfgridModuleNodeUpdatedEvent, TfgridModuleNodeUptimeReportedEvent, +import { Node, Location, PublicConfig, NodeCertification, Interfaces, UptimeEvent, NodePower, PowerState, Power } from "../model"; +import { + TfgridModuleNodeCertificationSetEvent, TfgridModuleNodeDeletedEvent, + TfgridModuleNodePublicConfigStoredEvent, TfgridModuleNodeStoredEvent, + TfgridModuleNodeUpdatedEvent, TfgridModuleNodeUptimeReportedEvent, TfgridModulePowerStateChangedEvent, TfgridModulePowerTargetChangedEvent, SmartContractModuleNodeExtraFeeSetEvent } from "../types/events"; @@ -15,6 +15,9 @@ import { Ctx } from '../processor' import assert from "assert"; import { allowedNodeEnvironmentFlags } from "process"; +const ZOSUsedSRU = 107374182400; +const ZOSMinUsedMemory = 2147483648; + export async function nodeStored( ctx: Ctx, item: EventItem<'TfgridModule.NodeStored', { event: { args: true } }>, @@ -77,8 +80,6 @@ export async function nodeStored( newNode.location = newLocation - await ctx.store.save(newNode) - const pubConfig = getNodePublicConfig(node) const newPubConfig = new PublicConfig() newPubConfig.id = item.event.id @@ -126,16 +127,17 @@ export async function nodeStored( newNode.connectionPrice = nodeEvent.connectionPrice } - await ctx.store.save(newNode) + newNode.totalCRU = nodeEvent.resources.cru + newNode.totalHRU = nodeEvent.resources.hru + newNode.totalSRU = nodeEvent.resources.sru + newNode.totalMRU = nodeEvent.resources.mru - const resourcesTotal = new NodeResourcesTotal() - resourcesTotal.node = newNode - resourcesTotal.id = item.event.id - resourcesTotal.sru = nodeEvent.resources.sru - resourcesTotal.hru = nodeEvent.resources.hru - resourcesTotal.mru = nodeEvent.resources.mru - resourcesTotal.cru = nodeEvent.resources.cru - await ctx.store.save(resourcesTotal) + newNode.freeHRU = nodeEvent.resources.hru + newNode.freeSRU = nodeEvent.resources.sru - BigInt(ZOSUsedSRU) + let mruPercentage = nodeEvent.resources.mru / BigInt(10) + newNode.freeMRU = nodeEvent.resources.mru - (mruPercentage > BigInt(ZOSMinUsedMemory) ? mruPercentage : BigInt(ZOSMinUsedMemory)) + + await ctx.store.save(newNode) newNode.interfaces = [] @@ -189,15 +191,26 @@ export async function nodeUpdated( savedNode.updatedAt = timestamp savedNode.farmingPolicyId = nodeEvent.farmingPolicyId - // Recalculate total / free resoures when a node get's updated - let resourcesTotal = await ctx.store.get(NodeResourcesTotal, { where: { node: { nodeID: savedNode.nodeID } }, relations: { node: true } }) - if (resourcesTotal) { - resourcesTotal.sru = nodeEvent.resources.sru - resourcesTotal.hru = nodeEvent.resources.hru - resourcesTotal.mru = nodeEvent.resources.mru - resourcesTotal.cru = nodeEvent.resources.cru - await ctx.store.save(resourcesTotal) - } + // calculate free resources difference + let prevMRUPercentage = savedNode.totalMRU / BigInt(10) + let prevZOSUsedMRU = savedNode.totalMRU - (prevMRUPercentage > BigInt(ZOSMinUsedMemory) ? prevMRUPercentage : BigInt(ZOSMinUsedMemory)) + let curMRUPercentage = nodeEvent.resources.mru / BigInt(10) + let curZOSUsedMRU = nodeEvent.resources.mru - (curMRUPercentage > BigInt(ZOSMinUsedMemory) ? curMRUPercentage : BigInt(ZOSMinUsedMemory)) + + let contractsUsedMRU = savedNode.totalMRU - savedNode.freeMRU - prevZOSUsedMRU + let newFreeMRU = nodeEvent.resources.mru - contractsUsedMRU - curZOSUsedMRU + + let hruDiff = nodeEvent.resources.hru - savedNode.totalHRU + let sruDiff = nodeEvent.resources.sru - savedNode.totalSRU + + savedNode.freeMRU = newFreeMRU + savedNode.freeHRU += hruDiff + savedNode.freeSRU += sruDiff + + savedNode.totalCRU = nodeEvent.resources.cru + savedNode.totalMRU = nodeEvent.resources.mru + savedNode.totalSRU = nodeEvent.resources.sru + savedNode.totalHRU = nodeEvent.resources.hru if (node.isV9) { nodeEvent = node.asV9 @@ -354,12 +367,6 @@ export async function nodeDeleted( const savedNode = await ctx.store.get(Node, { where: { nodeID: nodeID }, relations: { location: true, interfaces: true } }) if (savedNode) { - const resourcesTotal = await ctx.store.find(NodeResourcesTotal, { where: { node: { nodeID: savedNode.nodeID } }, relations: { node: true } }) - if (resourcesTotal) { - const p = resourcesTotal.map(r => ctx.store.remove(r)) - await Promise.all(p) - } - const pubConfig = await ctx.store.get(PublicConfig, { where: { node: { nodeID: savedNode.nodeID } }, relations: { node: true } }) if (pubConfig) { await ctx.store.remove(pubConfig) diff --git a/src/model/generated/_contractType.ts b/src/model/generated/_contractType.ts new file mode 100644 index 0000000..fc1d1b1 --- /dev/null +++ b/src/model/generated/_contractType.ts @@ -0,0 +1,5 @@ +export enum ContractType { + Node = "Node", + Name = "Name", + Rent = "Rent", +} diff --git a/src/model/generated/contract.model.ts b/src/model/generated/contract.model.ts new file mode 100644 index 0000000..3dc4394 --- /dev/null +++ b/src/model/generated/contract.model.ts @@ -0,0 +1,65 @@ +import {Entity as Entity_, Column as Column_, PrimaryColumn as PrimaryColumn_, Index as Index_} from "typeorm" +import * as marshal from "./marshal" +import {ContractState} from "./_contractState" +import {ContractType} from "./_contractType" + +@Entity_() +export class Contract { + constructor(props?: Partial) { + Object.assign(this, props) + } + + @PrimaryColumn_() + id!: string + + @Column_("int4", {nullable: false}) + gridVersion!: number + + @Index_() + @Column_("numeric", {transformer: marshal.bigintTransformer, nullable: false}) + contractID!: bigint + + @Column_("int4", {nullable: false}) + twinID!: number + + @Column_("int4", {nullable: true}) + nodeID!: number | undefined | null + + @Column_("text", {nullable: true}) + deploymentData!: string | undefined | null + + @Column_("text", {nullable: true}) + deploymentHash!: string | undefined | null + + @Column_("int4", {nullable: true}) + numberOfPublicIPs!: number | undefined | null + + @Index_() + @Column_("varchar", {length: 11, nullable: false}) + state!: ContractState + + @Column_("numeric", {transformer: marshal.bigintTransformer, nullable: false}) + createdAt!: bigint + + @Column_("int4", {nullable: true}) + solutionProviderID!: number | undefined | null + + @Column_("numeric", {transformer: marshal.bigintTransformer, nullable: true}) + usedCRU!: bigint | undefined | null + + @Column_("numeric", {transformer: marshal.bigintTransformer, nullable: true}) + usedMRU!: bigint | undefined | null + + @Column_("numeric", {transformer: marshal.bigintTransformer, nullable: true}) + usedSRU!: bigint | undefined | null + + @Column_("numeric", {transformer: marshal.bigintTransformer, nullable: true}) + usedHRU!: bigint | undefined | null + + @Column_("text", {nullable: true}) + name!: string | undefined | null + + @Index_() + @Column_("varchar", {length: 4, nullable: false}) + type!: ContractType +} diff --git a/src/model/generated/contractBillReport.model.ts b/src/model/generated/contractBillReport.model.ts index 949b8b3..4cfe8b6 100644 --- a/src/model/generated/contractBillReport.model.ts +++ b/src/model/generated/contractBillReport.model.ts @@ -1,4 +1,4 @@ -import {Entity as Entity_, Column as Column_, PrimaryColumn as PrimaryColumn_} from "typeorm" +import {Entity as Entity_, Column as Column_, PrimaryColumn as PrimaryColumn_, Index as Index_} from "typeorm" import * as marshal from "./marshal" import {DiscountLevel} from "./_discountLevel" @@ -11,6 +11,7 @@ export class ContractBillReport { @PrimaryColumn_() id!: string + @Index_() @Column_("numeric", {transformer: marshal.bigintTransformer, nullable: false}) contractID!: bigint diff --git a/src/model/generated/contractResources.model.ts b/src/model/generated/contractResources.model.ts deleted file mode 100644 index a342c3a..0000000 --- a/src/model/generated/contractResources.model.ts +++ /dev/null @@ -1,29 +0,0 @@ -import {Entity as Entity_, Column as Column_, PrimaryColumn as PrimaryColumn_, ManyToOne as ManyToOne_, Index as Index_} from "typeorm" -import * as marshal from "./marshal" -import {NodeContract} from "./nodeContract.model" - -@Entity_() -export class ContractResources { - constructor(props?: Partial) { - Object.assign(this, props) - } - - @PrimaryColumn_() - id!: string - - @Index_() - @ManyToOne_(() => NodeContract, {nullable: true}) - contract!: NodeContract - - @Column_("numeric", {transformer: marshal.bigintTransformer, nullable: false}) - hru!: bigint - - @Column_("numeric", {transformer: marshal.bigintTransformer, nullable: false}) - sru!: bigint - - @Column_("numeric", {transformer: marshal.bigintTransformer, nullable: false}) - cru!: bigint - - @Column_("numeric", {transformer: marshal.bigintTransformer, nullable: false}) - mru!: bigint -} diff --git a/src/model/generated/farm.model.ts b/src/model/generated/farm.model.ts index 1711898..f2fabd9 100644 --- a/src/model/generated/farm.model.ts +++ b/src/model/generated/farm.model.ts @@ -1,4 +1,4 @@ -import {Entity as Entity_, Column as Column_, PrimaryColumn as PrimaryColumn_, OneToMany as OneToMany_} from "typeorm" +import {Entity as Entity_, Column as Column_, PrimaryColumn as PrimaryColumn_, Index as Index_, OneToMany as OneToMany_} from "typeorm" import {FarmCertification} from "./_farmCertification" import {PublicIp} from "./publicIp.model" @@ -14,6 +14,7 @@ export class Farm { @Column_("int4", {nullable: false}) gridVersion!: number + @Index_() @Column_("int4", {nullable: false}) farmID!: number @@ -32,6 +33,12 @@ export class Farm { @OneToMany_(() => PublicIp, e => e.farm) publicIPs!: PublicIp[] + @Column_("int4", {nullable: false}) + totalIps!: number + + @Column_("int4", {nullable: false}) + freeIps!: number + @Column_("text", {nullable: true}) stellarAddress!: string | undefined | null diff --git a/src/model/generated/index.ts b/src/model/generated/index.ts index 64062eb..2b4a11a 100644 --- a/src/model/generated/index.ts +++ b/src/model/generated/index.ts @@ -9,7 +9,6 @@ export * from "./_nodeCertification" export * from "./_nodePower" export * from "./_powerState" export * from "./_power" -export * from "./nodeResourcesTotal.model" export * from "./interfaces.model" export * from "./publicConfig.model" export * from "./location.model" @@ -17,11 +16,9 @@ export * from "./pricingPolicy.model" export * from "./_policy" export * from "./country.model" export * from "./city.model" -export * from "./nodeContract.model" +export * from "./contract.model" export * from "./_contractState" -export * from "./contractResources.model" -export * from "./nameContract.model" -export * from "./rentContract.model" +export * from "./_contractType" export * from "./solutionProvider.model" export * from "./_provider" export * from "./nruConsumption.model" diff --git a/src/model/generated/nameContract.model.ts b/src/model/generated/nameContract.model.ts deleted file mode 100644 index b882d5d..0000000 --- a/src/model/generated/nameContract.model.ts +++ /dev/null @@ -1,34 +0,0 @@ -import {Entity as Entity_, Column as Column_, PrimaryColumn as PrimaryColumn_} from "typeorm" -import * as marshal from "./marshal" -import {ContractState} from "./_contractState" - -@Entity_() -export class NameContract { - constructor(props?: Partial) { - Object.assign(this, props) - } - - @PrimaryColumn_() - id!: string - - @Column_("int4", {nullable: false}) - gridVersion!: number - - @Column_("numeric", {transformer: marshal.bigintTransformer, nullable: false}) - contractID!: bigint - - @Column_("int4", {nullable: false}) - twinID!: number - - @Column_("text", {nullable: false}) - name!: string - - @Column_("varchar", {length: 11, nullable: false}) - state!: ContractState - - @Column_("numeric", {transformer: marshal.bigintTransformer, nullable: false}) - createdAt!: bigint - - @Column_("int4", {nullable: true}) - solutionProviderID!: number | undefined | null -} diff --git a/src/model/generated/node.model.ts b/src/model/generated/node.model.ts index 624f826..07dce0c 100644 --- a/src/model/generated/node.model.ts +++ b/src/model/generated/node.model.ts @@ -1,8 +1,7 @@ -import {Entity as Entity_, Column as Column_, PrimaryColumn as PrimaryColumn_, ManyToOne as ManyToOne_, Index as Index_, OneToMany as OneToMany_} from "typeorm" +import {Entity as Entity_, Column as Column_, PrimaryColumn as PrimaryColumn_, Index as Index_, ManyToOne as ManyToOne_, OneToMany as OneToMany_} from "typeorm" import * as marshal from "./marshal" import {Location} from "./location.model" import {PublicConfig} from "./publicConfig.model" -import {NodeResourcesTotal} from "./nodeResourcesTotal.model" import {Interfaces} from "./interfaces.model" import {NodeCertification} from "./_nodeCertification" import {NodePower} from "./_nodePower" @@ -19,6 +18,7 @@ export class Node { @Column_("int4", {nullable: false}) gridVersion!: number + @Index_() @Column_("int4", {nullable: false}) nodeID!: number @@ -39,7 +39,6 @@ export class Node { city!: string | undefined | null - @Column_("numeric", {transformer: marshal.bigintTransformer, nullable: true}) uptime!: bigint | undefined | null @@ -81,4 +80,31 @@ export class Node { @Column_("numeric", {transformer: marshal.bigintTransformer, nullable: true}) extraFee!: bigint | undefined | null + + @Column_("numeric", {transformer: marshal.bigintTransformer, nullable: false}) + totalCRU!: bigint + + @Column_("numeric", {transformer: marshal.bigintTransformer, nullable: false}) + totalHRU!: bigint + + @Column_("numeric", {transformer: marshal.bigintTransformer, nullable: false}) + totalSRU!: bigint + + @Column_("numeric", {transformer: marshal.bigintTransformer, nullable: false}) + totalMRU!: bigint + + @Index_() + @Column_("numeric", {transformer: marshal.bigintTransformer, nullable: false}) + freeMRU!: bigint + + @Index_() + @Column_("numeric", {transformer: marshal.bigintTransformer, nullable: false}) + freeHRU!: bigint + + @Index_() + @Column_("numeric", {transformer: marshal.bigintTransformer, nullable: false}) + freeSRU!: bigint + + @Column_("int4", {nullable: true}) + rentedBy!: number | undefined | null } diff --git a/src/model/generated/nodeContract.model.ts b/src/model/generated/nodeContract.model.ts deleted file mode 100644 index 42d66e8..0000000 --- a/src/model/generated/nodeContract.model.ts +++ /dev/null @@ -1,48 +0,0 @@ -import {Entity as Entity_, Column as Column_, PrimaryColumn as PrimaryColumn_, ManyToOne as ManyToOne_, Index as Index_} from "typeorm" -import * as marshal from "./marshal" -import {ContractState} from "./_contractState" -import {ContractResources} from "./contractResources.model" - -@Entity_() -export class NodeContract { - constructor(props?: Partial) { - Object.assign(this, props) - } - - @PrimaryColumn_() - id!: string - - @Column_("int4", {nullable: false}) - gridVersion!: number - - @Column_("numeric", {transformer: marshal.bigintTransformer, nullable: false}) - contractID!: bigint - - @Column_("int4", {nullable: false}) - twinID!: number - - @Column_("int4", {nullable: false}) - nodeID!: number - - @Column_("text", {nullable: false}) - deploymentData!: string - - @Column_("text", {nullable: false}) - deploymentHash!: string - - @Column_("int4", {nullable: false}) - numberOfPublicIPs!: number - - @Column_("varchar", {length: 11, nullable: false}) - state!: ContractState - - @Index_() - @ManyToOne_(() => ContractResources, {nullable: true}) - resourcesUsed!: ContractResources | undefined | null - - @Column_("numeric", {transformer: marshal.bigintTransformer, nullable: false}) - createdAt!: bigint - - @Column_("int4", {nullable: true}) - solutionProviderID!: number | undefined | null -} diff --git a/src/model/generated/nodeResourcesTotal.model.ts b/src/model/generated/nodeResourcesTotal.model.ts deleted file mode 100644 index 62113bb..0000000 --- a/src/model/generated/nodeResourcesTotal.model.ts +++ /dev/null @@ -1,30 +0,0 @@ -import {Entity as Entity_, Column as Column_, PrimaryColumn as PrimaryColumn_, OneToOne as OneToOne_, Index as Index_, JoinColumn as JoinColumn_} from "typeorm" -import * as marshal from "./marshal" -import {Node} from "./node.model" - -@Entity_() -export class NodeResourcesTotal { - constructor(props?: Partial) { - Object.assign(this, props) - } - - @PrimaryColumn_() - id!: string - - @Index_({unique: true}) - @OneToOne_(() => Node, {nullable: false}) - @JoinColumn_() - node!: Node - - @Column_("numeric", {transformer: marshal.bigintTransformer, nullable: false}) - hru!: bigint - - @Column_("numeric", {transformer: marshal.bigintTransformer, nullable: false}) - sru!: bigint - - @Column_("numeric", {transformer: marshal.bigintTransformer, nullable: false}) - cru!: bigint - - @Column_("numeric", {transformer: marshal.bigintTransformer, nullable: false}) - mru!: bigint -} diff --git a/src/model/generated/publicIp.model.ts b/src/model/generated/publicIp.model.ts index 448bde6..d012f72 100644 --- a/src/model/generated/publicIp.model.ts +++ b/src/model/generated/publicIp.model.ts @@ -21,6 +21,7 @@ export class PublicIp { @Column_("text", {nullable: false}) ip!: string - @Column_("numeric", {transformer: marshal.bigintTransformer, nullable: false}) - contractId!: bigint + @Index_() + @Column_("numeric", {transformer: marshal.bigintTransformer, nullable: true}) + contractId!: bigint | undefined | null } diff --git a/src/model/generated/rentContract.model.ts b/src/model/generated/rentContract.model.ts deleted file mode 100644 index 8a353c6..0000000 --- a/src/model/generated/rentContract.model.ts +++ /dev/null @@ -1,34 +0,0 @@ -import {Entity as Entity_, Column as Column_, PrimaryColumn as PrimaryColumn_} from "typeorm" -import * as marshal from "./marshal" -import {ContractState} from "./_contractState" - -@Entity_() -export class RentContract { - constructor(props?: Partial) { - Object.assign(this, props) - } - - @PrimaryColumn_() - id!: string - - @Column_("int4", {nullable: false}) - gridVersion!: number - - @Column_("numeric", {transformer: marshal.bigintTransformer, nullable: false}) - contractID!: bigint - - @Column_("int4", {nullable: false}) - twinID!: number - - @Column_("int4", {nullable: false}) - nodeID!: number - - @Column_("varchar", {length: 11, nullable: false}) - state!: ContractState - - @Column_("numeric", {transformer: marshal.bigintTransformer, nullable: false}) - createdAt!: bigint - - @Column_("int4", {nullable: true}) - solutionProviderID!: number | undefined | null -} diff --git a/src/model/generated/twin.model.ts b/src/model/generated/twin.model.ts index 637912a..5f35f9d 100644 --- a/src/model/generated/twin.model.ts +++ b/src/model/generated/twin.model.ts @@ -1,4 +1,4 @@ -import {Entity as Entity_, Column as Column_, PrimaryColumn as PrimaryColumn_} from "typeorm" +import {Entity as Entity_, Column as Column_, PrimaryColumn as PrimaryColumn_, Index as Index_} from "typeorm" @Entity_() export class Twin { @@ -12,6 +12,7 @@ export class Twin { @Column_("int4", {nullable: false}) gridVersion!: number + @Index_() @Column_("int4", {nullable: false}) twinID!: number