diff --git a/db/migrations/1700653099748-Data.js b/db/migrations/1700653099748-Data.js deleted file mode 100644 index f7e7cbe72348c12feca88205536099d23284944c..0000000000000000000000000000000000000000 --- a/db/migrations/1700653099748-Data.js +++ /dev/null @@ -1,61 +0,0 @@ -module.exports = class Data1700653099748 { - name = 'Data1700653099748' - - async up(db) { - await db.query(`CREATE TABLE "transfer" ("id" character varying NOT NULL, "block_number" integer NOT NULL, "timestamp" TIMESTAMP WITH TIME ZONE NOT NULL, "amount" numeric NOT NULL, "from_id" character varying, "to_id" character varying, CONSTRAINT "PK_fd9ddbdd49a17afcbe014401295" PRIMARY KEY ("id"))`) - await db.query(`CREATE INDEX "IDX_d6624eacc30144ea97915fe846" ON "transfer" ("block_number") `) - await db.query(`CREATE INDEX "IDX_70ff8b624c3118ac3a4862d22c" ON "transfer" ("timestamp") `) - await db.query(`CREATE INDEX "IDX_76bdfed1a7eb27c6d8ecbb7349" ON "transfer" ("from_id") `) - await db.query(`CREATE INDEX "IDX_0751309c66e97eac9ef1149362" ON "transfer" ("to_id") `) - await db.query(`CREATE INDEX "IDX_f4007436c1b546ede08a4fd7ab" ON "transfer" ("amount") `) - await db.query(`CREATE TABLE "account" ("id" character varying NOT NULL, CONSTRAINT "PK_54115ee388cdb6d86bb4bf5b2ea" PRIMARY KEY ("id"))`) - await db.query(`CREATE TABLE "cert" ("id" character varying NOT NULL, "block_number" integer NOT NULL, "issuer_id" character varying, "receiver_id" character varying, CONSTRAINT "PK_6a0ce80cc860598b4f16c00998c" PRIMARY KEY ("id"))`) - await db.query(`CREATE INDEX "IDX_a0541d4207bde7970641de0641" ON "cert" ("block_number") `) - await db.query(`CREATE INDEX "IDX_70592e488b2e75cd8a2fa79826" ON "cert" ("issuer_id") `) - await db.query(`CREATE INDEX "IDX_262e29ab91c8ebc727cc518f2f" ON "cert" ("receiver_id") `) - await db.query(`CREATE TABLE "identity" ("id" character varying NOT NULL, "index" integer NOT NULL, "name" text, "account_id" character varying, CONSTRAINT "REL_bafa9e6c71c3f69cef6602a809" UNIQUE ("account_id"), CONSTRAINT "PK_ff16a44186b286d5e626178f726" PRIMARY KEY ("id"))`) - await db.query(`CREATE UNIQUE INDEX "IDX_6f883c7979ea8dff46327f67cc" ON "identity" ("index") `) - await db.query(`CREATE UNIQUE INDEX "IDX_bafa9e6c71c3f69cef6602a809" ON "identity" ("account_id") `) - await db.query(`CREATE INDEX "IDX_883ba5be237fba47f2a2f39145" ON "identity" ("name") `) - await db.query(`CREATE TABLE "call" ("id" character varying NOT NULL, "name" text NOT NULL, "block_number" integer NOT NULL, CONSTRAINT "PK_2098af0169792a34f9cfdd39c47" PRIMARY KEY ("id"))`) - await db.query(`CREATE INDEX "IDX_8b212022b7428232091e2f8aa5" ON "call" ("name") `) - await db.query(`CREATE INDEX "IDX_8d62e222f606c249fb4046ad63" ON "call" ("block_number") `) - await db.query(`CREATE TABLE "block" ("id" character varying NOT NULL, "number" integer NOT NULL, "timestamp" TIMESTAMP WITH TIME ZONE NOT NULL, CONSTRAINT "PK_d0925763efb591c2e2ffb267572" PRIMARY KEY ("id"))`) - await db.query(`CREATE UNIQUE INDEX "IDX_38414873c187a3e0c7943bc4c7" ON "block" ("number") `) - await db.query(`CREATE INDEX "IDX_5c67cbcf4960c1a39e5fe25e87" ON "block" ("timestamp") `) - await db.query(`ALTER TABLE "transfer" ADD CONSTRAINT "FK_76bdfed1a7eb27c6d8ecbb73496" FOREIGN KEY ("from_id") REFERENCES "account"("id") ON DELETE NO ACTION ON UPDATE NO ACTION`) - await db.query(`ALTER TABLE "transfer" ADD CONSTRAINT "FK_0751309c66e97eac9ef11493623" FOREIGN KEY ("to_id") REFERENCES "account"("id") ON DELETE NO ACTION ON UPDATE NO ACTION`) - await db.query(`ALTER TABLE "cert" ADD CONSTRAINT "FK_70592e488b2e75cd8a2fa798261" FOREIGN KEY ("issuer_id") REFERENCES "identity"("id") ON DELETE NO ACTION ON UPDATE NO ACTION`) - await db.query(`ALTER TABLE "cert" ADD CONSTRAINT "FK_262e29ab91c8ebc727cc518f2fb" FOREIGN KEY ("receiver_id") REFERENCES "identity"("id") ON DELETE NO ACTION ON UPDATE NO ACTION`) - await db.query(`ALTER TABLE "identity" ADD CONSTRAINT "FK_bafa9e6c71c3f69cef6602a8095" FOREIGN KEY ("account_id") REFERENCES "account"("id") ON DELETE NO ACTION ON UPDATE NO ACTION`) - } - - async down(db) { - await db.query(`DROP TABLE "transfer"`) - await db.query(`DROP INDEX "public"."IDX_d6624eacc30144ea97915fe846"`) - await db.query(`DROP INDEX "public"."IDX_70ff8b624c3118ac3a4862d22c"`) - await db.query(`DROP INDEX "public"."IDX_76bdfed1a7eb27c6d8ecbb7349"`) - await db.query(`DROP INDEX "public"."IDX_0751309c66e97eac9ef1149362"`) - await db.query(`DROP INDEX "public"."IDX_f4007436c1b546ede08a4fd7ab"`) - await db.query(`DROP TABLE "account"`) - await db.query(`DROP TABLE "cert"`) - await db.query(`DROP INDEX "public"."IDX_a0541d4207bde7970641de0641"`) - await db.query(`DROP INDEX "public"."IDX_70592e488b2e75cd8a2fa79826"`) - await db.query(`DROP INDEX "public"."IDX_262e29ab91c8ebc727cc518f2f"`) - await db.query(`DROP TABLE "identity"`) - await db.query(`DROP INDEX "public"."IDX_6f883c7979ea8dff46327f67cc"`) - await db.query(`DROP INDEX "public"."IDX_bafa9e6c71c3f69cef6602a809"`) - await db.query(`DROP INDEX "public"."IDX_883ba5be237fba47f2a2f39145"`) - await db.query(`DROP TABLE "call"`) - await db.query(`DROP INDEX "public"."IDX_8b212022b7428232091e2f8aa5"`) - await db.query(`DROP INDEX "public"."IDX_8d62e222f606c249fb4046ad63"`) - await db.query(`DROP TABLE "block"`) - await db.query(`DROP INDEX "public"."IDX_38414873c187a3e0c7943bc4c7"`) - await db.query(`DROP INDEX "public"."IDX_5c67cbcf4960c1a39e5fe25e87"`) - await db.query(`ALTER TABLE "transfer" DROP CONSTRAINT "FK_76bdfed1a7eb27c6d8ecbb73496"`) - await db.query(`ALTER TABLE "transfer" DROP CONSTRAINT "FK_0751309c66e97eac9ef11493623"`) - await db.query(`ALTER TABLE "cert" DROP CONSTRAINT "FK_70592e488b2e75cd8a2fa798261"`) - await db.query(`ALTER TABLE "cert" DROP CONSTRAINT "FK_262e29ab91c8ebc727cc518f2fb"`) - await db.query(`ALTER TABLE "identity" DROP CONSTRAINT "FK_bafa9e6c71c3f69cef6602a8095"`) - } -} diff --git a/db/migrations/1700673585666-Data.js b/db/migrations/1700673585666-Data.js new file mode 100644 index 0000000000000000000000000000000000000000..ccce1786e9ae054d75e60747b612d967b403c454 --- /dev/null +++ b/db/migrations/1700673585666-Data.js @@ -0,0 +1,149 @@ +module.exports = class Data1700673585666 { + name = 'Data1700673585666' + + async up(db) { + await db.query(`CREATE TABLE "event" ("id" character varying NOT NULL, "index" integer NOT NULL, "phase" text NOT NULL, "pallet" text NOT NULL, "name" text NOT NULL, "args" jsonb, "args_str" text array, "block_id" character varying, "extrinsic_id" character varying, "call_id" character varying, CONSTRAINT "PK_30c2f3bbaf6d34a55f8ae6e4614" PRIMARY KEY ("id"))`) + await db.query(`CREATE INDEX "IDX_2b0d35d675c4f99751855c4502" ON "event" ("block_id") `) + await db.query(`CREATE INDEX "IDX_129efedcb305c80256db2d57a5" ON "event" ("extrinsic_id") `) + await db.query(`CREATE INDEX "IDX_83cf1bd59aa4521ed882fa5145" ON "event" ("call_id") `) + await db.query(`CREATE INDEX "IDX_7723d04c5a2f56c4373b6a4048" ON "event" ("pallet") `) + await db.query(`CREATE INDEX "IDX_b535fbe8ec6d832dde22065ebd" ON "event" ("name") `) + await db.query(`CREATE INDEX "IDX_0a00d817e614a91cda40d734cf" ON "event" ("id", "pallet", "name") `) + await db.query(`CREATE TABLE "call" ("id" character varying NOT NULL, "address" integer array NOT NULL, "success" boolean NOT NULL, "error" jsonb, "pallet" text NOT NULL, "name" text NOT NULL, "args" jsonb, "args_str" text array, "block_id" character varying, "extrinsic_id" character varying, "parent_id" character varying, CONSTRAINT "PK_2098af0169792a34f9cfdd39c47" PRIMARY KEY ("id"))`) + await db.query(`CREATE INDEX "IDX_bd3f11fd4110d60ac8b96cd62f" ON "call" ("block_id") `) + await db.query(`CREATE INDEX "IDX_dde30e4f2c6a80f9236bfdf259" ON "call" ("extrinsic_id") `) + await db.query(`CREATE INDEX "IDX_11c1e76d5be8f04c472c4a05b9" ON "call" ("parent_id") `) + await db.query(`CREATE INDEX "IDX_d3a8c3d00494950ad6dc93297d" ON "call" ("success") `) + await db.query(`CREATE INDEX "IDX_776bccbd3d7b3001c8708cf4e0" ON "call" ("pallet") `) + await db.query(`CREATE INDEX "IDX_8b212022b7428232091e2f8aa5" ON "call" ("name") `) + await db.query(`CREATE INDEX "IDX_f1e953379e1b3c453cd896bcd4" ON "call" ("id", "pallet", "name") `) + await db.query(`CREATE TABLE "extrinsic" ("id" character varying NOT NULL, "index" integer NOT NULL, "version" integer NOT NULL, "signature" jsonb, "tip" numeric, "fee" numeric, "success" boolean, "error" jsonb, "hash" bytea NOT NULL, "block_id" character varying, "call_id" character varying, CONSTRAINT "PK_80d7db0e4b1e83e30336bc76755" PRIMARY KEY ("id"))`) + await db.query(`CREATE INDEX "IDX_a3b99daba1259dab0dd040d4f7" ON "extrinsic" ("block_id") `) + await db.query(`CREATE INDEX "IDX_824d47cc4b2cda726405aa507c" ON "extrinsic" ("call_id") `) + await db.query(`CREATE INDEX "IDX_21e5db7671dfa1b00dbe6dbbd6" ON "extrinsic" ("success") `) + await db.query(`CREATE INDEX "IDX_1f45de0713a55049009e8e8127" ON "extrinsic" ("hash") `) + await db.query(`CREATE TABLE "block" ("id" character varying NOT NULL, "height" integer NOT NULL, "hash" bytea NOT NULL, "parent_hash" bytea NOT NULL, "state_root" bytea NOT NULL, "extrinsicsic_root" bytea NOT NULL, "spec_name" text NOT NULL, "spec_version" integer NOT NULL, "impl_name" text NOT NULL, "impl_version" integer NOT NULL, "timestamp" TIMESTAMP WITH TIME ZONE NOT NULL, "validator" bytea, "extrinsics_count" integer NOT NULL, "calls_count" integer NOT NULL, "events_count" integer NOT NULL, CONSTRAINT "PK_d0925763efb591c2e2ffb267572" PRIMARY KEY ("id"))`) + await db.query(`CREATE INDEX "IDX_bce676e2b005104ccb768495db" ON "block" ("height") `) + await db.query(`CREATE INDEX "IDX_f8fba63d7965bfee9f304c487a" ON "block" ("hash") `) + await db.query(`CREATE INDEX "IDX_5b79d140fa8e2c64a7ef223598" ON "block" ("spec_version") `) + await db.query(`CREATE INDEX "IDX_5c67cbcf4960c1a39e5fe25e87" ON "block" ("timestamp") `) + await db.query(`CREATE INDEX "IDX_b7e2f8fe1384a2910825029dcb" ON "block" ("validator") `) + await db.query(`CREATE TABLE "items_counter" ("id" character varying NOT NULL, "type" character varying(10) NOT NULL, "level" character varying(6) NOT NULL, "total" integer NOT NULL, CONSTRAINT "PK_161dcf46142538463f5d7174793" PRIMARY KEY ("id"))`) + await db.query(`CREATE INDEX "IDX_68d2eadecb3eeb540d2004acef" ON "items_counter" ("type") `) + await db.query(`CREATE INDEX "IDX_1d9be1d79f197d42dd163f86c8" ON "items_counter" ("level") `) + await db.query(`CREATE INDEX "IDX_e03dd1c60ac7622914f72ac2f1" ON "items_counter" ("total") `) + await db.query(`CREATE TABLE "transfer" ("id" character varying NOT NULL, "block_number" integer NOT NULL, "timestamp" TIMESTAMP WITH TIME ZONE NOT NULL, "amount" numeric NOT NULL, "from_id" character varying, "to_id" character varying, CONSTRAINT "PK_fd9ddbdd49a17afcbe014401295" PRIMARY KEY ("id"))`) + await db.query(`CREATE INDEX "IDX_d6624eacc30144ea97915fe846" ON "transfer" ("block_number") `) + await db.query(`CREATE INDEX "IDX_70ff8b624c3118ac3a4862d22c" ON "transfer" ("timestamp") `) + await db.query(`CREATE INDEX "IDX_76bdfed1a7eb27c6d8ecbb7349" ON "transfer" ("from_id") `) + await db.query(`CREATE INDEX "IDX_0751309c66e97eac9ef1149362" ON "transfer" ("to_id") `) + await db.query(`CREATE INDEX "IDX_f4007436c1b546ede08a4fd7ab" ON "transfer" ("amount") `) + await db.query(`CREATE TABLE "account" ("id" character varying NOT NULL, CONSTRAINT "PK_54115ee388cdb6d86bb4bf5b2ea" PRIMARY KEY ("id"))`) + await db.query(`CREATE TABLE "cert" ("id" character varying NOT NULL, "issuer_id" character varying, "receiver_id" character varying, CONSTRAINT "PK_6a0ce80cc860598b4f16c00998c" PRIMARY KEY ("id"))`) + await db.query(`CREATE INDEX "IDX_70592e488b2e75cd8a2fa79826" ON "cert" ("issuer_id") `) + await db.query(`CREATE INDEX "IDX_262e29ab91c8ebc727cc518f2f" ON "cert" ("receiver_id") `) + await db.query(`CREATE TABLE "smith_cert" ("id" character varying NOT NULL, "issuer_id" character varying, "receiver_id" character varying, CONSTRAINT "PK_ae2ef36c9f6d40348c86230fd35" PRIMARY KEY ("id"))`) + await db.query(`CREATE INDEX "IDX_ae67cbd087fcea0e1ec2f70cd0" ON "smith_cert" ("issuer_id") `) + await db.query(`CREATE INDEX "IDX_5e414c1d12af16165881a16b63" ON "smith_cert" ("receiver_id") `) + await db.query(`CREATE TABLE "identity" ("id" character varying NOT NULL, "index" integer NOT NULL, "name" text NOT NULL, "account_id" character varying, CONSTRAINT "REL_bafa9e6c71c3f69cef6602a809" UNIQUE ("account_id"), CONSTRAINT "PK_ff16a44186b286d5e626178f726" PRIMARY KEY ("id"))`) + await db.query(`CREATE UNIQUE INDEX "IDX_6f883c7979ea8dff46327f67cc" ON "identity" ("index") `) + await db.query(`CREATE UNIQUE INDEX "IDX_bafa9e6c71c3f69cef6602a809" ON "identity" ("account_id") `) + await db.query(`CREATE INDEX "IDX_883ba5be237fba47f2a2f39145" ON "identity" ("name") `) + await db.query(`CREATE TABLE "membership" ("id" character varying NOT NULL, "expire_on" integer NOT NULL, "identity_id" character varying, CONSTRAINT "REL_efc905420f5f6bfded16c4eb97" UNIQUE ("identity_id"), CONSTRAINT "PK_83c1afebef3059472e7c37e8de8" PRIMARY KEY ("id"))`) + await db.query(`CREATE UNIQUE INDEX "IDX_efc905420f5f6bfded16c4eb97" ON "membership" ("identity_id") `) + await db.query(`CREATE INDEX "IDX_a3f3d8dc21447800f72c0a27b2" ON "membership" ("expire_on") `) + await db.query(`CREATE TABLE "smith_membership" ("id" character varying NOT NULL, "expire_on" integer NOT NULL, "identity_id" character varying, CONSTRAINT "REL_8645aa09a4cfa921be42f1bdc1" UNIQUE ("identity_id"), CONSTRAINT "PK_ec71fc36eab1a7cb666eb11f60a" PRIMARY KEY ("id"))`) + await db.query(`CREATE UNIQUE INDEX "IDX_8645aa09a4cfa921be42f1bdc1" ON "smith_membership" ("identity_id") `) + await db.query(`CREATE INDEX "IDX_fc1bfbc13c6c92d1ef2a868f60" ON "smith_membership" ("expire_on") `) + await db.query(`ALTER TABLE "event" ADD CONSTRAINT "FK_2b0d35d675c4f99751855c45021" FOREIGN KEY ("block_id") REFERENCES "block"("id") ON DELETE NO ACTION ON UPDATE NO ACTION`) + await db.query(`ALTER TABLE "event" ADD CONSTRAINT "FK_129efedcb305c80256db2d57a59" FOREIGN KEY ("extrinsic_id") REFERENCES "extrinsic"("id") ON DELETE NO ACTION ON UPDATE NO ACTION`) + await db.query(`ALTER TABLE "event" ADD CONSTRAINT "FK_83cf1bd59aa4521ed882fa51452" FOREIGN KEY ("call_id") REFERENCES "call"("id") ON DELETE NO ACTION ON UPDATE NO ACTION`) + await db.query(`ALTER TABLE "call" ADD CONSTRAINT "FK_bd3f11fd4110d60ac8b96cd62f3" FOREIGN KEY ("block_id") REFERENCES "block"("id") ON DELETE NO ACTION ON UPDATE NO ACTION`) + await db.query(`ALTER TABLE "call" ADD CONSTRAINT "FK_dde30e4f2c6a80f9236bfdf2590" FOREIGN KEY ("extrinsic_id") REFERENCES "extrinsic"("id") ON DELETE NO ACTION ON UPDATE NO ACTION`) + await db.query(`ALTER TABLE "call" ADD CONSTRAINT "FK_11c1e76d5be8f04c472c4a05b95" FOREIGN KEY ("parent_id") REFERENCES "call"("id") ON DELETE NO ACTION ON UPDATE NO ACTION`) + await db.query(`ALTER TABLE "extrinsic" ADD CONSTRAINT "FK_a3b99daba1259dab0dd040d4f74" FOREIGN KEY ("block_id") REFERENCES "block"("id") ON DELETE NO ACTION ON UPDATE NO ACTION`) + await db.query(`ALTER TABLE "extrinsic" ADD CONSTRAINT "FK_824d47cc4b2cda726405aa507ca" FOREIGN KEY ("call_id") REFERENCES "call"("id") ON DELETE NO ACTION ON UPDATE NO ACTION`) + await db.query(`ALTER TABLE "transfer" ADD CONSTRAINT "FK_76bdfed1a7eb27c6d8ecbb73496" FOREIGN KEY ("from_id") REFERENCES "account"("id") ON DELETE NO ACTION ON UPDATE NO ACTION`) + await db.query(`ALTER TABLE "transfer" ADD CONSTRAINT "FK_0751309c66e97eac9ef11493623" FOREIGN KEY ("to_id") REFERENCES "account"("id") ON DELETE NO ACTION ON UPDATE NO ACTION`) + await db.query(`ALTER TABLE "cert" ADD CONSTRAINT "FK_70592e488b2e75cd8a2fa798261" FOREIGN KEY ("issuer_id") REFERENCES "identity"("id") ON DELETE NO ACTION ON UPDATE NO ACTION`) + await db.query(`ALTER TABLE "cert" ADD CONSTRAINT "FK_262e29ab91c8ebc727cc518f2fb" FOREIGN KEY ("receiver_id") REFERENCES "identity"("id") ON DELETE NO ACTION ON UPDATE NO ACTION`) + await db.query(`ALTER TABLE "smith_cert" ADD CONSTRAINT "FK_ae67cbd087fcea0e1ec2f70cd04" FOREIGN KEY ("issuer_id") REFERENCES "identity"("id") ON DELETE NO ACTION ON UPDATE NO ACTION`) + await db.query(`ALTER TABLE "smith_cert" ADD CONSTRAINT "FK_5e414c1d12af16165881a16b638" FOREIGN KEY ("receiver_id") REFERENCES "identity"("id") ON DELETE NO ACTION ON UPDATE NO ACTION`) + await db.query(`ALTER TABLE "identity" ADD CONSTRAINT "FK_bafa9e6c71c3f69cef6602a8095" FOREIGN KEY ("account_id") REFERENCES "account"("id") ON DELETE NO ACTION ON UPDATE NO ACTION`) + await db.query(`ALTER TABLE "membership" ADD CONSTRAINT "FK_efc905420f5f6bfded16c4eb978" FOREIGN KEY ("identity_id") REFERENCES "identity"("id") ON DELETE NO ACTION ON UPDATE NO ACTION`) + await db.query(`ALTER TABLE "smith_membership" ADD CONSTRAINT "FK_8645aa09a4cfa921be42f1bdc1b" FOREIGN KEY ("identity_id") REFERENCES "identity"("id") ON DELETE NO ACTION ON UPDATE NO ACTION`) + } + + async down(db) { + await db.query(`DROP TABLE "event"`) + await db.query(`DROP INDEX "public"."IDX_2b0d35d675c4f99751855c4502"`) + await db.query(`DROP INDEX "public"."IDX_129efedcb305c80256db2d57a5"`) + await db.query(`DROP INDEX "public"."IDX_83cf1bd59aa4521ed882fa5145"`) + await db.query(`DROP INDEX "public"."IDX_7723d04c5a2f56c4373b6a4048"`) + await db.query(`DROP INDEX "public"."IDX_b535fbe8ec6d832dde22065ebd"`) + await db.query(`DROP INDEX "public"."IDX_0a00d817e614a91cda40d734cf"`) + await db.query(`DROP TABLE "call"`) + await db.query(`DROP INDEX "public"."IDX_bd3f11fd4110d60ac8b96cd62f"`) + await db.query(`DROP INDEX "public"."IDX_dde30e4f2c6a80f9236bfdf259"`) + await db.query(`DROP INDEX "public"."IDX_11c1e76d5be8f04c472c4a05b9"`) + await db.query(`DROP INDEX "public"."IDX_d3a8c3d00494950ad6dc93297d"`) + await db.query(`DROP INDEX "public"."IDX_776bccbd3d7b3001c8708cf4e0"`) + await db.query(`DROP INDEX "public"."IDX_8b212022b7428232091e2f8aa5"`) + await db.query(`DROP INDEX "public"."IDX_f1e953379e1b3c453cd896bcd4"`) + await db.query(`DROP TABLE "extrinsic"`) + await db.query(`DROP INDEX "public"."IDX_a3b99daba1259dab0dd040d4f7"`) + await db.query(`DROP INDEX "public"."IDX_824d47cc4b2cda726405aa507c"`) + await db.query(`DROP INDEX "public"."IDX_21e5db7671dfa1b00dbe6dbbd6"`) + await db.query(`DROP INDEX "public"."IDX_1f45de0713a55049009e8e8127"`) + await db.query(`DROP TABLE "block"`) + await db.query(`DROP INDEX "public"."IDX_bce676e2b005104ccb768495db"`) + await db.query(`DROP INDEX "public"."IDX_f8fba63d7965bfee9f304c487a"`) + await db.query(`DROP INDEX "public"."IDX_5b79d140fa8e2c64a7ef223598"`) + await db.query(`DROP INDEX "public"."IDX_5c67cbcf4960c1a39e5fe25e87"`) + await db.query(`DROP INDEX "public"."IDX_b7e2f8fe1384a2910825029dcb"`) + await db.query(`DROP TABLE "items_counter"`) + await db.query(`DROP INDEX "public"."IDX_68d2eadecb3eeb540d2004acef"`) + await db.query(`DROP INDEX "public"."IDX_1d9be1d79f197d42dd163f86c8"`) + await db.query(`DROP INDEX "public"."IDX_e03dd1c60ac7622914f72ac2f1"`) + await db.query(`DROP TABLE "transfer"`) + await db.query(`DROP INDEX "public"."IDX_d6624eacc30144ea97915fe846"`) + await db.query(`DROP INDEX "public"."IDX_70ff8b624c3118ac3a4862d22c"`) + await db.query(`DROP INDEX "public"."IDX_76bdfed1a7eb27c6d8ecbb7349"`) + await db.query(`DROP INDEX "public"."IDX_0751309c66e97eac9ef1149362"`) + await db.query(`DROP INDEX "public"."IDX_f4007436c1b546ede08a4fd7ab"`) + await db.query(`DROP TABLE "account"`) + await db.query(`DROP TABLE "cert"`) + await db.query(`DROP INDEX "public"."IDX_70592e488b2e75cd8a2fa79826"`) + await db.query(`DROP INDEX "public"."IDX_262e29ab91c8ebc727cc518f2f"`) + await db.query(`DROP TABLE "smith_cert"`) + await db.query(`DROP INDEX "public"."IDX_ae67cbd087fcea0e1ec2f70cd0"`) + await db.query(`DROP INDEX "public"."IDX_5e414c1d12af16165881a16b63"`) + await db.query(`DROP TABLE "identity"`) + await db.query(`DROP INDEX "public"."IDX_6f883c7979ea8dff46327f67cc"`) + await db.query(`DROP INDEX "public"."IDX_bafa9e6c71c3f69cef6602a809"`) + await db.query(`DROP INDEX "public"."IDX_883ba5be237fba47f2a2f39145"`) + await db.query(`DROP TABLE "membership"`) + await db.query(`DROP INDEX "public"."IDX_efc905420f5f6bfded16c4eb97"`) + await db.query(`DROP INDEX "public"."IDX_a3f3d8dc21447800f72c0a27b2"`) + await db.query(`DROP TABLE "smith_membership"`) + await db.query(`DROP INDEX "public"."IDX_8645aa09a4cfa921be42f1bdc1"`) + await db.query(`DROP INDEX "public"."IDX_fc1bfbc13c6c92d1ef2a868f60"`) + await db.query(`ALTER TABLE "event" DROP CONSTRAINT "FK_2b0d35d675c4f99751855c45021"`) + await db.query(`ALTER TABLE "event" DROP CONSTRAINT "FK_129efedcb305c80256db2d57a59"`) + await db.query(`ALTER TABLE "event" DROP CONSTRAINT "FK_83cf1bd59aa4521ed882fa51452"`) + await db.query(`ALTER TABLE "call" DROP CONSTRAINT "FK_bd3f11fd4110d60ac8b96cd62f3"`) + await db.query(`ALTER TABLE "call" DROP CONSTRAINT "FK_dde30e4f2c6a80f9236bfdf2590"`) + await db.query(`ALTER TABLE "call" DROP CONSTRAINT "FK_11c1e76d5be8f04c472c4a05b95"`) + await db.query(`ALTER TABLE "extrinsic" DROP CONSTRAINT "FK_a3b99daba1259dab0dd040d4f74"`) + await db.query(`ALTER TABLE "extrinsic" DROP CONSTRAINT "FK_824d47cc4b2cda726405aa507ca"`) + await db.query(`ALTER TABLE "transfer" DROP CONSTRAINT "FK_76bdfed1a7eb27c6d8ecbb73496"`) + await db.query(`ALTER TABLE "transfer" DROP CONSTRAINT "FK_0751309c66e97eac9ef11493623"`) + await db.query(`ALTER TABLE "cert" DROP CONSTRAINT "FK_70592e488b2e75cd8a2fa798261"`) + await db.query(`ALTER TABLE "cert" DROP CONSTRAINT "FK_262e29ab91c8ebc727cc518f2fb"`) + await db.query(`ALTER TABLE "smith_cert" DROP CONSTRAINT "FK_ae67cbd087fcea0e1ec2f70cd04"`) + await db.query(`ALTER TABLE "smith_cert" DROP CONSTRAINT "FK_5e414c1d12af16165881a16b638"`) + await db.query(`ALTER TABLE "identity" DROP CONSTRAINT "FK_bafa9e6c71c3f69cef6602a8095"`) + await db.query(`ALTER TABLE "membership" DROP CONSTRAINT "FK_efc905420f5f6bfded16c4eb978"`) + await db.query(`ALTER TABLE "smith_membership" DROP CONSTRAINT "FK_8645aa09a4cfa921be42f1bdc1b"`) + } +} diff --git a/package-lock.json b/package-lock.json index 082b171c3af2ffb0c46ca22a10556334f950eea5..09377e5ce3d622ae7136db2eb91e16d0aecdd522 100644 --- a/package-lock.json +++ b/package-lock.json @@ -6,6 +6,7 @@ "": { "name": "squid", "dependencies": { + "@belopash/typeorm-store": "^0.2.0", "@subsquid/archive-registry": "^3.3.0", "@subsquid/graphql-server": "^4.3.1", "@subsquid/ss58": "^2.0.1", @@ -191,6 +192,24 @@ "node": ">=6.9.0" } }, + "node_modules/@belopash/typeorm-store": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/@belopash/typeorm-store/-/typeorm-store-0.2.0.tgz", + "integrity": "sha512-oQVXOl1VqVmvMlvPgE+Cq4DeiUTKb//IKtDwf+IWefvlSIQZbcPJncrBHvEVkvP6YTCI46r+wwaF2Uebew2aEA==", + "dependencies": { + "@subsquid/logger": "^1.3.1", + "@subsquid/util-internal": "^2.5.2" + }, + "peerDependencies": { + "@subsquid/typeorm-store": "^1.2.1", + "typeorm": "^0.3.16" + } + }, + "node_modules/@belopash/typeorm-store/node_modules/@subsquid/util-internal": { + "version": "2.5.2", + "resolved": "https://registry.npmjs.org/@subsquid/util-internal/-/util-internal-2.5.2.tgz", + "integrity": "sha512-N7lfZdWEkM35jG5wdGYx25TJKGGLMOx9VInSeRhW9T/3BEmHAuSWI2mIIYnZ8w5L041V8HGo61ijWF6qsXvZjg==" + }, "node_modules/@exodus/schemasafe": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/@exodus/schemasafe/-/schemasafe-1.3.0.tgz", diff --git a/package.json b/package.json index af40280af26f78a2d86d825da05b25eac3f09ca2..c3af04e79cc0b86aaa4c47658b537817414afb7e 100644 --- a/package.json +++ b/package.json @@ -8,16 +8,17 @@ "build": "rm -rf lib && tsc" }, "dependencies": { + "@belopash/typeorm-store": "^0.2.0", "@subsquid/archive-registry": "^3.3.0", "@subsquid/graphql-server": "^4.3.1", "@subsquid/ss58": "^2.0.1", "@subsquid/substrate-processor": "^7.2.1", "@subsquid/typeorm-migration": "^1.2.2", "@subsquid/typeorm-store": "^1.2.4", + "@types/node": "^20.8.4", "dotenv": "^16.3.1", "pg": "8.11.3", "typeorm": "^0.3.17", - "@types/node": "^20.8.4", "typescript": "^5.3.2" }, "devDependencies": { diff --git a/schema.graphql b/schema.graphql index 50e8fc91f861dbc2ec15fdf089b9387b3b377d1b..35cd182558c249556c1995fa63040092e9fb165e 100644 --- a/schema.graphql +++ b/schema.graphql @@ -1,3 +1,119 @@ +# === this part of the schema comes from giant squid === +# https://github.com/subsquid-labs/giant-squid-explorer/blob/main/schema.graphql +# Block / Extrinsic / Call / Event + +type Block @entity { + "BlockHeight-blockHash - e.g. 0001812319-0001c" + id: ID! + height: Int! @index + hash: Bytes! @index + parentHash: Bytes! + stateRoot: Bytes! + extrinsicsicRoot: Bytes! + specName: String! + specVersion: Int! @index + implName: String! + implVersion: Int! + timestamp: DateTime! @index + validator: Bytes @index + + extrinsicsCount: Int! + callsCount: Int! + eventsCount: Int! + + extrinsics: [Extrinsic]! @derivedFrom(field: "block") @cardinality(value: 1000) + calls: [Call]! @derivedFrom(field: "block") @cardinality(value: 1000) + events: [Event]! @derivedFrom(field: "block") @cardinality(value: 1000) +} + +type ExtrinsicSignature { + address: JSON + signature: JSON + signedExtensions: JSON +} + +type Extrinsic @entity { + id: ID! + + block: Block! + call: Call! + + index: Int! + version: Int! + signature: ExtrinsicSignature + tip: BigInt + fee: BigInt + success: Boolean @index + error: JSON + hash: Bytes! @index + + calls: [Call]! @derivedFrom(field: "extrinsic") + events: [Event]! @derivedFrom(field: "extrinsic") +} + +type Call @entity @index(fields: ["id", "pallet", "name"]) { + id: ID! + + block: Block! + extrinsic: Extrinsic + parent: Call + + address: [Int!]! + success: Boolean! @index + error: JSON + + pallet: String! @index + name: String! @index + + args: JSON + argsStr: [String] + + subcalls: [Call]! @derivedFrom(field: "parent") + events: [Event]! @derivedFrom(field: "call") +} + +type Event @entity @index(fields: ["id", "pallet", "name"]) { + "Event id - e.g. 0000000001-000000-272d6" + id: ID! + + block: Block! + extrinsic: Extrinsic + call: Call + + index: Int! + phase: String! + + pallet: String! @index + name: String! @index + + args: JSON + argsStr: [String] +} + +enum CounterLevel { + Global + Pallet + Item +} + +enum ItemType { + Extrinsics + Calls + Events +} + +type ItemsCounter @entity { + id: ID! + type: ItemType! @index + level: CounterLevel! @index + total: Int! @index +} + + +# === this part of the schema is for substrate pallets === +# Balances / + + type Account @entity { "Account address is SS58 format" id: ID! @@ -15,6 +131,9 @@ type Transfer @entity { amount: BigInt! @index } +# === this part of the schema is for Duniter pallets === +# + "Identity" type Identity @entity { "Identity index" @@ -60,15 +179,3 @@ type SmithMembership @entity { identity: Identity! @unique @index expireOn: Int! @index } - -# --- generic call record --- - -type Call @entity { - name: String! @index - blockNumber: Int! @index -} - -type Block @entity { - number: Int! @index @unique - timestamp: DateTime! @index -} \ No newline at end of file diff --git a/src/giant-squid.ts b/src/giant-squid.ts new file mode 100644 index 0000000000000000000000000000000000000000..24b7a490e4112d2b751b04e26a1d97b71f6b8872 --- /dev/null +++ b/src/giant-squid.ts @@ -0,0 +1,126 @@ +import { BlockHeader } from "@subsquid/substrate-processor"; +import { StoreWithCache } from "@belopash/typeorm-store"; +import { decodeHex } from "@subsquid/substrate-processor"; +import * as model from "./model"; +import {Event, Extrinsic, ProcessorContext, Call} from './processor' +import type {Hash} from '@subsquid/substrate-data-raw' + +// functions defined in giant squid +// https://github.com/subsquid-labs/giant-squid-explorer/blob/main/src/main.ts + +// taken from @subsquid/substrate-data +interface BlockNonRequiredFields { + /** + * Root hash of the state merkle tree + */ + stateRoot: Hash + /** + * Root hash of the extrinsics merkle tree + */ + extrinsicsRoot: Hash + /** + * Block timestamp as set by `timestamp.now()` (unix epoch ms, compatible with `Date`). + */ + timestamp?: number + /** + * Account address of block validator + */ + validator?: Hash +} + +export async function saveBlock(ctx: ProcessorContext<StoreWithCache>, block: BlockHeader & BlockNonRequiredFields) { + const entity = new model.Block({ + id: block.id, + height: block.height, + hash: decodeHex(block.hash), + parentHash: decodeHex(block.parentHash), + timestamp: new Date(block.timestamp ?? 0), + extrinsicsicRoot: decodeHex(block.extrinsicsRoot), + specName: block.specName, + specVersion: block.specVersion, + implName: block.implName, + implVersion: block.implVersion, + stateRoot: decodeHex(block.stateRoot), + validator: block.validator ? decodeHex(block.validator) : undefined, + extrinsicsCount: 0, + callsCount: 0, + eventsCount: 0, + }); + + await ctx.store.insert(entity); +} + +export async function saveExtrinsic(ctx: ProcessorContext<StoreWithCache>, extrinsic: Extrinsic) { + const block = await ctx.store.getOrFail(model.Block, extrinsic.block.id); + + const entity = new model.Extrinsic({ + id: extrinsic.id, + block, + error: extrinsic.error, + fee: extrinsic.fee, + hash: decodeHex(extrinsic.hash), + index: extrinsic.index, + signature: new model.ExtrinsicSignature(extrinsic.signature), + success: extrinsic.success, + tip: extrinsic.tip, + version: extrinsic.version, + }); + await ctx.store.insert(entity); + + block.extrinsicsCount += 1; + await ctx.store.upsert(block); +} + +export async function saveCall(ctx: ProcessorContext<StoreWithCache>, call: Call) { + const block = await ctx.store.getOrFail(model.Block, call.block.id); + const extrinsic = await ctx.store.getOrFail(model.Extrinsic, call.getExtrinsic().id); + const parent = call.parentCall ? await ctx.store.getOrFail(model.Call, call.parentCall.id) : undefined; + + const [pallet, name] = call.name.split("."); + + const entity = new model.Call({ + id: call.id, + block, + address: call.address, + args: call.args, + error: call.error, + extrinsic, + name, + pallet, + parent, + success: call.success, + }); + await ctx.store.insert(entity); + + block.callsCount += 1; + await ctx.store.upsert(block); + + if (call.address.length == 0) { + extrinsic.call = entity; + await ctx.store.upsert(extrinsic); + } +} + +export async function saveEvent(ctx: ProcessorContext<StoreWithCache>, event: Event) { + const block = await ctx.store.getOrFail(model.Block, event.block.id); + const extrinsic = event.extrinsic ? await ctx.store.getOrFail(model.Extrinsic, event.extrinsic.id) : undefined; + const call = event.call ? await ctx.store.getOrFail(model.Call, event.call.id) : undefined; + + const [pallet, name] = event.name.split("."); + + const entity = new model.Event({ + id: event.id, + block, + args: event.args, + call, + extrinsic, + index: event.index, + name, + pallet, + phase: event.phase, + }); + await ctx.store.insert(entity); + + block.eventsCount += 1; + await ctx.store.upsert(block); +} diff --git a/src/main.ts b/src/main.ts index 6b4b24f97700b9c8de8522ee36a0ebaad266b440..374b12645d1e46be15c0ccb908c1076753fa08d9 100644 --- a/src/main.ts +++ b/src/main.ts @@ -1,240 +1,230 @@ -import {TypeormDatabase, Store} from '@subsquid/typeorm-store' -import {In} from 'typeorm' -import * as ss58 from '@subsquid/ss58' -import assert from 'assert' +// import { TypeormDatabase, Store } from "@subsquid/typeorm-store"; +import { StoreWithCache, TypeormDatabaseWithCache } from "@belopash/typeorm-store"; +import { In } from "typeorm"; +import * as ss58 from "@subsquid/ss58"; +import assert from "assert"; -import {processor, ProcessorContext} from './processor' -import {Account, Cert, Identity, Transfer, Call, Block} from './model' -import {events, calls} from './types' -import { string } from './model/generated/marshal' +import { processor, ProcessorContext } from "./processor"; +import { Account, Cert, Identity, Transfer, Call, Block } from "./model"; +import { events, calls } from "./types"; +import { string } from "./model/generated/marshal"; -processor.run(new TypeormDatabase({supportHotBlocks: true}), async (ctx) => { - let events: Events = getEvents(ctx) +import { saveBlock, saveExtrinsic, saveCall, saveEvent } from "./giant-squid"; - let accounts: Map<string, Account> = await createAccounts(ctx, events.transfer) - let idties: Map<number, Identity> = await createIdentities(ctx, events.cert) - let transfers: Transfer[] = createTransfers(events.transfer, accounts) - let certs: Cert[] = createCerts(events.cert, idties) - let theCalls: Call[] = createCalls(events.call) +// main processor loop able to manage a batch of blocks +processor.run(new TypeormDatabaseWithCache(), async (ctx) => { + // this part comes from giant squid + // https://github.com/subsquid-labs/giant-squid-explorer/blob/main/src/main.ts + for (const { header: block, calls, events, extrinsics } of ctx.blocks) { + ctx.log.debug(`block ${block.height}: extrinsics - ${extrinsics.length}, calls - ${calls.length}, events - ${events.length}`); + await saveBlock(ctx, block); + for (const extrinsic of extrinsics) { + await saveExtrinsic(ctx, extrinsic); + } + for (const call of calls.reverse()) { + await saveCall(ctx, call); + } + for (const event of events) { + await saveEvent(ctx, event); + } + } + + let events: DuniterData = getDuniterData(ctx); + + let accounts: Map<string, Account> = await createAccounts(ctx, events.transfer); // TODO create account with system event intead of tx + let idties: Map<number, Identity> = await createIdentities(ctx, events.cert); // TODO create identities with event instead of cert + let transfers: Transfer[] = createTransfers(events.transfer, accounts); + let certs: Cert[] = createCerts(events.cert, idties); - await ctx.store.upsert([...accounts.values()]) - await ctx.store.upsert([...idties.values()]) - await ctx.store.insert(transfers) - await ctx.store.insert(certs) - await ctx.store.insert(theCalls) - await ctx.store.insert(events.blocks) -}) + // UPSERT = update or insert if not existing + await ctx.store.upsert([...accounts.values()]); + await ctx.store.upsert([...idties.values()]); + // INSERT + await ctx.store.insert(transfers); + await ctx.store.insert(certs); +}); -interface Events { - blocks: Block[] - transfer: TransferEvent[] - cert: CertEvent[] - call: CallRecord[] +// a way to group data returned +interface DuniterData { + transfer: TransferEvent[]; + cert: CertEvent[]; } + +// buffer to get all the accounts to fetch before inserting interface TransferEvent { - id: string - blockNumber: number - timestamp: Date - from: string - to: string - amount: bigint + id: string; + blockNumber: number; + timestamp: Date; + from: string; + to: string; + amount: bigint; } interface CertEvent { - id: string - issuer: number - receiver: number - blockNumber: number -} -interface CallRecord { - id: string - name: string - blockNumber: number - // eventNames: string[] // call could be linked to events (not their names) + id: string; + issuer: number; + receiver: number; + blockNumber: number; } -function getEvents(ctx: ProcessorContext<Store>): Events { - let transfers: TransferEvent[] = [] - let certs: CertEvent[] = [] - let all_calls: CallRecord[] = [] - let blocks: Block[] = [] +function getDuniterData(ctx: ProcessorContext<StoreWithCache>): DuniterData { + let transfers: TransferEvent[] = []; + let certs: CertEvent[] = []; - // Blocks - for (let block of ctx.blocks) { - // Block - blocks.push(new Block({ - id: block.header.hash, - number: block.header.height, - timestamp: block.header.timestamp ? new Date(block.header.timestamp) : new Date(0), - })) - - // Events - for (let event of block.events) { - switch (event.name) { - // Balances.Transfer - case events.balances.transfer.name: - let rec: {from: string; to: string; amount: bigint} - if (events.balances.transfer.v700.is(event)) { - rec = events.balances.transfer.v700.decode(event) - } else { - throw new Error('Unsupported spec') - } - assert(block.header.timestamp, `Got an undefined timestamp at block ${block.header.height}`) - transfers.push({ - id: event.id, - blockNumber: block.header.height, - timestamp: new Date(block.header.timestamp), - from: ss58.codec(42).encode(rec.from), - to: ss58.codec(42).encode(rec.to), - amount: rec.amount, - }) - break - // Cert.NewCert - case events.cert.newCert.name: - let cert: {issuer: number, receiver: number} - if (events.cert.newCert.v700.is(event)) { - cert = events.cert.newCert.v700.decode(event) - } else { - throw new Error('Unsupported spec') - } - certs.push({ - id: event.id, - blockNumber: block.header.height, - issuer: cert.issuer, - receiver: cert.receiver, - }) - break - // Cert.RenewCert - case events.cert.renewedCert.name: - let recert: {issuer: number, receiver: number} - if (events.cert.renewedCert.v700.is(event)) { - recert = events.cert.renewedCert.v700.decode(event) - } else { - throw new Error('Unsupported spec') - } - certs.push({ - id: event.id, - blockNumber: block.header.height, - issuer: recert.issuer, - receiver: recert.receiver, - }) - break - default: - // ctx.log.info(`Unhandled event ${event.name}`) - } - } - for (let call of block.calls) { - if (call.name != "Timestamp.set") { - all_calls.push(new Call({ - id: call.id, - name: call.name, - blockNumber: block.header.height, - // eventNames: call.events.map((e: Event) => e.id) - })) - } - } - } - let all_events: Events = { - transfer: transfers, - cert: certs, - call: all_calls, - blocks, + // Blocks + for (let block of ctx.blocks) { + // Events + for (let event of block.events) { + switch (event.name) { + // Balances.Transfer + case events.balances.transfer.name: + let rec: { from: string; to: string; amount: bigint }; + if (events.balances.transfer.v700.is(event)) { + rec = events.balances.transfer.v700.decode(event); + } else { + throw new Error("Unsupported spec"); + } + assert(block.header.timestamp, `Got an undefined timestamp at block ${block.header.height}`); + transfers.push({ + id: event.id, + blockNumber: block.header.height, + timestamp: new Date(block.header.timestamp), + from: ss58.codec(42).encode(rec.from), + to: ss58.codec(42).encode(rec.to), + amount: rec.amount, + }); + break; + // Cert.NewCert + case events.cert.newCert.name: + let cert: { issuer: number; receiver: number }; + if (events.cert.newCert.v700.is(event)) { + cert = events.cert.newCert.v700.decode(event); + } else { + throw new Error("Unsupported spec"); + } + certs.push({ + id: event.id, + blockNumber: block.header.height, + issuer: cert.issuer, + receiver: cert.receiver, + }); + break; + // Cert.RenewCert + case events.cert.renewedCert.name: + let recert: { issuer: number; receiver: number }; + if (events.cert.renewedCert.v700.is(event)) { + recert = events.cert.renewedCert.v700.decode(event); + } else { + throw new Error("Unsupported spec"); + } + certs.push({ + id: event.id, + blockNumber: block.header.height, + issuer: recert.issuer, + receiver: recert.receiver, + }); + break; + default: + // ctx.log.info(`Unhandled event ${event.name}`) + } } - return all_events + } + let all_events: DuniterData = { + transfer: transfers, + cert: certs, + }; + return all_events; } -async function createAccounts(ctx: ProcessorContext<Store>, transferEvents: TransferEvent[]): Promise<Map<string,Account>> { - // collect all accounts from transfers - const accountIds = new Set<string>() - for (let t of transferEvents) { - accountIds.add(t.from) - accountIds.add(t.to) +async function createAccounts(ctx: ProcessorContext<StoreWithCache>, transferEvents: TransferEvent[]): Promise<Map<string, Account>> { + // collect all accounts from transfers + const accountIds = new Set<string>(); + for (let t of transferEvents) { + accountIds.add(t.from); + accountIds.add(t.to); + } + // build a map of accounts ids that are already stored + const accounts = await ctx.store.findBy(Account, { id: In([...accountIds]) }).then((accounts) => { + return new Map(accounts.map((a) => [a.id, a])); + }); + // for each transfer, add an account if not already existing + for (let t of transferEvents) { + updateAccounts(t.from); + updateAccounts(t.to); + } + // function to add inexisting accounts to the map + function updateAccounts(id: string): void { + const acc = accounts.get(id); + if (acc == null) { + accounts.set(id, new Account({ id })); } - // build a map of accounts ids that are already stored - const accounts = await ctx.store.findBy(Account, {id: In([...accountIds])}).then((accounts) => { - return new Map(accounts.map((a) => [a.id, a])) - }) - // for each transfer, add an account if not already existing - for (let t of transferEvents) { - updateAccounts(t.from) - updateAccounts(t.to) - } - // function to add inexisting accounts to the map - function updateAccounts(id: string): void { - const acc = accounts.get(id) - if (acc == null) { - accounts.set(id, new Account({id})) - } - } - // return this account map - return accounts + } + // return this account map + return accounts; } -async function createIdentities(ctx: ProcessorContext<Store>, certEvents: CertEvent[]): Promise<Map<number,Identity>> { - const idtyId = new Set<number>() - for (let c of certEvents) { - idtyId.add(c.issuer) - idtyId.add(c.receiver) - } - const idties = await ctx.store.findBy(Identity, {id: In([...idtyId])}).then((idties) => { - return new Map(idties.map((i) => [i.index, i])) - }) - for (let c of certEvents) { - updateIdties(c.issuer) - updateIdties(c.receiver) - } - function updateIdties(index: number): void { - const acc = idties.get(index) - if (acc == null) { - idties.set(index, new Identity({ - id: String(index), - index, - // account: , - // name: , - })) - } +async function createIdentities(ctx: ProcessorContext<StoreWithCache>, certEvents: CertEvent[]): Promise<Map<number, Identity>> { + const idtyId = new Set<number>(); + for (let c of certEvents) { + idtyId.add(c.issuer); + idtyId.add(c.receiver); + } + const idties = await ctx.store.findBy(Identity, { id: In([...idtyId]) }).then((idties) => { + return new Map(idties.map((i) => [i.index, i])); + }); + for (let c of certEvents) { + updateIdties(c.issuer); + updateIdties(c.receiver); + } + function updateIdties(index: number): void { + const acc = idties.get(index); + if (acc == null) { + idties.set( + index, + new Identity({ + id: String(index), + index, + // account: , + // name: , + }) + ); } - return idties + } + return idties; } function createTransfers(transferEvents: TransferEvent[], accounts: Map<string, Account>): Transfer[] { - let transfers: Transfer[] = [] - for (let t of transferEvents) { - let {id, blockNumber, timestamp, amount} = t - let from = accounts.get(t.from) - let to = accounts.get(t.to) - transfers.push(new Transfer({ - id, - blockNumber, - timestamp, - from, - to, - amount, - })) - } - return transfers + let transfers: Transfer[] = []; + for (let t of transferEvents) { + let { id, blockNumber, timestamp, amount } = t; + let from = accounts.get(t.from); + let to = accounts.get(t.to); + transfers.push( + new Transfer({ + id, + blockNumber, + timestamp, + from, + to, + amount, + }) + ); + } + return transfers; } function createCerts(certEvents: CertEvent[], idties: Map<number, Identity>): Cert[] { - let certs: Cert[] = [] - for (let c of certEvents) { - let {id, blockNumber, issuer, receiver} = c - let issuerIdty = idties.get(issuer) - let receiverIdty = idties.get(receiver) - certs.push(new Cert({ - id, - blockNumber, - issuer: issuerIdty, - receiver: receiverIdty, - })) - } - return certs + let certs: Cert[] = []; + for (let c of certEvents) { + let { id, blockNumber, issuer, receiver } = c; + let issuerIdty = idties.get(issuer); + let receiverIdty = idties.get(receiver); + certs.push( + new Cert({ + id, + issuer: issuerIdty, + receiver: receiverIdty, + }) + ); + } + return certs; } - -function createCalls(theCalls: CallRecord[]): Call[] { - let calls : Call[] = [] - for (let c of theCalls) { - let {id, blockNumber, name} = c - calls.push(new Call({id, blockNumber, name})) - } - return calls -} \ No newline at end of file diff --git a/src/model/generated/_counterLevel.ts b/src/model/generated/_counterLevel.ts new file mode 100644 index 0000000000000000000000000000000000000000..fd3c55e12cfce5ef5690df1f046eab7441aae33a --- /dev/null +++ b/src/model/generated/_counterLevel.ts @@ -0,0 +1,5 @@ +export enum CounterLevel { + Global = "Global", + Pallet = "Pallet", + Item = "Item", +} diff --git a/src/model/generated/_extrinsicSignature.ts b/src/model/generated/_extrinsicSignature.ts new file mode 100644 index 0000000000000000000000000000000000000000..a0c4a71c7ff6d24e25e95e22b8c00c20ef05b1a0 --- /dev/null +++ b/src/model/generated/_extrinsicSignature.ts @@ -0,0 +1,49 @@ +import assert from "assert" +import * as marshal from "./marshal" + +export class ExtrinsicSignature { + private _address!: unknown | undefined | null + private _signature!: unknown | undefined | null + private _signedExtensions!: unknown | undefined | null + + constructor(props?: Partial<Omit<ExtrinsicSignature, 'toJSON'>>, json?: any) { + Object.assign(this, props) + if (json != null) { + this._address = json.address + this._signature = json.signature + this._signedExtensions = json.signedExtensions + } + } + + get address(): unknown | undefined | null { + return this._address + } + + set address(value: unknown | undefined | null) { + this._address = value + } + + get signature(): unknown | undefined | null { + return this._signature + } + + set signature(value: unknown | undefined | null) { + this._signature = value + } + + get signedExtensions(): unknown | undefined | null { + return this._signedExtensions + } + + set signedExtensions(value: unknown | undefined | null) { + this._signedExtensions = value + } + + toJSON(): object { + return { + address: this.address, + signature: this.signature, + signedExtensions: this.signedExtensions, + } + } +} diff --git a/src/model/generated/_itemType.ts b/src/model/generated/_itemType.ts new file mode 100644 index 0000000000000000000000000000000000000000..4888f0722cfc3dec86171e44b94291b85877eca9 --- /dev/null +++ b/src/model/generated/_itemType.ts @@ -0,0 +1,5 @@ +export enum ItemType { + Extrinsics = "Extrinsics", + Calls = "Calls", + Events = "Events", +} diff --git a/src/model/generated/block.model.ts b/src/model/generated/block.model.ts index 0f65c6a5e7d196b0d50ae329324e01486cd263c9..46c2a4955d57d4f974275ec6993367875efcc8fb 100644 --- a/src/model/generated/block.model.ts +++ b/src/model/generated/block.model.ts @@ -1,4 +1,7 @@ -import {Entity as Entity_, Column as Column_, PrimaryColumn as PrimaryColumn_, Index as Index_} from "typeorm" +import {Entity as Entity_, Column as Column_, PrimaryColumn as PrimaryColumn_, Index as Index_, OneToMany as OneToMany_} from "typeorm" +import {Extrinsic} from "./extrinsic.model" +import {Call} from "./call.model" +import {Event} from "./event.model" @Entity_() export class Block { @@ -6,14 +9,65 @@ export class Block { Object.assign(this, props) } + /** + * BlockHeight-blockHash - e.g. 0001812319-0001c + */ @PrimaryColumn_() id!: string - @Index_({unique: true}) + @Index_() + @Column_("int4", {nullable: false}) + height!: number + + @Index_() + @Column_("bytea", {nullable: false}) + hash!: Uint8Array + + @Column_("bytea", {nullable: false}) + parentHash!: Uint8Array + + @Column_("bytea", {nullable: false}) + stateRoot!: Uint8Array + + @Column_("bytea", {nullable: false}) + extrinsicsicRoot!: Uint8Array + + @Column_("text", {nullable: false}) + specName!: string + + @Index_() + @Column_("int4", {nullable: false}) + specVersion!: number + + @Column_("text", {nullable: false}) + implName!: string + @Column_("int4", {nullable: false}) - number!: number + implVersion!: number @Index_() @Column_("timestamp with time zone", {nullable: false}) timestamp!: Date + + @Index_() + @Column_("bytea", {nullable: true}) + validator!: Uint8Array | undefined | null + + @Column_("int4", {nullable: false}) + extrinsicsCount!: number + + @Column_("int4", {nullable: false}) + callsCount!: number + + @Column_("int4", {nullable: false}) + eventsCount!: number + + @OneToMany_(() => Extrinsic, e => e.block) + extrinsics!: Extrinsic[] + + @OneToMany_(() => Call, e => e.block) + calls!: Call[] + + @OneToMany_(() => Event, e => e.block) + events!: Event[] } diff --git a/src/model/generated/call.model.ts b/src/model/generated/call.model.ts index 09553a8a69d851931ca826d6a32fb4114656b343..933cfef6d9e9e88a7ba3e42d10a8ce742a039eb5 100644 --- a/src/model/generated/call.model.ts +++ b/src/model/generated/call.model.ts @@ -1,5 +1,9 @@ -import {Entity as Entity_, Column as Column_, PrimaryColumn as PrimaryColumn_, Index as Index_} from "typeorm" +import {Entity as Entity_, Column as Column_, PrimaryColumn as PrimaryColumn_, Index as Index_, ManyToOne as ManyToOne_, OneToMany as OneToMany_} from "typeorm" +import {Block} from "./block.model" +import {Extrinsic} from "./extrinsic.model" +import {Event} from "./event.model" +@Index_(["id", "pallet", "name"], {unique: false}) @Entity_() export class Call { constructor(props?: Partial<Call>) { @@ -9,11 +13,45 @@ export class Call { @PrimaryColumn_() id!: string + @Index_() + @ManyToOne_(() => Block, {nullable: true}) + block!: Block + + @Index_() + @ManyToOne_(() => Extrinsic, {nullable: true}) + extrinsic!: Extrinsic | undefined | null + + @Index_() + @ManyToOne_(() => Call, {nullable: true}) + parent!: Call | undefined | null + + @Column_("int4", {array: true, nullable: false}) + address!: (number)[] + + @Index_() + @Column_("bool", {nullable: false}) + success!: boolean + + @Column_("jsonb", {nullable: true}) + error!: unknown | undefined | null + @Index_() @Column_("text", {nullable: false}) - name!: string + pallet!: string @Index_() - @Column_("int4", {nullable: false}) - blockNumber!: number + @Column_("text", {nullable: false}) + name!: string + + @Column_("jsonb", {nullable: true}) + args!: unknown | undefined | null + + @Column_("text", {array: true, nullable: true}) + argsStr!: (string | undefined | null)[] | undefined | null + + @OneToMany_(() => Call, e => e.parent) + subcalls!: Call[] + + @OneToMany_(() => Event, e => e.call) + events!: Event[] } diff --git a/src/model/generated/event.model.ts b/src/model/generated/event.model.ts new file mode 100644 index 0000000000000000000000000000000000000000..43ee5e1cda08e02a9d12745fdd2af2dae443cae8 --- /dev/null +++ b/src/model/generated/event.model.ts @@ -0,0 +1,50 @@ +import {Entity as Entity_, Column as Column_, PrimaryColumn as PrimaryColumn_, Index as Index_, ManyToOne as ManyToOne_} from "typeorm" +import {Block} from "./block.model" +import {Extrinsic} from "./extrinsic.model" +import {Call} from "./call.model" + +@Index_(["id", "pallet", "name"], {unique: false}) +@Entity_() +export class Event { + constructor(props?: Partial<Event>) { + Object.assign(this, props) + } + + /** + * Event id - e.g. 0000000001-000000-272d6 + */ + @PrimaryColumn_() + id!: string + + @Index_() + @ManyToOne_(() => Block, {nullable: true}) + block!: Block + + @Index_() + @ManyToOne_(() => Extrinsic, {nullable: true}) + extrinsic!: Extrinsic | undefined | null + + @Index_() + @ManyToOne_(() => Call, {nullable: true}) + call!: Call | undefined | null + + @Column_("int4", {nullable: false}) + index!: number + + @Column_("text", {nullable: false}) + phase!: string + + @Index_() + @Column_("text", {nullable: false}) + pallet!: string + + @Index_() + @Column_("text", {nullable: false}) + name!: string + + @Column_("jsonb", {nullable: true}) + args!: unknown | undefined | null + + @Column_("text", {array: true, nullable: true}) + argsStr!: (string | undefined | null)[] | undefined | null +} diff --git a/src/model/generated/extrinsic.model.ts b/src/model/generated/extrinsic.model.ts new file mode 100644 index 0000000000000000000000000000000000000000..4eecefd20e83a26a1bd142aef0b976ad03e356a7 --- /dev/null +++ b/src/model/generated/extrinsic.model.ts @@ -0,0 +1,56 @@ +import {Entity as Entity_, Column as Column_, PrimaryColumn as PrimaryColumn_, ManyToOne as ManyToOne_, Index as Index_, OneToMany as OneToMany_} from "typeorm" +import * as marshal from "./marshal" +import {Block} from "./block.model" +import {Call} from "./call.model" +import {ExtrinsicSignature} from "./_extrinsicSignature" +import {Event} from "./event.model" + +@Entity_() +export class Extrinsic { + constructor(props?: Partial<Extrinsic>) { + Object.assign(this, props) + } + + @PrimaryColumn_() + id!: string + + @Index_() + @ManyToOne_(() => Block, {nullable: true}) + block!: Block + + @Index_() + @ManyToOne_(() => Call, {nullable: true}) + call!: Call + + @Column_("int4", {nullable: false}) + index!: number + + @Column_("int4", {nullable: false}) + version!: number + + @Column_("jsonb", {transformer: {to: obj => obj == null ? undefined : obj.toJSON(), from: obj => obj == null ? undefined : new ExtrinsicSignature(undefined, obj)}, nullable: true}) + signature!: ExtrinsicSignature | undefined | null + + @Column_("numeric", {transformer: marshal.bigintTransformer, nullable: true}) + tip!: bigint | undefined | null + + @Column_("numeric", {transformer: marshal.bigintTransformer, nullable: true}) + fee!: bigint | undefined | null + + @Index_() + @Column_("bool", {nullable: true}) + success!: boolean | undefined | null + + @Column_("jsonb", {nullable: true}) + error!: unknown | undefined | null + + @Index_() + @Column_("bytea", {nullable: false}) + hash!: Uint8Array + + @OneToMany_(() => Call, e => e.extrinsic) + calls!: Call[] + + @OneToMany_(() => Event, e => e.extrinsic) + events!: Event[] +} diff --git a/src/model/generated/index.ts b/src/model/generated/index.ts index 8703db3e4d33ae6f191fce19d6b7ed28b3054a97..318f67bd157a4fc726217b9c29d0f6ed3d41b5c9 100644 --- a/src/model/generated/index.ts +++ b/src/model/generated/index.ts @@ -1,3 +1,11 @@ +export * from "./block.model" +export * from "./extrinsic.model" +export * from "./_extrinsicSignature" +export * from "./call.model" +export * from "./event.model" +export * from "./itemsCounter.model" +export * from "./_itemType" +export * from "./_counterLevel" export * from "./account.model" export * from "./transfer.model" export * from "./identity.model" @@ -5,5 +13,3 @@ export * from "./cert.model" export * from "./smithCert.model" export * from "./membership.model" export * from "./smithMembership.model" -export * from "./call.model" -export * from "./block.model" diff --git a/src/model/generated/itemsCounter.model.ts b/src/model/generated/itemsCounter.model.ts new file mode 100644 index 0000000000000000000000000000000000000000..7196bbdd34ea205c391f171ff361e8cee469ced3 --- /dev/null +++ b/src/model/generated/itemsCounter.model.ts @@ -0,0 +1,25 @@ +import {Entity as Entity_, Column as Column_, PrimaryColumn as PrimaryColumn_, Index as Index_} from "typeorm" +import {ItemType} from "./_itemType" +import {CounterLevel} from "./_counterLevel" + +@Entity_() +export class ItemsCounter { + constructor(props?: Partial<ItemsCounter>) { + Object.assign(this, props) + } + + @PrimaryColumn_() + id!: string + + @Index_() + @Column_("varchar", {length: 10, nullable: false}) + type!: ItemType + + @Index_() + @Column_("varchar", {length: 6, nullable: false}) + level!: CounterLevel + + @Index_() + @Column_("int4", {nullable: false}) + total!: number +} diff --git a/src/processor.ts b/src/processor.ts index bf9b4db982a84b8556f73e8969b0f34203c4da78..46e571add7050112cb022ca7a515af1dc579dbc6 100644 --- a/src/processor.ts +++ b/src/processor.ts @@ -29,16 +29,27 @@ export const processor = new SubstrateBatchProcessor() .addEvent({ // name: [], name: [events.balances.transfer.name, events.cert.newCert.name, events.cert.renewedCert.name, events.cert.removedCert.name], + // extrinsic: true, + // https://github.com/subsquid-labs/giant-squid-explorer/blob/main/src/processor.ts + call: true, extrinsic: true, }) .addCall({ // name: [] - events: true, + // events: true, + // https://github.com/subsquid-labs/giant-squid-explorer/blob/main/src/processor.ts + extrinsic: true, + stack: true, }) .setFields({ - event: { - name: true, - args: true, + // this part of the processor config comes from giant squid + // https://github.com/subsquid-labs/giant-squid-explorer/blob/main/src/processor.ts + block: { + timestamp: true, + digest: true, + extrinsicsRoot: true, + stateRoot: true, + validator: true, }, call: { name: true, @@ -47,14 +58,20 @@ export const processor = new SubstrateBatchProcessor() success: true, error: true, }, + event: { + name: true, + args: true, + phase: true, + }, extrinsic: { + hash: true, success: true, error: true, + fee: true, + signature: true, + tip: true, + version: true, }, - block: { - height: true, - timestamp: true, - } }) // Uncomment to disable RPC ingestion and drastically reduce no of RPC calls //.useArchiveOnly()