diff --git a/package-lock.json b/package-lock.json
index 2d5ff71e..e8ef2b91 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -23,6 +23,7 @@
         "it-pushable": "^3.2.0",
         "multiformats": "^12.0.1",
         "protobufjs": "^7.2.4",
+        "protons-runtime": "^3.1.0",
         "uint8arraylist": "^2.4.3",
         "uint8arrays": "^4.0.4"
       },
@@ -52,6 +53,7 @@
         "p-retry": "^5.1.2",
         "p-wait-for": "^5.0.2",
         "prettier": "^2.0.5",
+        "protons": "^5.1.0",
         "sinon": "^15.1.2",
         "time-cache": "^0.3.0",
         "ts-node": "^10.7.0",
@@ -2556,6 +2558,18 @@
       "integrity": "sha512-XPSJHWmi394fuUuzDnGz1wiKqWfo1yXecHQMRf2l6hztTO+nPru658AyDngaBe7isIxEkRsPR3FZh+s7iVa4Uw==",
       "dev": true
     },
+    "node_modules/@jsdoc/salty": {
+      "version": "0.2.5",
+      "resolved": "https://registry.npmjs.org/@jsdoc/salty/-/salty-0.2.5.tgz",
+      "integrity": "sha512-TfRP53RqunNe2HBobVBJ0VLhK1HbfvBYeTC1ahnN64PWvyYyGebmMiPkuwvD9fpw2ZbkoPb8Q7mwy0aR8Z9rvw==",
+      "dev": true,
+      "dependencies": {
+        "lodash": "^4.17.21"
+      },
+      "engines": {
+        "node": ">=v12.0.0"
+      }
+    },
     "node_modules/@libp2p/crypto": {
       "version": "2.0.0",
       "resolved": "https://registry.npmjs.org/@libp2p/crypto/-/crypto-2.0.0.tgz",
@@ -2571,6 +2585,18 @@
         "uint8arrays": "^4.0.4"
       }
     },
+    "node_modules/@libp2p/crypto/node_modules/protons-runtime": {
+      "version": "5.0.1",
+      "resolved": "https://registry.npmjs.org/protons-runtime/-/protons-runtime-5.0.1.tgz",
+      "integrity": "sha512-AwyAA3pQ4Ka4tEBMdIjLi/cRdpb322f7sgv3NruVq9yguLggzwu5eeLe1HuRPFYlI4UsVN/QK/AQXjLPVLCzTA==",
+      "dependencies": {
+        "protobufjs": "^7.0.0",
+        "uint8arraylist": "^2.4.3"
+      },
+      "peerDependencies": {
+        "uint8arraylist": "^2.3.2"
+      }
+    },
     "node_modules/@libp2p/floodsub": {
       "version": "8.0.1",
       "resolved": "https://registry.npmjs.org/@libp2p/floodsub/-/floodsub-8.0.1.tgz",
@@ -2585,6 +2611,19 @@
         "uint8arrays": "^4.0.4"
       }
     },
+    "node_modules/@libp2p/floodsub/node_modules/protons-runtime": {
+      "version": "5.0.1",
+      "resolved": "https://registry.npmjs.org/protons-runtime/-/protons-runtime-5.0.1.tgz",
+      "integrity": "sha512-AwyAA3pQ4Ka4tEBMdIjLi/cRdpb322f7sgv3NruVq9yguLggzwu5eeLe1HuRPFYlI4UsVN/QK/AQXjLPVLCzTA==",
+      "dev": true,
+      "dependencies": {
+        "protobufjs": "^7.0.0",
+        "uint8arraylist": "^2.4.3"
+      },
+      "peerDependencies": {
+        "uint8arraylist": "^2.3.2"
+      }
+    },
     "node_modules/@libp2p/interface": {
       "version": "0.1.0",
       "resolved": "https://registry.npmjs.org/@libp2p/interface/-/interface-0.1.0.tgz",
@@ -2638,6 +2677,19 @@
         "uint8arrays": "^4.0.4"
       }
     },
+    "node_modules/@libp2p/interface-compliance-tests/node_modules/protons-runtime": {
+      "version": "5.0.2",
+      "resolved": "https://registry.npmjs.org/protons-runtime/-/protons-runtime-5.0.2.tgz",
+      "integrity": "sha512-eKppVrIS5dDh+Y61Yj4bDEOs2sQLQbQGIhr7EBiybPQhIMGBynzVXlYILPWl3Td1GDadobc8qevh5D+JwfG9bw==",
+      "dev": true,
+      "dependencies": {
+        "protobufjs": "^7.0.0",
+        "uint8arraylist": "^2.4.3"
+      },
+      "peerDependencies": {
+        "uint8arraylist": "^2.3.2"
+      }
+    },
     "node_modules/@libp2p/interface-internal": {
       "version": "0.1.0",
       "resolved": "https://registry.npmjs.org/@libp2p/interface-internal/-/interface-internal-0.1.0.tgz",
@@ -2748,6 +2800,19 @@
         "uint8arrays": "^4.0.4"
       }
     },
+    "node_modules/@libp2p/peer-id-factory/node_modules/protons-runtime": {
+      "version": "5.0.1",
+      "resolved": "https://registry.npmjs.org/protons-runtime/-/protons-runtime-5.0.1.tgz",
+      "integrity": "sha512-AwyAA3pQ4Ka4tEBMdIjLi/cRdpb322f7sgv3NruVq9yguLggzwu5eeLe1HuRPFYlI4UsVN/QK/AQXjLPVLCzTA==",
+      "dev": true,
+      "dependencies": {
+        "protobufjs": "^7.0.0",
+        "uint8arraylist": "^2.4.3"
+      },
+      "peerDependencies": {
+        "uint8arraylist": "^2.3.2"
+      }
+    },
     "node_modules/@libp2p/peer-record": {
       "version": "6.0.0",
       "resolved": "https://registry.npmjs.org/@libp2p/peer-record/-/peer-record-6.0.0.tgz",
@@ -2765,6 +2830,19 @@
         "uint8arrays": "^4.0.4"
       }
     },
+    "node_modules/@libp2p/peer-record/node_modules/protons-runtime": {
+      "version": "5.0.1",
+      "resolved": "https://registry.npmjs.org/protons-runtime/-/protons-runtime-5.0.1.tgz",
+      "integrity": "sha512-AwyAA3pQ4Ka4tEBMdIjLi/cRdpb322f7sgv3NruVq9yguLggzwu5eeLe1HuRPFYlI4UsVN/QK/AQXjLPVLCzTA==",
+      "dev": true,
+      "dependencies": {
+        "protobufjs": "^7.0.0",
+        "uint8arraylist": "^2.4.3"
+      },
+      "peerDependencies": {
+        "uint8arraylist": "^2.3.2"
+      }
+    },
     "node_modules/@libp2p/peer-store": {
       "version": "9.0.0",
       "resolved": "https://registry.npmjs.org/@libp2p/peer-store/-/peer-store-9.0.0.tgz",
@@ -2787,6 +2865,19 @@
         "uint8arrays": "^4.0.4"
       }
     },
+    "node_modules/@libp2p/peer-store/node_modules/protons-runtime": {
+      "version": "5.0.1",
+      "resolved": "https://registry.npmjs.org/protons-runtime/-/protons-runtime-5.0.1.tgz",
+      "integrity": "sha512-AwyAA3pQ4Ka4tEBMdIjLi/cRdpb322f7sgv3NruVq9yguLggzwu5eeLe1HuRPFYlI4UsVN/QK/AQXjLPVLCzTA==",
+      "dev": true,
+      "dependencies": {
+        "protobufjs": "^7.0.0",
+        "uint8arraylist": "^2.4.3"
+      },
+      "peerDependencies": {
+        "uint8arraylist": "^2.3.2"
+      }
+    },
     "node_modules/@libp2p/pubsub": {
       "version": "8.0.1",
       "resolved": "https://registry.npmjs.org/@libp2p/pubsub/-/pubsub-8.0.1.tgz",
@@ -3896,6 +3987,22 @@
         "@types/node": "*"
       }
     },
+    "node_modules/@types/linkify-it": {
+      "version": "3.0.2",
+      "resolved": "https://registry.npmjs.org/@types/linkify-it/-/linkify-it-3.0.2.tgz",
+      "integrity": "sha512-HZQYqbiFVWufzCwexrvh694SOim8z2d+xJl5UNamcvQFejLY/2YUtzXHYi3cHdI7PMlS8ejH2slRAOJQ32aNbA==",
+      "dev": true
+    },
+    "node_modules/@types/markdown-it": {
+      "version": "12.2.3",
+      "resolved": "https://registry.npmjs.org/@types/markdown-it/-/markdown-it-12.2.3.tgz",
+      "integrity": "sha512-GKMHFfv3458yYy+v/N8gjufHO6MSZKCOXpZc5GXIWWy8uldwfmPn98vp81gZ5f9SVw8YYBctgfJ22a2d7AOMeQ==",
+      "dev": true,
+      "dependencies": {
+        "@types/linkify-it": "*",
+        "@types/mdurl": "*"
+      }
+    },
     "node_modules/@types/mdast": {
       "version": "4.0.0",
       "resolved": "https://registry.npmjs.org/@types/mdast/-/mdast-4.0.0.tgz",
@@ -3905,6 +4012,12 @@
         "@types/unist": "*"
       }
     },
+    "node_modules/@types/mdurl": {
+      "version": "1.0.2",
+      "resolved": "https://registry.npmjs.org/@types/mdurl/-/mdurl-1.0.2.tgz",
+      "integrity": "sha512-eC4U9MlIcu2q0KQmXszyn5Akca/0jrQmwDRgpAMJai7qBWq4amIQhZyNau4VYGtCeALvW1/NtjzJJ567aZxfKA==",
+      "dev": true
+    },
     "node_modules/@types/minimatch": {
       "version": "3.0.5",
       "resolved": "https://registry.npmjs.org/@types/minimatch/-/minimatch-3.0.5.tgz",
@@ -6195,6 +6308,12 @@
         "node": ">= 6"
       }
     },
+    "node_modules/bluebird": {
+      "version": "3.7.2",
+      "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.7.2.tgz",
+      "integrity": "sha512-XpNj6GDQzdfW+r2Wnn7xiSAd7TM3jzkxGXBGTtWKuSXv1xUV+azxAm8jdWZN06QTQk+2N2XB9jRDkvbmQmcRtg==",
+      "dev": true
+    },
     "node_modules/boolean": {
       "version": "3.2.0",
       "resolved": "https://registry.npmjs.org/boolean/-/boolean-3.2.0.tgz",
@@ -6724,6 +6843,18 @@
         "cdl": "bin/cdl.js"
       }
     },
+    "node_modules/catharsis": {
+      "version": "0.9.0",
+      "resolved": "https://registry.npmjs.org/catharsis/-/catharsis-0.9.0.tgz",
+      "integrity": "sha512-prMTQVpcns/tzFgFVkVp6ak6RykZyWb3gu8ckUpd6YkTlacOd3DXGJjIpD4Q6zJirizvaiAjSSHlOsA+6sNh2A==",
+      "dev": true,
+      "dependencies": {
+        "lodash": "^4.17.15"
+      },
+      "engines": {
+        "node": ">= 10"
+      }
+    },
     "node_modules/ccount": {
       "version": "2.0.1",
       "resolved": "https://registry.npmjs.org/ccount/-/ccount-2.0.1.tgz",
@@ -8594,6 +8725,15 @@
         "node": ">=8.6"
       }
     },
+    "node_modules/entities": {
+      "version": "2.1.0",
+      "resolved": "https://registry.npmjs.org/entities/-/entities-2.1.0.tgz",
+      "integrity": "sha512-hCx1oky9PFrJ611mf0ifBLBRW8lUUVRlFolb5gWRfIELabBlbp9xZvrqZLZAs+NxFnbfQoeGd8wDkygjg7U85w==",
+      "dev": true,
+      "funding": {
+        "url": "https://github.com/fb55/entities?sponsor=1"
+      }
+    },
     "node_modules/env-ci": {
       "version": "9.1.1",
       "resolved": "https://registry.npmjs.org/env-ci/-/env-ci-9.1.1.tgz",
@@ -8818,6 +8958,79 @@
         "node": ">=0.8.0"
       }
     },
+    "node_modules/escodegen": {
+      "version": "1.14.3",
+      "resolved": "https://registry.npmjs.org/escodegen/-/escodegen-1.14.3.tgz",
+      "integrity": "sha512-qFcX0XJkdg+PB3xjZZG/wKSuT1PnQWx57+TVSjIMmILd2yC/6ByYElPwJnslDsuWuSAp4AwJGumarAAmJch5Kw==",
+      "dev": true,
+      "dependencies": {
+        "esprima": "^4.0.1",
+        "estraverse": "^4.2.0",
+        "esutils": "^2.0.2",
+        "optionator": "^0.8.1"
+      },
+      "bin": {
+        "escodegen": "bin/escodegen.js",
+        "esgenerate": "bin/esgenerate.js"
+      },
+      "engines": {
+        "node": ">=4.0"
+      },
+      "optionalDependencies": {
+        "source-map": "~0.6.1"
+      }
+    },
+    "node_modules/escodegen/node_modules/levn": {
+      "version": "0.3.0",
+      "resolved": "https://registry.npmjs.org/levn/-/levn-0.3.0.tgz",
+      "integrity": "sha512-0OO4y2iOHix2W6ujICbKIaEQXvFQHue65vUG3pb5EUomzPI90z9hsA1VsO/dbIIpC53J8gxM9Q4Oho0jrCM/yA==",
+      "dev": true,
+      "dependencies": {
+        "prelude-ls": "~1.1.2",
+        "type-check": "~0.3.2"
+      },
+      "engines": {
+        "node": ">= 0.8.0"
+      }
+    },
+    "node_modules/escodegen/node_modules/optionator": {
+      "version": "0.8.3",
+      "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.8.3.tgz",
+      "integrity": "sha512-+IW9pACdk3XWmmTXG8m3upGUJst5XRGzxMRjXzAuJ1XnIFNvfhjjIuYkDvysnPQ7qzqVzLt78BCruntqRhWQbA==",
+      "dev": true,
+      "dependencies": {
+        "deep-is": "~0.1.3",
+        "fast-levenshtein": "~2.0.6",
+        "levn": "~0.3.0",
+        "prelude-ls": "~1.1.2",
+        "type-check": "~0.3.2",
+        "word-wrap": "~1.2.3"
+      },
+      "engines": {
+        "node": ">= 0.8.0"
+      }
+    },
+    "node_modules/escodegen/node_modules/prelude-ls": {
+      "version": "1.1.2",
+      "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.1.2.tgz",
+      "integrity": "sha512-ESF23V4SKG6lVSGZgYNpbsiaAkdab6ZgOxe52p7+Kid3W3u3bxR4Vfd/o21dmN7jSt0IwgZ4v5MUd26FEtXE9w==",
+      "dev": true,
+      "engines": {
+        "node": ">= 0.8.0"
+      }
+    },
+    "node_modules/escodegen/node_modules/type-check": {
+      "version": "0.3.2",
+      "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.3.2.tgz",
+      "integrity": "sha512-ZCmOJdvOWDBYJlzAoFkC+Q0+bUyEOS1ltgp1MGU03fqHG+dbi9tBFU2Rd9QKiDZFAYrhPh2JUf7rZRIuHRKtOg==",
+      "dev": true,
+      "dependencies": {
+        "prelude-ls": "~1.1.2"
+      },
+      "engines": {
+        "node": ">= 0.8.0"
+      }
+    },
     "node_modules/eslint": {
       "version": "7.32.0",
       "resolved": "https://registry.npmjs.org/eslint/-/eslint-7.32.0.tgz",
@@ -12458,6 +12671,19 @@
         "npm": ">=7.0.0"
       }
     },
+    "node_modules/it-protobuf-stream/node_modules/protons-runtime": {
+      "version": "5.0.2",
+      "resolved": "https://registry.npmjs.org/protons-runtime/-/protons-runtime-5.0.2.tgz",
+      "integrity": "sha512-eKppVrIS5dDh+Y61Yj4bDEOs2sQLQbQGIhr7EBiybPQhIMGBynzVXlYILPWl3Td1GDadobc8qevh5D+JwfG9bw==",
+      "dev": true,
+      "dependencies": {
+        "protobufjs": "^7.0.0",
+        "uint8arraylist": "^2.4.3"
+      },
+      "peerDependencies": {
+        "uint8arraylist": "^2.3.2"
+      }
+    },
     "node_modules/it-pushable": {
       "version": "3.2.1",
       "resolved": "https://registry.npmjs.org/it-pushable/-/it-pushable-3.2.1.tgz",
@@ -12584,12 +12810,50 @@
         "js-yaml": "bin/js-yaml.js"
       }
     },
+    "node_modules/js2xmlparser": {
+      "version": "4.0.2",
+      "resolved": "https://registry.npmjs.org/js2xmlparser/-/js2xmlparser-4.0.2.tgz",
+      "integrity": "sha512-6n4D8gLlLf1n5mNLQPRfViYzu9RATblzPEtm1SthMX1Pjao0r9YI9nw7ZIfRxQMERS87mcswrg+r/OYrPRX6jA==",
+      "dev": true,
+      "dependencies": {
+        "xmlcreate": "^2.0.4"
+      }
+    },
     "node_modules/jsbn": {
       "version": "1.1.0",
       "resolved": "https://registry.npmjs.org/jsbn/-/jsbn-1.1.0.tgz",
       "integrity": "sha512-4bYVV3aAMtDTTu4+xsDYa6sy9GyJ69/amsu9sYF2zqjiEoZA5xJi3BrfX3uY+/IekIu7MwdObdbDWpoZdBv3/A==",
       "dev": true
     },
+    "node_modules/jsdoc": {
+      "version": "4.0.2",
+      "resolved": "https://registry.npmjs.org/jsdoc/-/jsdoc-4.0.2.tgz",
+      "integrity": "sha512-e8cIg2z62InH7azBBi3EsSEqrKx+nUtAS5bBcYTSpZFA+vhNPyhv8PTFZ0WsjOPDj04/dOLlm08EDcQJDqaGQg==",
+      "dev": true,
+      "dependencies": {
+        "@babel/parser": "^7.20.15",
+        "@jsdoc/salty": "^0.2.1",
+        "@types/markdown-it": "^12.2.3",
+        "bluebird": "^3.7.2",
+        "catharsis": "^0.9.0",
+        "escape-string-regexp": "^2.0.0",
+        "js2xmlparser": "^4.0.2",
+        "klaw": "^3.0.0",
+        "markdown-it": "^12.3.2",
+        "markdown-it-anchor": "^8.4.1",
+        "marked": "^4.0.10",
+        "mkdirp": "^1.0.4",
+        "requizzle": "^0.2.3",
+        "strip-json-comments": "^3.1.0",
+        "underscore": "~1.13.2"
+      },
+      "bin": {
+        "jsdoc": "jsdoc.js"
+      },
+      "engines": {
+        "node": ">=12.0.0"
+      }
+    },
     "node_modules/jsdoc-type-pratt-parser": {
       "version": "4.0.0",
       "resolved": "https://registry.npmjs.org/jsdoc-type-pratt-parser/-/jsdoc-type-pratt-parser-4.0.0.tgz",
@@ -12599,6 +12863,63 @@
         "node": ">=12.0.0"
       }
     },
+    "node_modules/jsdoc/node_modules/@babel/parser": {
+      "version": "7.22.7",
+      "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.22.7.tgz",
+      "integrity": "sha512-7NF8pOkHP5o2vpmGgNGcfAeCvOYhGLyA3Z4eBQkT1RJlWu47n63bCs93QfJ2hIAFCil7L5P2IWhs1oToVgrL0Q==",
+      "dev": true,
+      "bin": {
+        "parser": "bin/babel-parser.js"
+      },
+      "engines": {
+        "node": ">=6.0.0"
+      }
+    },
+    "node_modules/jsdoc/node_modules/escape-string-regexp": {
+      "version": "2.0.0",
+      "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-2.0.0.tgz",
+      "integrity": "sha512-UpzcLCXolUWcNu5HtVMHYdXJjArjsF9C0aNnquZYY4uW/Vu0miy5YoWvbV345HauVvcAUnpRuhMMcqTcGOY2+w==",
+      "dev": true,
+      "engines": {
+        "node": ">=8"
+      }
+    },
+    "node_modules/jsdoc/node_modules/marked": {
+      "version": "4.3.0",
+      "resolved": "https://registry.npmjs.org/marked/-/marked-4.3.0.tgz",
+      "integrity": "sha512-PRsaiG84bK+AMvxziE/lCFss8juXjNaWzVbN5tXAm4XjeaS9NAHhop+PjQxz2A9h8Q4M/xGmzP8vqNwy6JeK0A==",
+      "dev": true,
+      "bin": {
+        "marked": "bin/marked.js"
+      },
+      "engines": {
+        "node": ">= 12"
+      }
+    },
+    "node_modules/jsdoc/node_modules/mkdirp": {
+      "version": "1.0.4",
+      "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz",
+      "integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==",
+      "dev": true,
+      "bin": {
+        "mkdirp": "bin/cmd.js"
+      },
+      "engines": {
+        "node": ">=10"
+      }
+    },
+    "node_modules/jsdoc/node_modules/strip-json-comments": {
+      "version": "3.1.1",
+      "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz",
+      "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==",
+      "dev": true,
+      "engines": {
+        "node": ">=8"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/sindresorhus"
+      }
+    },
     "node_modules/jsesc": {
       "version": "2.5.2",
       "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-2.5.2.tgz",
@@ -12738,6 +13059,15 @@
         "node": ">=0.10.0"
       }
     },
+    "node_modules/klaw": {
+      "version": "3.0.0",
+      "resolved": "https://registry.npmjs.org/klaw/-/klaw-3.0.0.tgz",
+      "integrity": "sha512-0Fo5oir+O9jnXu5EefYbVK+mHMBeEVEy2cmctR1O1NECcCkPRreJKrS6Qt/j3KC2C148Dfo9i3pCmCMsdqGr0g==",
+      "dev": true,
+      "dependencies": {
+        "graceful-fs": "^4.1.9"
+      }
+    },
     "node_modules/klaw-sync": {
       "version": "6.0.0",
       "resolved": "https://registry.npmjs.org/klaw-sync/-/klaw-sync-6.0.0.tgz",
@@ -12799,6 +13129,15 @@
       "integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==",
       "dev": true
     },
+    "node_modules/linkify-it": {
+      "version": "3.0.3",
+      "resolved": "https://registry.npmjs.org/linkify-it/-/linkify-it-3.0.3.tgz",
+      "integrity": "sha512-ynTsyrFSdE5oZ/O9GEf00kPngmOfVwazR5GKDq6EYfhlpFug3J2zybX56a2PRRpc9P+FuSoGNAwjlbDs9jJBPQ==",
+      "dev": true,
+      "dependencies": {
+        "uc.micro": "^1.0.1"
+      }
+    },
     "node_modules/listr": {
       "version": "0.14.3",
       "resolved": "https://registry.npmjs.org/listr/-/listr-0.14.3.tgz",
@@ -13297,6 +13636,38 @@
         "url": "https://github.com/sponsors/sindresorhus"
       }
     },
+    "node_modules/markdown-it": {
+      "version": "12.3.2",
+      "resolved": "https://registry.npmjs.org/markdown-it/-/markdown-it-12.3.2.tgz",
+      "integrity": "sha512-TchMembfxfNVpHkbtriWltGWc+m3xszaRD0CZup7GFFhzIgQqxIfn3eGj1yZpfuflzPvfkt611B2Q/Bsk1YnGg==",
+      "dev": true,
+      "dependencies": {
+        "argparse": "^2.0.1",
+        "entities": "~2.1.0",
+        "linkify-it": "^3.0.1",
+        "mdurl": "^1.0.1",
+        "uc.micro": "^1.0.5"
+      },
+      "bin": {
+        "markdown-it": "bin/markdown-it.js"
+      }
+    },
+    "node_modules/markdown-it-anchor": {
+      "version": "8.6.7",
+      "resolved": "https://registry.npmjs.org/markdown-it-anchor/-/markdown-it-anchor-8.6.7.tgz",
+      "integrity": "sha512-FlCHFwNnutLgVTflOYHPW2pPcl2AACqVzExlkGQNsi4CJgqOHN7YTgDd4LuhgN1BFO3TS0vLAruV1Td6dwWPJA==",
+      "dev": true,
+      "peerDependencies": {
+        "@types/markdown-it": "*",
+        "markdown-it": "*"
+      }
+    },
+    "node_modules/markdown-it/node_modules/argparse": {
+      "version": "2.0.1",
+      "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz",
+      "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==",
+      "dev": true
+    },
     "node_modules/markdown-table": {
       "version": "3.0.3",
       "resolved": "https://registry.npmjs.org/markdown-table/-/markdown-table-3.0.3.tgz",
@@ -13635,6 +14006,12 @@
         "url": "https://opencollective.com/unified"
       }
     },
+    "node_modules/mdurl": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmjs.org/mdurl/-/mdurl-1.0.1.tgz",
+      "integrity": "sha512-/sKlQJCBYVY9Ers9hqzKou4H6V5UWc/M59TH2dvkt+84itfnq7uFOMLpOiOS4ujvHP4etln18fmIxA5R5fll0g==",
+      "dev": true
+    },
     "node_modules/meow": {
       "version": "8.1.2",
       "resolved": "https://registry.npmjs.org/meow/-/meow-8.1.2.tgz",
@@ -21361,18 +21738,478 @@
         "node": ">=12.0.0"
       }
     },
-    "node_modules/protons-runtime": {
-      "version": "5.0.1",
-      "resolved": "https://registry.npmjs.org/protons-runtime/-/protons-runtime-5.0.1.tgz",
-      "integrity": "sha512-AwyAA3pQ4Ka4tEBMdIjLi/cRdpb322f7sgv3NruVq9yguLggzwu5eeLe1HuRPFYlI4UsVN/QK/AQXjLPVLCzTA==",
+    "node_modules/protobufjs-cli": {
+      "version": "1.1.1",
+      "resolved": "https://registry.npmjs.org/protobufjs-cli/-/protobufjs-cli-1.1.1.tgz",
+      "integrity": "sha512-VPWMgIcRNyQwWUv8OLPyGQ/0lQY/QTQAVN5fh+XzfDwsVw1FZ2L3DM/bcBf8WPiRz2tNpaov9lPZfNcmNo6LXA==",
+      "dev": true,
       "dependencies": {
-        "protobufjs": "^7.0.0",
-        "uint8arraylist": "^2.4.3"
+        "chalk": "^4.0.0",
+        "escodegen": "^1.13.0",
+        "espree": "^9.0.0",
+        "estraverse": "^5.1.0",
+        "glob": "^8.0.0",
+        "jsdoc": "^4.0.0",
+        "minimist": "^1.2.0",
+        "semver": "^7.1.2",
+        "tmp": "^0.2.1",
+        "uglify-js": "^3.7.7"
+      },
+      "bin": {
+        "pbjs": "bin/pbjs",
+        "pbts": "bin/pbts"
+      },
+      "engines": {
+        "node": ">=12.0.0"
+      },
+      "peerDependencies": {
+        "protobufjs": "^7.0.0"
+      }
+    },
+    "node_modules/protobufjs-cli/node_modules/acorn": {
+      "version": "8.10.0",
+      "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.10.0.tgz",
+      "integrity": "sha512-F0SAmZ8iUtS//m8DmCTA0jlh6TDKkHQyK6xc6V4KDTyZKA9dnvX9/3sRTVQrWm79glUAZbnmmNcdYwUIHWVybw==",
+      "dev": true,
+      "bin": {
+        "acorn": "bin/acorn"
+      },
+      "engines": {
+        "node": ">=0.4.0"
+      }
+    },
+    "node_modules/protobufjs-cli/node_modules/ansi-styles": {
+      "version": "4.3.0",
+      "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",
+      "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==",
+      "dev": true,
+      "dependencies": {
+        "color-convert": "^2.0.1"
+      },
+      "engines": {
+        "node": ">=8"
+      },
+      "funding": {
+        "url": "https://github.com/chalk/ansi-styles?sponsor=1"
+      }
+    },
+    "node_modules/protobufjs-cli/node_modules/brace-expansion": {
+      "version": "2.0.1",
+      "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz",
+      "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==",
+      "dev": true,
+      "dependencies": {
+        "balanced-match": "^1.0.0"
+      }
+    },
+    "node_modules/protobufjs-cli/node_modules/chalk": {
+      "version": "4.1.2",
+      "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz",
+      "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==",
+      "dev": true,
+      "dependencies": {
+        "ansi-styles": "^4.1.0",
+        "supports-color": "^7.1.0"
+      },
+      "engines": {
+        "node": ">=10"
+      },
+      "funding": {
+        "url": "https://github.com/chalk/chalk?sponsor=1"
+      }
+    },
+    "node_modules/protobufjs-cli/node_modules/color-convert": {
+      "version": "2.0.1",
+      "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
+      "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
+      "dev": true,
+      "dependencies": {
+        "color-name": "~1.1.4"
+      },
+      "engines": {
+        "node": ">=7.0.0"
+      }
+    },
+    "node_modules/protobufjs-cli/node_modules/color-name": {
+      "version": "1.1.4",
+      "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
+      "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==",
+      "dev": true
+    },
+    "node_modules/protobufjs-cli/node_modules/eslint-visitor-keys": {
+      "version": "3.4.3",
+      "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz",
+      "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==",
+      "dev": true,
+      "engines": {
+        "node": "^12.22.0 || ^14.17.0 || >=16.0.0"
+      },
+      "funding": {
+        "url": "https://opencollective.com/eslint"
+      }
+    },
+    "node_modules/protobufjs-cli/node_modules/espree": {
+      "version": "9.6.1",
+      "resolved": "https://registry.npmjs.org/espree/-/espree-9.6.1.tgz",
+      "integrity": "sha512-oruZaFkjorTpF32kDSI5/75ViwGeZginGGy2NoOSg3Q9bnwlnmDm4HLnkl0RE3n+njDXR037aY1+x58Z/zFdwQ==",
+      "dev": true,
+      "dependencies": {
+        "acorn": "^8.9.0",
+        "acorn-jsx": "^5.3.2",
+        "eslint-visitor-keys": "^3.4.1"
+      },
+      "engines": {
+        "node": "^12.22.0 || ^14.17.0 || >=16.0.0"
+      },
+      "funding": {
+        "url": "https://opencollective.com/eslint"
+      }
+    },
+    "node_modules/protobufjs-cli/node_modules/estraverse": {
+      "version": "5.3.0",
+      "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz",
+      "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==",
+      "dev": true,
+      "engines": {
+        "node": ">=4.0"
+      }
+    },
+    "node_modules/protobufjs-cli/node_modules/glob": {
+      "version": "8.1.0",
+      "resolved": "https://registry.npmjs.org/glob/-/glob-8.1.0.tgz",
+      "integrity": "sha512-r8hpEjiQEYlF2QU0df3dS+nxxSIreXQS1qRhMJM0Q5NDdR386C7jb7Hwwod8Fgiuex+k0GFjgft18yvxm5XoCQ==",
+      "dev": true,
+      "dependencies": {
+        "fs.realpath": "^1.0.0",
+        "inflight": "^1.0.4",
+        "inherits": "2",
+        "minimatch": "^5.0.1",
+        "once": "^1.3.0"
+      },
+      "engines": {
+        "node": ">=12"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/isaacs"
+      }
+    },
+    "node_modules/protobufjs-cli/node_modules/has-flag": {
+      "version": "4.0.0",
+      "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz",
+      "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==",
+      "dev": true,
+      "engines": {
+        "node": ">=8"
+      }
+    },
+    "node_modules/protobufjs-cli/node_modules/minimatch": {
+      "version": "5.1.6",
+      "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.6.tgz",
+      "integrity": "sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g==",
+      "dev": true,
+      "dependencies": {
+        "brace-expansion": "^2.0.1"
+      },
+      "engines": {
+        "node": ">=10"
+      }
+    },
+    "node_modules/protobufjs-cli/node_modules/rimraf": {
+      "version": "3.0.2",
+      "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz",
+      "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==",
+      "dev": true,
+      "dependencies": {
+        "glob": "^7.1.3"
+      },
+      "bin": {
+        "rimraf": "bin.js"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/isaacs"
+      }
+    },
+    "node_modules/protobufjs-cli/node_modules/rimraf/node_modules/brace-expansion": {
+      "version": "1.1.11",
+      "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz",
+      "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==",
+      "dev": true,
+      "dependencies": {
+        "balanced-match": "^1.0.0",
+        "concat-map": "0.0.1"
+      }
+    },
+    "node_modules/protobufjs-cli/node_modules/rimraf/node_modules/glob": {
+      "version": "7.2.3",
+      "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz",
+      "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==",
+      "dev": true,
+      "dependencies": {
+        "fs.realpath": "^1.0.0",
+        "inflight": "^1.0.4",
+        "inherits": "2",
+        "minimatch": "^3.1.1",
+        "once": "^1.3.0",
+        "path-is-absolute": "^1.0.0"
+      },
+      "engines": {
+        "node": "*"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/isaacs"
+      }
+    },
+    "node_modules/protobufjs-cli/node_modules/rimraf/node_modules/minimatch": {
+      "version": "3.1.2",
+      "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz",
+      "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==",
+      "dev": true,
+      "dependencies": {
+        "brace-expansion": "^1.1.7"
+      },
+      "engines": {
+        "node": "*"
+      }
+    },
+    "node_modules/protobufjs-cli/node_modules/semver": {
+      "version": "7.5.4",
+      "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz",
+      "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==",
+      "dev": true,
+      "dependencies": {
+        "lru-cache": "^6.0.0"
+      },
+      "bin": {
+        "semver": "bin/semver.js"
+      },
+      "engines": {
+        "node": ">=10"
+      }
+    },
+    "node_modules/protobufjs-cli/node_modules/supports-color": {
+      "version": "7.2.0",
+      "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz",
+      "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==",
+      "dev": true,
+      "dependencies": {
+        "has-flag": "^4.0.0"
+      },
+      "engines": {
+        "node": ">=8"
+      }
+    },
+    "node_modules/protobufjs-cli/node_modules/tmp": {
+      "version": "0.2.1",
+      "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.2.1.tgz",
+      "integrity": "sha512-76SUhtfqR2Ijn+xllcI5P1oyannHNHByD80W1q447gU3mp9G9PSpGdWmjUOHRDPiHYacIk66W7ubDTuPF3BEtQ==",
+      "dev": true,
+      "dependencies": {
+        "rimraf": "^3.0.0"
+      },
+      "engines": {
+        "node": ">=8.17.0"
+      }
+    },
+    "node_modules/protons": {
+      "version": "5.1.0",
+      "resolved": "https://registry.npmjs.org/protons/-/protons-5.1.0.tgz",
+      "integrity": "sha512-U6FldjrEEIVF4ujIN5VrzAUkGKbYhYUGixy1SxlL38t85EXSnlYIvaHZHmvEdi4WTWyskQVD/zlgQdkRqkU/5Q==",
+      "dev": true,
+      "dependencies": {
+        "meow": "^10.1.2",
+        "protobufjs-cli": "^1.0.0"
+      },
+      "bin": {
+        "protons": "dist/bin/protons.js"
+      },
+      "engines": {
+        "node": ">=16.0.0",
+        "npm": ">=7.0.0"
+      }
+    },
+    "node_modules/protons-runtime": {
+      "version": "3.1.0",
+      "resolved": "https://registry.npmjs.org/protons-runtime/-/protons-runtime-3.1.0.tgz",
+      "integrity": "sha512-S1iSPQC0McdHKJRi0XcATBkWgwWPx46UDfrnshYDXBvGHSYqkFtn4MQ8Gatf67w7FzFtHivA+Hb0ZPq56upG8w==",
+      "dependencies": {
+        "protobufjs": "^7.0.0",
+        "uint8arraylist": "^2.3.2"
+      },
+      "engines": {
+        "node": ">=16.0.0",
+        "npm": ">=7.0.0"
       },
       "peerDependencies": {
         "uint8arraylist": "^2.3.2"
       }
     },
+    "node_modules/protons/node_modules/camelcase-keys": {
+      "version": "7.0.2",
+      "resolved": "https://registry.npmjs.org/camelcase-keys/-/camelcase-keys-7.0.2.tgz",
+      "integrity": "sha512-Rjs1H+A9R+Ig+4E/9oyB66UC5Mj9Xq3N//vcLf2WzgdTi/3gUu3Z9KoqmlrEG4VuuLK8wJHofxzdQXz/knhiYg==",
+      "dev": true,
+      "dependencies": {
+        "camelcase": "^6.3.0",
+        "map-obj": "^4.1.0",
+        "quick-lru": "^5.1.1",
+        "type-fest": "^1.2.1"
+      },
+      "engines": {
+        "node": ">=12"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/sindresorhus"
+      }
+    },
+    "node_modules/protons/node_modules/decamelize": {
+      "version": "5.0.1",
+      "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-5.0.1.tgz",
+      "integrity": "sha512-VfxadyCECXgQlkoEAjeghAr5gY3Hf+IKjKb+X8tGVDtveCjN+USwprd2q3QXBR9T1+x2DG0XZF5/w+7HAtSaXA==",
+      "dev": true,
+      "engines": {
+        "node": ">=10"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/sindresorhus"
+      }
+    },
+    "node_modules/protons/node_modules/indent-string": {
+      "version": "5.0.0",
+      "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-5.0.0.tgz",
+      "integrity": "sha512-m6FAo/spmsW2Ab2fU35JTYwtOKa2yAwXSwgjSv1TJzh4Mh7mC3lzAOVLBprb72XsTrgkEIsl7YrFNAiDiRhIGg==",
+      "dev": true,
+      "engines": {
+        "node": ">=12"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/sindresorhus"
+      }
+    },
+    "node_modules/protons/node_modules/meow": {
+      "version": "10.1.5",
+      "resolved": "https://registry.npmjs.org/meow/-/meow-10.1.5.tgz",
+      "integrity": "sha512-/d+PQ4GKmGvM9Bee/DPa8z3mXs/pkvJE2KEThngVNOqtmljC6K7NMPxtc2JeZYTmpWb9k/TmxjeL18ez3h7vCw==",
+      "dev": true,
+      "dependencies": {
+        "@types/minimist": "^1.2.2",
+        "camelcase-keys": "^7.0.0",
+        "decamelize": "^5.0.0",
+        "decamelize-keys": "^1.1.0",
+        "hard-rejection": "^2.1.0",
+        "minimist-options": "4.1.0",
+        "normalize-package-data": "^3.0.2",
+        "read-pkg-up": "^8.0.0",
+        "redent": "^4.0.0",
+        "trim-newlines": "^4.0.2",
+        "type-fest": "^1.2.2",
+        "yargs-parser": "^20.2.9"
+      },
+      "engines": {
+        "node": "^12.20.0 || ^14.13.1 || >=16.0.0"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/sindresorhus"
+      }
+    },
+    "node_modules/protons/node_modules/read-pkg": {
+      "version": "6.0.0",
+      "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-6.0.0.tgz",
+      "integrity": "sha512-X1Fu3dPuk/8ZLsMhEj5f4wFAF0DWoK7qhGJvgaijocXxBmSToKfbFtqbxMO7bVjNA1dmE5huAzjXj/ey86iw9Q==",
+      "dev": true,
+      "dependencies": {
+        "@types/normalize-package-data": "^2.4.0",
+        "normalize-package-data": "^3.0.2",
+        "parse-json": "^5.2.0",
+        "type-fest": "^1.0.1"
+      },
+      "engines": {
+        "node": ">=12"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/sindresorhus"
+      }
+    },
+    "node_modules/protons/node_modules/read-pkg-up": {
+      "version": "8.0.0",
+      "resolved": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-8.0.0.tgz",
+      "integrity": "sha512-snVCqPczksT0HS2EC+SxUndvSzn6LRCwpfSvLrIfR5BKDQQZMaI6jPRC9dYvYFDRAuFEAnkwww8kBBNE/3VvzQ==",
+      "dev": true,
+      "dependencies": {
+        "find-up": "^5.0.0",
+        "read-pkg": "^6.0.0",
+        "type-fest": "^1.0.1"
+      },
+      "engines": {
+        "node": ">=12"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/sindresorhus"
+      }
+    },
+    "node_modules/protons/node_modules/redent": {
+      "version": "4.0.0",
+      "resolved": "https://registry.npmjs.org/redent/-/redent-4.0.0.tgz",
+      "integrity": "sha512-tYkDkVVtYkSVhuQ4zBgfvciymHaeuel+zFKXShfDnFP5SyVEP7qo70Rf1jTOTCx3vGNAbnEi/xFkcfQVMIBWag==",
+      "dev": true,
+      "dependencies": {
+        "indent-string": "^5.0.0",
+        "strip-indent": "^4.0.0"
+      },
+      "engines": {
+        "node": ">=12"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/sindresorhus"
+      }
+    },
+    "node_modules/protons/node_modules/strip-indent": {
+      "version": "4.0.0",
+      "resolved": "https://registry.npmjs.org/strip-indent/-/strip-indent-4.0.0.tgz",
+      "integrity": "sha512-mnVSV2l+Zv6BLpSD/8V87CW/y9EmmbYzGCIavsnsI6/nwn26DwffM/yztm30Z/I2DY9wdS3vXVCMnHDgZaVNoA==",
+      "dev": true,
+      "dependencies": {
+        "min-indent": "^1.0.1"
+      },
+      "engines": {
+        "node": ">=12"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/sindresorhus"
+      }
+    },
+    "node_modules/protons/node_modules/trim-newlines": {
+      "version": "4.1.1",
+      "resolved": "https://registry.npmjs.org/trim-newlines/-/trim-newlines-4.1.1.tgz",
+      "integrity": "sha512-jRKj0n0jXWo6kh62nA5TEh3+4igKDXLvzBJcPpiizP7oOolUrYIxmVBG9TOtHYFHoddUk6YvAkGeGoSVTXfQXQ==",
+      "dev": true,
+      "engines": {
+        "node": ">=12"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/sindresorhus"
+      }
+    },
+    "node_modules/protons/node_modules/type-fest": {
+      "version": "1.4.0",
+      "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-1.4.0.tgz",
+      "integrity": "sha512-yGSza74xk0UG8k+pLh5oeoYirvIiWo5t0/o3zHHAO2tRDiZcxWP7fywNlXhqb6/r6sWvwi+RsyQMWhVLe4BVuA==",
+      "dev": true,
+      "engines": {
+        "node": ">=10"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/sindresorhus"
+      }
+    },
+    "node_modules/protons/node_modules/yargs-parser": {
+      "version": "20.2.9",
+      "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.9.tgz",
+      "integrity": "sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w==",
+      "dev": true,
+      "engines": {
+        "node": ">=10"
+      }
+    },
     "node_modules/pseudomap": {
       "version": "1.0.2",
       "resolved": "https://registry.npmjs.org/pseudomap/-/pseudomap-1.0.2.tgz",
@@ -22472,6 +23309,15 @@
         "node": ">=0.10.5"
       }
     },
+    "node_modules/requizzle": {
+      "version": "0.2.4",
+      "resolved": "https://registry.npmjs.org/requizzle/-/requizzle-0.2.4.tgz",
+      "integrity": "sha512-JRrFk1D4OQ4SqovXOgdav+K8EAhSB/LJZqCz8tbX0KObcdeM15Ss59ozWMBWmmINMagCwmqn4ZNryUGpBsl6Jw==",
+      "dev": true,
+      "dependencies": {
+        "lodash": "^4.17.21"
+      }
+    },
     "node_modules/resolve": {
       "version": "1.22.3",
       "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.3.tgz",
@@ -25191,12 +26037,17 @@
         "node": ">=8"
       }
     },
+    "node_modules/uc.micro": {
+      "version": "1.0.6",
+      "resolved": "https://registry.npmjs.org/uc.micro/-/uc.micro-1.0.6.tgz",
+      "integrity": "sha512-8Y75pvTYkLJW2hWQHXxoqRgV7qb9B+9vFEtidML+7koHUFapnVJAZ6cKs+Qjz5Aw3aZWHMC6u0wJE3At+nSGwA==",
+      "dev": true
+    },
     "node_modules/uglify-js": {
       "version": "3.17.4",
       "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.17.4.tgz",
       "integrity": "sha512-T9q82TJI9e/C1TAxYvfb16xO120tMVFZrGA3f9/P4424DNu6ypK103y0GPFVa17yotwSyZW5iYXgjYHkGrJW/g==",
       "dev": true,
-      "optional": true,
       "bin": {
         "uglifyjs": "bin/uglifyjs"
       },
@@ -25263,6 +26114,12 @@
         "url": "https://github.com/sponsors/ljharb"
       }
     },
+    "node_modules/underscore": {
+      "version": "1.13.6",
+      "resolved": "https://registry.npmjs.org/underscore/-/underscore-1.13.6.tgz",
+      "integrity": "sha512-+A5Sja4HP1M08MaXya7p5LvjuM7K6q/2EaC0+iovj/wOcMsTzMvDFbasi/oSapiwOlt252IqsKqPjCl7huKS0A==",
+      "dev": true
+    },
     "node_modules/undici": {
       "version": "5.22.1",
       "resolved": "https://registry.npmjs.org/undici/-/undici-5.22.1.tgz",
@@ -25833,6 +26690,15 @@
         "lodash": "^4.17.14"
       }
     },
+    "node_modules/word-wrap": {
+      "version": "1.2.5",
+      "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.5.tgz",
+      "integrity": "sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==",
+      "dev": true,
+      "engines": {
+        "node": ">=0.10.0"
+      }
+    },
     "node_modules/wordwrap": {
       "version": "1.0.0",
       "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-1.0.0.tgz",
@@ -26027,6 +26893,12 @@
         "node": ">=4.0"
       }
     },
+    "node_modules/xmlcreate": {
+      "version": "2.0.4",
+      "resolved": "https://registry.npmjs.org/xmlcreate/-/xmlcreate-2.0.4.tgz",
+      "integrity": "sha512-nquOebG4sngPmGPICTS5EnxqhKbCmz5Ox5hsszI2T6U5qdrJizBc+0ilYSEjTSzU0yZcmvppztXe/5Al5fUwdg==",
+      "dev": true
+    },
     "node_modules/xtend": {
       "version": "4.0.2",
       "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz",
diff --git a/package.json b/package.json
index fc364922..4a4a3b34 100644
--- a/package.json
+++ b/package.json
@@ -44,8 +44,8 @@
   "scripts": {
     "lint": "eslint --ext .ts src test",
     "release": "aegir release --no-types",
-    "copy": "mkdirp dist/src/message && cp src/message/*.* dist/src/message",
-    "build": "npm run copy && aegir build",
+    "build": "aegir build",
+    "generate": "protons ./src/message/rpc.proto",
     "prepare": "npm run build",
     "pretest": "npm run build",
     "pretest:e2e": "npm run build",
@@ -87,6 +87,7 @@
     "multiformats": "^12.0.1",
     "protobufjs": "^7.2.4",
     "uint8arraylist": "^2.4.3",
+    "protons-runtime": "^3.1.0",
     "uint8arrays": "^4.0.4"
   },
   "devDependencies": {
@@ -116,6 +117,7 @@
     "p-wait-for": "^5.0.2",
     "prettier": "^2.0.5",
     "sinon": "^15.1.2",
+    "protons": "^5.1.0",
     "time-cache": "^0.3.0",
     "ts-node": "^10.7.0",
     "ts-sinon": "^2.0.2"
diff --git a/src/index.ts b/src/index.ts
index 8a882beb..57ddaaa1 100644
--- a/src/index.ts
+++ b/src/index.ts
@@ -6,7 +6,7 @@ import type { PeerId } from '@libp2p/interface/peer-id'
 import { CustomEvent, EventEmitter } from '@libp2p/interface/events'
 
 import { MessageCache, type MessageCacheRecord } from './message-cache.js'
-import { RPC, type IRPC } from './message/rpc.js'
+import { RPC } from './message/rpc.js'
 import * as constants from './constants.js'
 import { shuffle, messageIdToString } from './utils/index.js'
 import {
@@ -79,6 +79,7 @@ import type { ConnectionManager } from '@libp2p/interface-internal/connection-ma
 import type { Peer, PeerStore } from '@libp2p/interface/peer-store'
 import type { Multiaddr } from '@multiformats/multiaddr'
 import { multiaddrToIPStr } from './utils/multiaddr.js'
+import { createGossipRpc, ensureControl } from './utils/create-gossip-rpc.js'
 
 type ConnectionDirection = 'inbound' | 'outbound'
 
@@ -293,13 +294,13 @@ export class GossipSub extends EventEmitter<GossipsubEvents> implements PubSub<G
    * Map of pending messages to gossip
    * peer id => control messages
    */
-  public readonly gossip = new Map<PeerIdStr, RPC.IControlIHave[]>()
+  public readonly gossip = new Map<PeerIdStr, RPC.ControlIHave[]>()
 
   /**
    * Map of control messages
    * peer id => control message
    */
-  public readonly control = new Map<PeerIdStr, RPC.IControlMessage>()
+  public readonly control = new Map<PeerIdStr, RPC.ControlMessage>()
 
   /**
    * Number of IHAVEs received from peer in the last heartbeat
@@ -977,7 +978,7 @@ export class GossipSub extends EventEmitter<GossipsubEvents> implements PubSub<G
   /**
    * Handles an rpc request from a peer
    */
-  public async handleReceivedRpc(from: PeerId, rpc: IRPC): Promise<void> {
+  public async handleReceivedRpc(from: PeerId, rpc: RPC): Promise<void> {
     // Check if peer is graylisted in which case we ignore the event
     if (!this.acceptFrom(from.toString())) {
       this.log('received message from unacceptable peer %p', from)
@@ -985,24 +986,24 @@ export class GossipSub extends EventEmitter<GossipsubEvents> implements PubSub<G
       return
     }
 
-    const subscriptions = rpc.subscriptions ? rpc.subscriptions.length : 0
-    const messages = rpc.messages ? rpc.messages.length : 0
+    const subscriptions = rpc.subscriptions.length
+    const messages = rpc.messages.length
     let ihave = 0
     let iwant = 0
     let graft = 0
     let prune = 0
     if (rpc.control) {
-      if (rpc.control.ihave) ihave = rpc.control.ihave.length
-      if (rpc.control.iwant) iwant = rpc.control.iwant.length
-      if (rpc.control.graft) graft = rpc.control.graft.length
-      if (rpc.control.prune) prune = rpc.control.prune.length
+      ihave = rpc.control.ihave.length
+      iwant = rpc.control.iwant.length
+      graft = rpc.control.graft.length
+      prune = rpc.control.prune.length
     }
     this.log(
       `rpc.from ${from.toString()} subscriptions ${subscriptions} messages ${messages} ihave ${ihave} iwant ${iwant} graft ${graft} prune ${prune}`
     )
 
     // Handle received subscriptions
-    if (rpc.subscriptions && rpc.subscriptions.length > 0) {
+    if (rpc.subscriptions.length > 0) {
       // update peer subscriptions
 
       const subscriptions: { topic: TopicStr; subscribe: boolean }[] = []
@@ -1033,24 +1034,22 @@ export class GossipSub extends EventEmitter<GossipsubEvents> implements PubSub<G
 
     // Handle messages
     // TODO: (up to limit)
-    if (rpc.messages) {
-      for (const message of rpc.messages) {
-        if (this.allowedTopics && !this.allowedTopics.has(message.topic)) {
-          // Not allowed: message cache data-structures are not bounded by topic count
-          // TODO: Should apply behaviour penalties?
-          continue
-        }
+    for (const message of rpc.messages) {
+      if (this.allowedTopics && !this.allowedTopics.has(message.topic)) {
+        // Not allowed: message cache data-structures are not bounded by topic count
+        // TODO: Should apply behaviour penalties?
+        continue
+      }
 
-        const handleReceivedMessagePromise = this.handleReceivedMessage(from, message)
-          // Should never throw, but handle just in case
-          .catch((err) => {
-            this.metrics?.onMsgRecvError(message.topic)
-            this.log(err)
-          })
+      const handleReceivedMessagePromise = this.handleReceivedMessage(from, message)
+        // Should never throw, but handle just in case
+        .catch((err) => {
+          this.metrics?.onMsgRecvError(message.topic)
+          this.log(err)
+        })
 
-        if (this.opts.awaitRpcMessageHandler) {
-          await handleReceivedMessagePromise
-        }
+      if (this.opts.awaitRpcMessageHandler) {
+        await handleReceivedMessagePromise
       }
     }
 
@@ -1087,7 +1086,7 @@ export class GossipSub extends EventEmitter<GossipsubEvents> implements PubSub<G
    * Handles a newly received message from an RPC.
    * May forward to all peers in the mesh.
    */
-  private async handleReceivedMessage(from: PeerId, rpcMsg: RPC.IMessage): Promise<void> {
+  private async handleReceivedMessage(from: PeerId, rpcMsg: RPC.Message): Promise<void> {
     this.metrics?.onMsgRecvPreValidation(rpcMsg.topic)
 
     const validationResult = await this.validateReceivedMessage(from, rpcMsg)
@@ -1166,7 +1165,7 @@ export class GossipSub extends EventEmitter<GossipsubEvents> implements PubSub<G
    */
   private async validateReceivedMessage(
     propagationSource: PeerId,
-    rpcMsg: RPC.IMessage
+    rpcMsg: RPC.Message
   ): Promise<ReceivedMessageResult> {
     // Fast message ID stuff
     const fastMsgIdStr = this.fastMsgIdFn?.(rpcMsg)
@@ -1256,35 +1255,34 @@ export class GossipSub extends EventEmitter<GossipsubEvents> implements PubSub<G
    */
   private sendSubscriptions(toPeer: PeerIdStr, topics: string[], subscribe: boolean): void {
     this.sendRpc(toPeer, {
-      subscriptions: topics.map((topic) => ({ topic, subscribe }))
+      subscriptions: topics.map((topic) => ({ topic, subscribe })),
+      messages: []
     })
   }
 
   /**
    * Handles an rpc control message from a peer
    */
-  private async handleControlMessage(id: PeerIdStr, controlMsg: RPC.IControlMessage): Promise<void> {
+  private async handleControlMessage(id: PeerIdStr, controlMsg: RPC.ControlMessage): Promise<void> {
     if (controlMsg === undefined) {
       return
     }
 
-    const iwant = controlMsg.ihave ? this.handleIHave(id, controlMsg.ihave) : []
-    const ihave = controlMsg.iwant ? this.handleIWant(id, controlMsg.iwant) : []
-    const prune = controlMsg.graft ? await this.handleGraft(id, controlMsg.graft) : []
-    controlMsg.prune && (await this.handlePrune(id, controlMsg.prune))
+    const iwant = this.handleIHave(id, controlMsg.ihave)
+    const ihave = this.handleIWant(id, controlMsg.iwant)
+    const prune = await this.handleGraft(id, controlMsg.graft)
+    await this.handlePrune(id, controlMsg.prune)
 
     if (!iwant.length && !ihave.length && !prune.length) {
       return
     }
 
-    const sent = this.sendRpc(id, { messages: ihave, control: { iwant, prune } })
+    const sent = this.sendRpc(id, createGossipRpc(ihave, { iwant, prune }))
     const iwantMessageIds = iwant[0]?.messageIDs
-    if (iwantMessageIds) {
-      if (sent) {
-        this.gossipTracer.addPromise(id, iwantMessageIds)
-      } else {
-        this.metrics?.iwantPromiseUntracked.inc(1)
-      }
+    if (sent) {
+      this.gossipTracer.addPromise(id, iwantMessageIds)
+    } else {
+      this.metrics?.iwantPromiseUntracked.inc(1)
     }
   }
 
@@ -1322,7 +1320,7 @@ export class GossipSub extends EventEmitter<GossipsubEvents> implements PubSub<G
   /**
    * Handles IHAVE messages
    */
-  private handleIHave(id: PeerIdStr, ihave: RPC.IControlIHave[]): RPC.IControlIWant[] {
+  private handleIHave(id: PeerIdStr, ihave: RPC.ControlIHave[]): RPC.ControlIWant[] {
     if (!ihave.length) {
       return []
     }
@@ -1359,7 +1357,7 @@ export class GossipSub extends EventEmitter<GossipsubEvents> implements PubSub<G
     const iwant = new Map<MsgIdStr, Uint8Array>()
 
     ihave.forEach(({ topicID, messageIDs }) => {
-      if (!topicID || !messageIDs || !this.mesh.has(topicID)) {
+      if (!topicID || !this.mesh.has(topicID)) {
         return
       }
 
@@ -1408,7 +1406,7 @@ export class GossipSub extends EventEmitter<GossipsubEvents> implements PubSub<G
    * Handles IWANT messages
    * Returns messages to send back to peer
    */
-  private handleIWant(id: PeerIdStr, iwant: RPC.IControlIWant[]): RPC.IMessage[] {
+  private handleIWant(id: PeerIdStr, iwant: RPC.ControlIWant[]): RPC.Message[] {
     if (!iwant.length) {
       return []
     }
@@ -1420,29 +1418,28 @@ export class GossipSub extends EventEmitter<GossipsubEvents> implements PubSub<G
       return []
     }
 
-    const ihave = new Map<MsgIdStr, RPC.IMessage>()
+    const ihave = new Map<MsgIdStr, RPC.Message>()
     const iwantByTopic = new Map<TopicStr, number>()
     let iwantDonthave = 0
 
     iwant.forEach(({ messageIDs }) => {
-      messageIDs &&
-        messageIDs.forEach((msgId) => {
-          const msgIdStr = this.msgIdToStrFn(msgId)
-          const entry = this.mcache.getWithIWantCount(msgIdStr, id)
-          if (entry == null) {
-            iwantDonthave++
-            return
-          }
+      messageIDs.forEach((msgId) => {
+        const msgIdStr = this.msgIdToStrFn(msgId)
+        const entry = this.mcache.getWithIWantCount(msgIdStr, id)
+        if (entry == null) {
+          iwantDonthave++
+          return
+        }
 
-          iwantByTopic.set(entry.msg.topic, 1 + (iwantByTopic.get(entry.msg.topic) ?? 0))
+        iwantByTopic.set(entry.msg.topic, 1 + (iwantByTopic.get(entry.msg.topic) ?? 0))
 
-          if (entry.count > constants.GossipsubGossipRetransmission) {
-            this.log('IWANT: Peer %s has asked for message %s too many times: ignoring request', id, msgId)
-            return
-          }
+        if (entry.count > constants.GossipsubGossipRetransmission) {
+          this.log('IWANT: Peer %s has asked for message %s too many times: ignoring request', id, msgId)
+          return
+        }
 
-          ihave.set(msgIdStr, entry.msg)
-        })
+        ihave.set(msgIdStr, entry.msg)
+      })
     })
 
     this.metrics?.onIwantRcv(iwantByTopic, iwantDonthave)
@@ -1460,7 +1457,7 @@ export class GossipSub extends EventEmitter<GossipsubEvents> implements PubSub<G
   /**
    * Handles Graft messages
    */
-  private async handleGraft(id: PeerIdStr, graft: RPC.IControlGraft[]): Promise<RPC.IControlPrune[]> {
+  private async handleGraft(id: PeerIdStr, graft: RPC.ControlGraft[]): Promise<RPC.ControlPrune[]> {
     const prune: TopicStr[] = []
     const score = this.score.score(id)
     const now = Date.now()
@@ -1553,7 +1550,7 @@ export class GossipSub extends EventEmitter<GossipsubEvents> implements PubSub<G
   /**
    * Handles Prune messages
    */
-  private async handlePrune(id: PeerIdStr, prune: RPC.IControlPrune[]): Promise<void> {
+  private async handlePrune(id: PeerIdStr, prune: RPC.ControlPrune[]): Promise<void> {
     const score = this.score.score(id)
 
     for (const { topicID, backoff, peers } of prune) {
@@ -1581,7 +1578,7 @@ export class GossipSub extends EventEmitter<GossipsubEvents> implements PubSub<G
       }
 
       // PX
-      if (peers && peers.length) {
+      if (peers.length) {
         // we ignore PX from peers with insufficient scores
         if (score < this.opts.scoreThresholds.acceptPXThreshold) {
           this.log(
@@ -1674,7 +1671,7 @@ export class GossipSub extends EventEmitter<GossipsubEvents> implements PubSub<G
   /**
    * Maybe attempt connection given signed peer records
    */
-  private async pxConnect(peers: RPC.IPeerInfo[]): Promise<void> {
+  private async pxConnect(peers: RPC.PeerInfo[]): Promise<void> {
     if (peers.length > this.opts.prunePeers) {
       shuffle(peers)
       peers = peers.slice(0, this.opts.prunePeers)
@@ -2011,7 +2008,7 @@ export class GossipSub extends EventEmitter<GossipsubEvents> implements PubSub<G
    */
   private forwardMessage(
     msgIdStr: string,
-    rawMsg: RPC.IMessage,
+    rawMsg: RPC.Message,
     propagationSource?: PeerIdStr,
     excludePeers?: Set<PeerIdStr>
   ): void {
@@ -2027,7 +2024,7 @@ export class GossipSub extends EventEmitter<GossipsubEvents> implements PubSub<G
     // forward the message to peers
     tosend.forEach((id) => {
       // sendRpc may mutate RPC message on piggyback, create a new message for each peer
-      this.sendRpc(id, { messages: [rawMsg] })
+      this.sendRpc(id, createGossipRpc([rawMsg]))
     })
 
     this.metrics?.onForwardMsg(rawMsg.topic, tosend.size)
@@ -2089,7 +2086,7 @@ export class GossipSub extends EventEmitter<GossipsubEvents> implements PubSub<G
     // Send to set of peers aggregated from direct, mesh, fanout
     for (const id of tosend) {
       // sendRpc may mutate RPC message on piggyback, create a new message for each peer
-      const sent = this.sendRpc(id, { messages: [rawMsg] })
+      const sent = this.sendRpc(id, createGossipRpc([rawMsg]))
 
       // did not actually send the message
       if (!sent) {
@@ -2196,8 +2193,8 @@ export class GossipSub extends EventEmitter<GossipsubEvents> implements PubSub<G
         topicID: topic
       }
     ]
-
-    this.sendRpc(id, { control: { graft } })
+    const out = createGossipRpc([], { graft })
+    this.sendRpc(id, out)
   }
 
   /**
@@ -2207,14 +2204,14 @@ export class GossipSub extends EventEmitter<GossipsubEvents> implements PubSub<G
     // this is only called from leave() function
     const onUnsubscribe = true
     const prune = [await this.makePrune(id, topic, this.opts.doPX, onUnsubscribe)]
-
-    this.sendRpc(id, { control: { prune } })
+    const out = createGossipRpc([], { prune })
+    this.sendRpc(id, out)
   }
 
   /**
    * Send an rpc object to a peer
    */
-  private sendRpc(id: PeerIdStr, rpc: IRPC): boolean {
+  private sendRpc(id: PeerIdStr, rpc: RPC): boolean {
     const outboundStream = this.streamsOutbound.get(id)
     if (!outboundStream) {
       this.log(`Cannot send RPC to ${id} as there is no open stream to it available`)
@@ -2235,7 +2232,7 @@ export class GossipSub extends EventEmitter<GossipsubEvents> implements PubSub<G
       this.gossip.delete(id)
     }
 
-    const rpcBytes = RPC.encode(rpc).finish()
+    const rpcBytes = RPC.encode(rpc)
     try {
       outboundStream.push(rpcBytes)
     } catch (e) {
@@ -2258,32 +2255,25 @@ export class GossipSub extends EventEmitter<GossipsubEvents> implements PubSub<G
   }
 
   /** Mutates `outRpc` adding graft and prune control messages */
-  public piggybackControl(id: PeerIdStr, outRpc: IRPC, ctrl: RPC.IControlMessage): void {
-    if (ctrl.graft) {
-      if (!outRpc.control) outRpc.control = {}
-      if (!outRpc.control.graft) outRpc.control.graft = []
-      for (const graft of ctrl.graft) {
-        if (graft.topicID && this.mesh.get(graft.topicID)?.has(id)) {
-          outRpc.control.graft.push(graft)
-        }
+  public piggybackControl(id: PeerIdStr, outRpc: RPC, ctrl: RPC.ControlMessage): void {
+    const rpc = ensureControl(outRpc)
+    for (const graft of ctrl.graft) {
+      if (graft.topicID && this.mesh.get(graft.topicID)?.has(id)) {
+        rpc.control.graft.push(graft)
       }
     }
 
-    if (ctrl.prune) {
-      if (!outRpc.control) outRpc.control = {}
-      if (!outRpc.control.prune) outRpc.control.prune = []
-      for (const prune of ctrl.prune) {
-        if (prune.topicID && !this.mesh.get(prune.topicID)?.has(id)) {
-          outRpc.control.prune.push(prune)
-        }
+    for (const prune of ctrl.prune) {
+      if (prune.topicID && !this.mesh.get(prune.topicID)?.has(id)) {
+        rpc.control.prune.push(prune)
       }
     }
   }
 
   /** Mutates `outRpc` adding ihave control messages */
-  private piggybackGossip(id: PeerIdStr, outRpc: IRPC, ihave: RPC.IControlIHave[]): void {
-    if (!outRpc.control) outRpc.control = {}
-    outRpc.control.ihave = ihave
+  private piggybackGossip(id: PeerIdStr, outRpc: RPC, ihave: RPC.ControlIHave[]): void {
+    const rpc = ensureControl(outRpc)
+    rpc.control.ihave = ihave
   }
 
   /**
@@ -2301,7 +2291,7 @@ export class GossipSub extends EventEmitter<GossipsubEvents> implements PubSub<G
     const onUnsubscribe = false
     for (const [id, topics] of tograft) {
       const graft = topics.map((topicID) => ({ topicID }))
-      let prune: RPC.IControlPrune[] = []
+      let prune: RPC.ControlPrune[] = []
       // If a peer also has prunes, process them now
       const pruning = toprune.get(id)
       if (pruning) {
@@ -2313,7 +2303,7 @@ export class GossipSub extends EventEmitter<GossipsubEvents> implements PubSub<G
         toprune.delete(id)
       }
 
-      this.sendRpc(id, { control: { graft, prune } })
+      this.sendRpc(id, createGossipRpc([], { graft, prune }))
     }
     for (const [id, topics] of toprune) {
       const prune = await Promise.all(
@@ -2321,7 +2311,7 @@ export class GossipSub extends EventEmitter<GossipsubEvents> implements PubSub<G
           async (topicID) => await this.makePrune(id, topicID, doPX && !(noPX.get(id) ?? false), onUnsubscribe)
         )
       )
-      this.sendRpc(id, { control: { prune } })
+      this.sendRpc(id, createGossipRpc([], { prune }))
     }
   }
 
@@ -2394,19 +2384,20 @@ export class GossipSub extends EventEmitter<GossipsubEvents> implements PubSub<G
     // send gossip first, which will also piggyback control
     for (const [peer, ihave] of this.gossip.entries()) {
       this.gossip.delete(peer)
-      this.sendRpc(peer, { control: { ihave } })
+      this.sendRpc(peer, createGossipRpc([], { ihave }))
     }
     // send the remaining control messages
     for (const [peer, control] of this.control.entries()) {
       this.control.delete(peer)
-      this.sendRpc(peer, { control: { graft: control.graft, prune: control.prune } })
+      const out = createGossipRpc([], { graft: control.graft, prune: control.prune })
+      this.sendRpc(peer, out)
     }
   }
 
   /**
    * Adds new IHAVE messages to pending gossip
    */
-  private pushGossip(id: PeerIdStr, controlIHaveMsgs: RPC.IControlIHave): void {
+  private pushGossip(id: PeerIdStr, controlIHaveMsgs: RPC.ControlIHave): void {
     this.log('Add gossip to %s', id)
     const gossip = this.gossip.get(id) || []
     this.gossip.set(id, gossip.concat(controlIHaveMsgs))
@@ -2420,7 +2411,7 @@ export class GossipSub extends EventEmitter<GossipsubEvents> implements PubSub<G
     topic: string,
     doPX: boolean,
     onUnsubscribe: boolean
-  ): Promise<RPC.IControlPrune> {
+  ): Promise<RPC.ControlPrune> {
     this.score.prune(id, topic)
     if (this.streamsOutbound.get(id)!.protocol === constants.GossipsubIDv10) {
       // Gossipsub v1.0 -- no backoff, the peer won't be able to parse it anyway
@@ -2433,7 +2424,7 @@ export class GossipSub extends EventEmitter<GossipsubEvents> implements PubSub<G
     // GossipsubPruneBackoff and GossipsubUnsubscribeBackoff are measured in milliseconds
     // The protobuf has it as a uint64
     const backoffMs = onUnsubscribe ? this.opts.unsubcribeBackoff : this.opts.pruneBackoff
-    const backoff = backoffMs / 1000
+    const backoff = BigInt(backoffMs / 1000)
     this.doAddBackoff(id, topic, backoffMs)
 
     if (!doPX) {
diff --git a/src/message-cache.ts b/src/message-cache.ts
index 3544a519..f2449832 100644
--- a/src/message-cache.ts
+++ b/src/message-cache.ts
@@ -8,7 +8,7 @@ export type CacheEntry = MessageId & {
 export type MessageCacheRecord = Pick<MessageCacheEntry, 'message' | 'originatingPeers'>
 
 interface MessageCacheEntry {
-  message: RPC.IMessage
+  message: RPC.Message
   /**
    * Tracks if the message has been validated by the app layer and thus forwarded
    */
@@ -60,7 +60,7 @@ export class MessageCache {
    * Adds a message to the current window and the cache
    * Returns true if the message is not known and is inserted in the cache
    */
-  put(messageId: MessageId, msg: RPC.IMessage, validated = false): boolean {
+  put(messageId: MessageId, msg: RPC.Message, validated = false): boolean {
     const { msgIdStr } = messageId
     // Don't add duplicate entries to the cache.
     if (this.msgs.has(msgIdStr)) {
@@ -99,7 +99,7 @@ export class MessageCache {
   /**
    * Retrieves a message from the cache by its ID, if it is still present
    */
-  get(msgId: Uint8Array): RPC.IMessage | undefined {
+  get(msgId: Uint8Array): RPC.Message | undefined {
     return this.msgs.get(this.msgIdToStrFn(msgId))?.message
   }
 
@@ -107,7 +107,7 @@ export class MessageCache {
    * Increases the iwant count for the given message by one and returns the message together
    * with the iwant if the message exists.
    */
-  getWithIWantCount(msgIdStr: string, p: string): { msg: RPC.IMessage; count: number } | null {
+  getWithIWantCount(msgIdStr: string, p: string): { msg: RPC.Message; count: number } | null {
     const msg = this.msgs.get(msgIdStr)
     if (!msg) {
       return null
diff --git a/src/message/decodeRpc.ts b/src/message/decodeRpc.ts
index 11037930..1cad17fb 100644
--- a/src/message/decodeRpc.ts
+++ b/src/message/decodeRpc.ts
@@ -1,5 +1,5 @@
-import type { IRPC, RPC } from './rpc.js'
-import protobuf from 'protobufjs/minimal.js'
+import type { RPC } from './rpc.js'
+import pb from 'protobufjs'
 
 export type DecodeRPCLimits = {
   maxSubscriptions: number
@@ -20,228 +20,299 @@ export const defaultDecodeRpcLimits: DecodeRPCLimits = {
 }
 
 /**
- * Copied code from src/message/rpc.cjs but with decode limits to prevent OOM attacks
+ * Copied code from src/message/rpc.ts but with decode limits to prevent OOM attacks
  */
-export function decodeRpc(bytes: Uint8Array, opts: DecodeRPCLimits): IRPC {
+export function decodeRpc(bytes: Uint8Array, opts: DecodeRPCLimits): RPC {
   // Mutate to use the option as stateful counter. Must limit the total count of messageIDs across all IWANT, IHAVE
   // else one count put 100 messageIDs into each 100 IWANT and "get around" the limit
   opts = { ...opts }
 
-  const r = protobuf.Reader.create(bytes)
-  const l = bytes.length
+  const reader = pb.Reader.create(bytes)
+  const obj: any = {
+    subscriptions: [],
+    messages: []
+  }
+
+  const end = reader.len
+
+  while (reader.pos < end) {
+    const tag = reader.uint32()
 
-  const c = l === undefined ? r.len : r.pos + l
-  const m: IRPC = {}
-  while (r.pos < c) {
-    const t = r.uint32()
-    switch (t >>> 3) {
+    switch (tag >>> 3) {
       case 1:
-        if (!(m.subscriptions && m.subscriptions.length)) m.subscriptions = []
-        if (m.subscriptions.length < opts.maxSubscriptions) m.subscriptions.push(decodeSubOpts(r, r.uint32()))
-        else r.skipType(t & 7)
+        if (obj.subscriptions.length < opts.maxSubscriptions) {
+          obj.subscriptions.push(decodeSubOpts(reader, reader.uint32()))
+        } else {
+          reader.skipType(tag & 7)
+        }
         break
       case 2:
-        if (!(m.messages && m.messages.length)) m.messages = []
-        if (m.messages.length < opts.maxMessages) m.messages.push(decodeMessage(r, r.uint32()))
-        else r.skipType(t & 7)
+        if (obj.messages.length < opts.maxMessages) {
+          obj.messages.push(decodeMessage(reader, reader.uint32()))
+        } else {
+          reader.skipType(tag & 7)
+        }
         break
       case 3:
-        m.control = decodeControlMessage(r, r.uint32(), opts)
+        obj.control = decodeControlMessage(reader, reader.uint32(), opts)
         break
       default:
-        r.skipType(t & 7)
+        reader.skipType(tag & 7)
         break
     }
   }
-  return m
+
+  return obj
 }
 
-function decodeSubOpts(r: protobuf.Reader, l: number) {
-  const c = l === undefined ? r.len : r.pos + l
-  const m: RPC.ISubOpts = {}
-  while (r.pos < c) {
-    const t = r.uint32()
-    switch (t >>> 3) {
+function decodeSubOpts(reader: pb.Reader, length: number): RPC.SubOpts {
+  const obj: any = {}
+
+  const end = length == null ? reader.len : reader.pos + length
+
+  while (reader.pos < end) {
+    const tag = reader.uint32()
+
+    switch (tag >>> 3) {
       case 1:
-        m.subscribe = r.bool()
+        obj.subscribe = reader.bool()
         break
       case 2:
-        m.topic = r.string()
+        obj.topic = reader.string()
         break
       default:
-        r.skipType(t & 7)
+        reader.skipType(tag & 7)
         break
     }
   }
-  return m
+
+  return obj
 }
 
-function decodeMessage(r: protobuf.Reader, l: number) {
-  const c = l === undefined ? r.len : r.pos + l
-  const m = {} as RPC.IMessage
-  while (r.pos < c) {
-    const t = r.uint32()
-    switch (t >>> 3) {
+function decodeMessage(reader: pb.Reader, length: number): RPC.Message {
+  const obj: any = {
+    topic: ''
+  }
+
+  const end = length == null ? reader.len : reader.pos + length
+
+  while (reader.pos < end) {
+    const tag = reader.uint32()
+
+    switch (tag >>> 3) {
       case 1:
-        m.from = r.bytes()
+        obj.from = reader.bytes()
         break
       case 2:
-        m.data = r.bytes()
+        obj.data = reader.bytes()
         break
       case 3:
-        m.seqno = r.bytes()
+        obj.seqno = reader.bytes()
         break
       case 4:
-        m.topic = r.string()
+        obj.topic = reader.string()
         break
       case 5:
-        m.signature = r.bytes()
+        obj.signature = reader.bytes()
         break
       case 6:
-        m.key = r.bytes()
+        obj.key = reader.bytes()
         break
       default:
-        r.skipType(t & 7)
+        reader.skipType(tag & 7)
         break
     }
   }
-  if (!m.topic) throw Error("missing required 'topic'")
-  return m
+
+  if (obj.topic == null) {
+    throw new Error('Protocol error: value for required field "topic" was not found in protobuf')
+  }
+
+  return obj
 }
 
-function decodeControlMessage(r: protobuf.Reader, l: number, opts: DecodeRPCLimits) {
-  const c = l === undefined ? r.len : r.pos + l
-  const m = {} as RPC.IControlMessage
-  while (r.pos < c) {
-    const t = r.uint32()
-    switch (t >>> 3) {
+function decodeControlMessage(reader: protobuf.Reader, length: number, opts: DecodeRPCLimits): RPC.ControlMessage {
+  const obj: any = {
+    ihave: [],
+    iwant: [],
+    graft: [],
+    prune: []
+  }
+
+  const end = length == null ? reader.len : reader.pos + length
+
+  while (reader.pos < end) {
+    const tag = reader.uint32()
+
+    switch (tag >>> 3) {
       case 1:
-        if (!(m.ihave && m.ihave.length)) m.ihave = []
-        if (m.ihave.length < opts.maxControlMessages) m.ihave.push(decodeControlIHave(r, r.uint32(), opts))
-        else r.skipType(t & 7)
+        if (obj.ihave.length < opts.maxControlMessages) {
+          obj.ihave.push(decodeControlIHave(reader, reader.uint32(), opts))
+        } else {
+          reader.skipType(tag & 7)
+        }
         break
       case 2:
-        if (!(m.iwant && m.iwant.length)) m.iwant = []
-        if (m.iwant.length < opts.maxControlMessages) m.iwant.push(decodeControlIWant(r, r.uint32(), opts))
-        else r.skipType(t & 7)
+        if (obj.iwant.length < opts.maxControlMessages) {
+          obj.iwant.push(decodeControlIWant(reader, reader.uint32(), opts))
+        } else {
+          reader.skipType(tag & 7)
+        }
         break
       case 3:
-        if (!(m.graft && m.graft.length)) m.graft = []
-        if (m.graft.length < opts.maxControlMessages) m.graft.push(decodeControlGraft(r, r.uint32()))
-        else r.skipType(t & 7)
+        if (obj.graft.length < opts.maxControlMessages) {
+          obj.graft.push(decodeControlGraft(reader, reader.uint32()))
+        } else {
+          reader.skipType(tag & 7)
+        }
         break
       case 4:
-        if (!(m.prune && m.prune.length)) m.prune = []
-        if (m.prune.length < opts.maxControlMessages) m.prune.push(decodeControlPrune(r, r.uint32(), opts))
-        else r.skipType(t & 7)
+        if (obj.prune.length < opts.maxControlMessages) {
+          obj.prune.push(decodeControlPrune(reader, reader.uint32(), opts))
+        } else {
+          reader.skipType(tag & 7)
+        }
         break
       default:
-        r.skipType(t & 7)
+        reader.skipType(tag & 7)
         break
     }
   }
-  return m
+
+  return obj
 }
 
-function decodeControlIHave(r: protobuf.Reader, l: number, opts: DecodeRPCLimits) {
-  const c = l === undefined ? r.len : r.pos + l
-  const m = {} as RPC.IControlIHave
-  while (r.pos < c) {
-    const t = r.uint32()
-    switch (t >>> 3) {
+function decodeControlIHave(reader: protobuf.Reader, length: number, opts: DecodeRPCLimits): RPC.ControlIHave {
+  const obj: any = {
+    messageIDs: []
+  }
+
+  const end = length == null ? reader.len : reader.pos + length
+
+  while (reader.pos < end) {
+    const tag = reader.uint32()
+
+    switch (tag >>> 3) {
       case 1:
-        m.topicID = r.string()
+        obj.topicID = reader.string()
         break
       case 2:
-        if (!(m.messageIDs && m.messageIDs.length)) m.messageIDs = []
-        if (opts.maxIhaveMessageIDs-- > 0) m.messageIDs.push(r.bytes())
-        else r.skipType(t & 7)
+        if (opts.maxIhaveMessageIDs-- > 0) {
+          obj.messageIDs.push(reader.bytes())
+        } else {
+          reader.skipType(tag & 7)
+        }
         break
       default:
-        r.skipType(t & 7)
+        reader.skipType(tag & 7)
         break
     }
   }
-  return m
+
+  return obj
 }
 
-function decodeControlIWant(r: protobuf.Reader, l: number, opts: DecodeRPCLimits) {
-  const c = l === undefined ? r.len : r.pos + l
-  const m = {} as RPC.IControlIWant
-  while (r.pos < c) {
-    const t = r.uint32()
-    switch (t >>> 3) {
+function decodeControlIWant(reader: protobuf.Reader, length: number, opts: DecodeRPCLimits): RPC.ControlIWant {
+  const obj: any = {
+    messageIDs: []
+  }
+
+  const end = length == null ? reader.len : reader.pos + length
+
+  while (reader.pos < end) {
+    const tag = reader.uint32()
+
+    switch (tag >>> 3) {
       case 1:
-        if (!(m.messageIDs && m.messageIDs.length)) m.messageIDs = []
-        if (opts.maxIwantMessageIDs-- > 0) m.messageIDs.push(r.bytes())
-        else r.skipType(t & 7)
+        if (opts.maxIwantMessageIDs-- > 0) {
+          obj.messageIDs.push(reader.bytes())
+        } else {
+          reader.skipType(tag & 7)
+        }
         break
       default:
-        r.skipType(t & 7)
+        reader.skipType(tag & 7)
         break
     }
   }
-  return m
+
+  return obj
 }
 
-function decodeControlGraft(r: protobuf.Reader, l: number) {
-  const c = l === undefined ? r.len : r.pos + l
-  const m = {} as RPC.IControlGraft
-  while (r.pos < c) {
-    const t = r.uint32()
-    switch (t >>> 3) {
+function decodeControlGraft(reader: protobuf.Reader, length: number) {
+  const obj: any = {}
+
+  const end = length == null ? reader.len : reader.pos + length
+
+  while (reader.pos < end) {
+    const tag = reader.uint32()
+
+    switch (tag >>> 3) {
       case 1:
-        m.topicID = r.string()
+        obj.topicID = reader.string()
         break
       default:
-        r.skipType(t & 7)
+        reader.skipType(tag & 7)
         break
     }
   }
-  return m
+
+  return obj
 }
 
-function decodeControlPrune(r: protobuf.Reader, l: number, opts: DecodeRPCLimits) {
-  const c = l === undefined ? r.len : r.pos + l
-  const m = {} as RPC.IControlPrune
-  while (r.pos < c) {
-    const t = r.uint32()
-    switch (t >>> 3) {
+function decodeControlPrune(reader: protobuf.Reader, length: number, opts: DecodeRPCLimits): RPC.ControlPrune {
+  const obj: any = {
+    peers: []
+  }
+
+  const end = length == null ? reader.len : reader.pos + length
+
+  while (reader.pos < end) {
+    const tag = reader.uint32()
+
+    switch (tag >>> 3) {
       case 1:
-        m.topicID = r.string()
+        obj.topicID = reader.string()
         break
       case 2:
-        if (!(m.peers && m.peers.length)) m.peers = []
-        if (opts.maxPeerInfos-- > 0) m.peers.push(decodePeerInfo(r, r.uint32()))
-        else r.skipType(t & 7)
+        if (opts.maxPeerInfos-- > 0) {
+          obj.peers.push(decodePeerInfo(reader, reader.uint32()))
+        } else {
+          reader.skipType(tag & 7)
+        }
         break
       case 3:
-        m.backoff = r.uint64() as unknown as number
+        obj.backoff = reader.uint64()
         break
       default:
-        r.skipType(t & 7)
+        reader.skipType(tag & 7)
         break
     }
   }
-  return m
+
+  return obj
 }
 
-function decodePeerInfo(r: protobuf.Reader, l: number) {
-  const c = l === undefined ? r.len : r.pos + l
-  const m = {} as RPC.IPeerInfo
-  while (r.pos < c) {
-    const t = r.uint32()
-    switch (t >>> 3) {
+function decodePeerInfo(reader: protobuf.Reader, length: number): RPC.PeerInfo {
+  const obj: any = {}
+
+  const end = length == null ? reader.len : reader.pos + length
+
+  while (reader.pos < end) {
+    const tag = reader.uint32()
+
+    switch (tag >>> 3) {
       case 1:
-        m.peerID = r.bytes()
+        obj.peerID = reader.bytes()
         break
       case 2:
-        m.signedPeerRecord = r.bytes()
+        obj.signedPeerRecord = reader.bytes()
         break
       default:
-        r.skipType(t & 7)
+        reader.skipType(tag & 7)
         break
     }
   }
-  return m
+
+  return obj
 }
diff --git a/src/message/rpc.cjs b/src/message/rpc.cjs
deleted file mode 100644
index 4f5f4ce6..00000000
--- a/src/message/rpc.cjs
+++ /dev/null
@@ -1,1878 +0,0 @@
-// @ts-nocheck
-/*eslint-disable*/
-(function(global, factory) { /* global define, require, module */
-
-    /* AMD */ if (typeof define === 'function' && define.amd)
-        define(["protobufjs/minimal"], factory);
-
-    /* CommonJS */ else if (typeof require === 'function' && typeof module === 'object' && module && module.exports)
-        module.exports = factory(require("protobufjs/minimal"));
-
-})(this, function($protobuf) {
-    "use strict";
-
-    // Common aliases
-    var $Reader = $protobuf.Reader, $Writer = $protobuf.Writer, $util = $protobuf.util;
-
-    // Exported root namespace
-    var $root = $protobuf.roots["default"] || ($protobuf.roots["default"] = {});
-
-    $root.RPC = (function() {
-
-        /**
-         * Properties of a RPC.
-         * @exports IRPC
-         * @interface IRPC
-         * @property {Array.<RPC.ISubOpts>|null} [subscriptions] RPC subscriptions
-         * @property {Array.<RPC.IMessage>|null} [messages] RPC messages
-         * @property {RPC.IControlMessage|null} [control] RPC control
-         */
-
-        /**
-         * Constructs a new RPC.
-         * @exports RPC
-         * @classdesc Represents a RPC.
-         * @implements IRPC
-         * @constructor
-         * @param {IRPC=} [p] Properties to set
-         */
-        function RPC(p) {
-            this.subscriptions = [];
-            this.messages = [];
-            if (p)
-                for (var ks = Object.keys(p), i = 0; i < ks.length; ++i)
-                    if (p[ks[i]] != null)
-                        this[ks[i]] = p[ks[i]];
-        }
-
-        /**
-         * RPC subscriptions.
-         * @member {Array.<RPC.ISubOpts>} subscriptions
-         * @memberof RPC
-         * @instance
-         */
-        RPC.prototype.subscriptions = $util.emptyArray;
-
-        /**
-         * RPC messages.
-         * @member {Array.<RPC.IMessage>} messages
-         * @memberof RPC
-         * @instance
-         */
-        RPC.prototype.messages = $util.emptyArray;
-
-        /**
-         * RPC control.
-         * @member {RPC.IControlMessage|null|undefined} control
-         * @memberof RPC
-         * @instance
-         */
-        RPC.prototype.control = null;
-
-        // OneOf field names bound to virtual getters and setters
-        var $oneOfFields;
-
-        /**
-         * RPC _control.
-         * @member {"control"|undefined} _control
-         * @memberof RPC
-         * @instance
-         */
-        Object.defineProperty(RPC.prototype, "_control", {
-            get: $util.oneOfGetter($oneOfFields = ["control"]),
-            set: $util.oneOfSetter($oneOfFields)
-        });
-
-        /**
-         * Encodes the specified RPC message. Does not implicitly {@link RPC.verify|verify} messages.
-         * @function encode
-         * @memberof RPC
-         * @static
-         * @param {IRPC} m RPC message or plain object to encode
-         * @param {$protobuf.Writer} [w] Writer to encode to
-         * @returns {$protobuf.Writer} Writer
-         */
-        RPC.encode = function encode(m, w) {
-            if (!w)
-                w = $Writer.create();
-            if (m.subscriptions != null && m.subscriptions.length) {
-                for (var i = 0; i < m.subscriptions.length; ++i)
-                    $root.RPC.SubOpts.encode(m.subscriptions[i], w.uint32(10).fork()).ldelim();
-            }
-            if (m.messages != null && m.messages.length) {
-                for (var i = 0; i < m.messages.length; ++i)
-                    $root.RPC.Message.encode(m.messages[i], w.uint32(18).fork()).ldelim();
-            }
-            if (m.control != null && Object.hasOwnProperty.call(m, "control"))
-                $root.RPC.ControlMessage.encode(m.control, w.uint32(26).fork()).ldelim();
-            return w;
-        };
-
-        /**
-         * Decodes a RPC message from the specified reader or buffer.
-         * @function decode
-         * @memberof RPC
-         * @static
-         * @param {$protobuf.Reader|Uint8Array} r Reader or buffer to decode from
-         * @param {number} [l] Message length if known beforehand
-         * @returns {RPC} RPC
-         * @throws {Error} If the payload is not a reader or valid buffer
-         * @throws {$protobuf.util.ProtocolError} If required fields are missing
-         */
-        RPC.decode = function decode(r, l) {
-            if (!(r instanceof $Reader))
-                r = $Reader.create(r);
-            var c = l === undefined ? r.len : r.pos + l, m = new $root.RPC();
-            while (r.pos < c) {
-                var t = r.uint32();
-                switch (t >>> 3) {
-                case 1:
-                    if (!(m.subscriptions && m.subscriptions.length))
-                        m.subscriptions = [];
-                    m.subscriptions.push($root.RPC.SubOpts.decode(r, r.uint32()));
-                    break;
-                case 2:
-                    if (!(m.messages && m.messages.length))
-                        m.messages = [];
-                    m.messages.push($root.RPC.Message.decode(r, r.uint32()));
-                    break;
-                case 3:
-                    m.control = $root.RPC.ControlMessage.decode(r, r.uint32());
-                    break;
-                default:
-                    r.skipType(t & 7);
-                    break;
-                }
-            }
-            return m;
-        };
-
-        /**
-         * Creates a RPC message from a plain object. Also converts values to their respective internal types.
-         * @function fromObject
-         * @memberof RPC
-         * @static
-         * @param {Object.<string,*>} d Plain object
-         * @returns {RPC} RPC
-         */
-        RPC.fromObject = function fromObject(d) {
-            if (d instanceof $root.RPC)
-                return d;
-            var m = new $root.RPC();
-            if (d.subscriptions) {
-                if (!Array.isArray(d.subscriptions))
-                    throw TypeError(".RPC.subscriptions: array expected");
-                m.subscriptions = [];
-                for (var i = 0; i < d.subscriptions.length; ++i) {
-                    if (typeof d.subscriptions[i] !== "object")
-                        throw TypeError(".RPC.subscriptions: object expected");
-                    m.subscriptions[i] = $root.RPC.SubOpts.fromObject(d.subscriptions[i]);
-                }
-            }
-            if (d.messages) {
-                if (!Array.isArray(d.messages))
-                    throw TypeError(".RPC.messages: array expected");
-                m.messages = [];
-                for (var i = 0; i < d.messages.length; ++i) {
-                    if (typeof d.messages[i] !== "object")
-                        throw TypeError(".RPC.messages: object expected");
-                    m.messages[i] = $root.RPC.Message.fromObject(d.messages[i]);
-                }
-            }
-            if (d.control != null) {
-                if (typeof d.control !== "object")
-                    throw TypeError(".RPC.control: object expected");
-                m.control = $root.RPC.ControlMessage.fromObject(d.control);
-            }
-            return m;
-        };
-
-        /**
-         * Creates a plain object from a RPC message. Also converts values to other types if specified.
-         * @function toObject
-         * @memberof RPC
-         * @static
-         * @param {RPC} m RPC
-         * @param {$protobuf.IConversionOptions} [o] Conversion options
-         * @returns {Object.<string,*>} Plain object
-         */
-        RPC.toObject = function toObject(m, o) {
-            if (!o)
-                o = {};
-            var d = {};
-            if (o.arrays || o.defaults) {
-                d.subscriptions = [];
-                d.messages = [];
-            }
-            if (m.subscriptions && m.subscriptions.length) {
-                d.subscriptions = [];
-                for (var j = 0; j < m.subscriptions.length; ++j) {
-                    d.subscriptions[j] = $root.RPC.SubOpts.toObject(m.subscriptions[j], o);
-                }
-            }
-            if (m.messages && m.messages.length) {
-                d.messages = [];
-                for (var j = 0; j < m.messages.length; ++j) {
-                    d.messages[j] = $root.RPC.Message.toObject(m.messages[j], o);
-                }
-            }
-            if (m.control != null && m.hasOwnProperty("control")) {
-                d.control = $root.RPC.ControlMessage.toObject(m.control, o);
-                if (o.oneofs)
-                    d._control = "control";
-            }
-            return d;
-        };
-
-        /**
-         * Converts this RPC to JSON.
-         * @function toJSON
-         * @memberof RPC
-         * @instance
-         * @returns {Object.<string,*>} JSON object
-         */
-        RPC.prototype.toJSON = function toJSON() {
-            return this.constructor.toObject(this, $protobuf.util.toJSONOptions);
-        };
-
-        RPC.SubOpts = (function() {
-
-            /**
-             * Properties of a SubOpts.
-             * @memberof RPC
-             * @interface ISubOpts
-             * @property {boolean|null} [subscribe] SubOpts subscribe
-             * @property {string|null} [topic] SubOpts topic
-             */
-
-            /**
-             * Constructs a new SubOpts.
-             * @memberof RPC
-             * @classdesc Represents a SubOpts.
-             * @implements ISubOpts
-             * @constructor
-             * @param {RPC.ISubOpts=} [p] Properties to set
-             */
-            function SubOpts(p) {
-                if (p)
-                    for (var ks = Object.keys(p), i = 0; i < ks.length; ++i)
-                        if (p[ks[i]] != null)
-                            this[ks[i]] = p[ks[i]];
-            }
-
-            /**
-             * SubOpts subscribe.
-             * @member {boolean|null|undefined} subscribe
-             * @memberof RPC.SubOpts
-             * @instance
-             */
-            SubOpts.prototype.subscribe = null;
-
-            /**
-             * SubOpts topic.
-             * @member {string|null|undefined} topic
-             * @memberof RPC.SubOpts
-             * @instance
-             */
-            SubOpts.prototype.topic = null;
-
-            // OneOf field names bound to virtual getters and setters
-            var $oneOfFields;
-
-            /**
-             * SubOpts _subscribe.
-             * @member {"subscribe"|undefined} _subscribe
-             * @memberof RPC.SubOpts
-             * @instance
-             */
-            Object.defineProperty(SubOpts.prototype, "_subscribe", {
-                get: $util.oneOfGetter($oneOfFields = ["subscribe"]),
-                set: $util.oneOfSetter($oneOfFields)
-            });
-
-            /**
-             * SubOpts _topic.
-             * @member {"topic"|undefined} _topic
-             * @memberof RPC.SubOpts
-             * @instance
-             */
-            Object.defineProperty(SubOpts.prototype, "_topic", {
-                get: $util.oneOfGetter($oneOfFields = ["topic"]),
-                set: $util.oneOfSetter($oneOfFields)
-            });
-
-            /**
-             * Encodes the specified SubOpts message. Does not implicitly {@link RPC.SubOpts.verify|verify} messages.
-             * @function encode
-             * @memberof RPC.SubOpts
-             * @static
-             * @param {RPC.ISubOpts} m SubOpts message or plain object to encode
-             * @param {$protobuf.Writer} [w] Writer to encode to
-             * @returns {$protobuf.Writer} Writer
-             */
-            SubOpts.encode = function encode(m, w) {
-                if (!w)
-                    w = $Writer.create();
-                if (m.subscribe != null && Object.hasOwnProperty.call(m, "subscribe"))
-                    w.uint32(8).bool(m.subscribe);
-                if (m.topic != null && Object.hasOwnProperty.call(m, "topic"))
-                    w.uint32(18).string(m.topic);
-                return w;
-            };
-
-            /**
-             * Decodes a SubOpts message from the specified reader or buffer.
-             * @function decode
-             * @memberof RPC.SubOpts
-             * @static
-             * @param {$protobuf.Reader|Uint8Array} r Reader or buffer to decode from
-             * @param {number} [l] Message length if known beforehand
-             * @returns {RPC.SubOpts} SubOpts
-             * @throws {Error} If the payload is not a reader or valid buffer
-             * @throws {$protobuf.util.ProtocolError} If required fields are missing
-             */
-            SubOpts.decode = function decode(r, l) {
-                if (!(r instanceof $Reader))
-                    r = $Reader.create(r);
-                var c = l === undefined ? r.len : r.pos + l, m = new $root.RPC.SubOpts();
-                while (r.pos < c) {
-                    var t = r.uint32();
-                    switch (t >>> 3) {
-                    case 1:
-                        m.subscribe = r.bool();
-                        break;
-                    case 2:
-                        m.topic = r.string();
-                        break;
-                    default:
-                        r.skipType(t & 7);
-                        break;
-                    }
-                }
-                return m;
-            };
-
-            /**
-             * Creates a SubOpts message from a plain object. Also converts values to their respective internal types.
-             * @function fromObject
-             * @memberof RPC.SubOpts
-             * @static
-             * @param {Object.<string,*>} d Plain object
-             * @returns {RPC.SubOpts} SubOpts
-             */
-            SubOpts.fromObject = function fromObject(d) {
-                if (d instanceof $root.RPC.SubOpts)
-                    return d;
-                var m = new $root.RPC.SubOpts();
-                if (d.subscribe != null) {
-                    m.subscribe = Boolean(d.subscribe);
-                }
-                if (d.topic != null) {
-                    m.topic = String(d.topic);
-                }
-                return m;
-            };
-
-            /**
-             * Creates a plain object from a SubOpts message. Also converts values to other types if specified.
-             * @function toObject
-             * @memberof RPC.SubOpts
-             * @static
-             * @param {RPC.SubOpts} m SubOpts
-             * @param {$protobuf.IConversionOptions} [o] Conversion options
-             * @returns {Object.<string,*>} Plain object
-             */
-            SubOpts.toObject = function toObject(m, o) {
-                if (!o)
-                    o = {};
-                var d = {};
-                if (m.subscribe != null && m.hasOwnProperty("subscribe")) {
-                    d.subscribe = m.subscribe;
-                    if (o.oneofs)
-                        d._subscribe = "subscribe";
-                }
-                if (m.topic != null && m.hasOwnProperty("topic")) {
-                    d.topic = m.topic;
-                    if (o.oneofs)
-                        d._topic = "topic";
-                }
-                return d;
-            };
-
-            /**
-             * Converts this SubOpts to JSON.
-             * @function toJSON
-             * @memberof RPC.SubOpts
-             * @instance
-             * @returns {Object.<string,*>} JSON object
-             */
-            SubOpts.prototype.toJSON = function toJSON() {
-                return this.constructor.toObject(this, $protobuf.util.toJSONOptions);
-            };
-
-            return SubOpts;
-        })();
-
-        RPC.Message = (function() {
-
-            /**
-             * Properties of a Message.
-             * @memberof RPC
-             * @interface IMessage
-             * @property {Uint8Array|null} [from] Message from
-             * @property {Uint8Array|null} [data] Message data
-             * @property {Uint8Array|null} [seqno] Message seqno
-             * @property {string} topic Message topic
-             * @property {Uint8Array|null} [signature] Message signature
-             * @property {Uint8Array|null} [key] Message key
-             */
-
-            /**
-             * Constructs a new Message.
-             * @memberof RPC
-             * @classdesc Represents a Message.
-             * @implements IMessage
-             * @constructor
-             * @param {RPC.IMessage=} [p] Properties to set
-             */
-            function Message(p) {
-                if (p)
-                    for (var ks = Object.keys(p), i = 0; i < ks.length; ++i)
-                        if (p[ks[i]] != null)
-                            this[ks[i]] = p[ks[i]];
-            }
-
-            /**
-             * Message from.
-             * @member {Uint8Array|null|undefined} from
-             * @memberof RPC.Message
-             * @instance
-             */
-            Message.prototype.from = null;
-
-            /**
-             * Message data.
-             * @member {Uint8Array|null|undefined} data
-             * @memberof RPC.Message
-             * @instance
-             */
-            Message.prototype.data = null;
-
-            /**
-             * Message seqno.
-             * @member {Uint8Array|null|undefined} seqno
-             * @memberof RPC.Message
-             * @instance
-             */
-            Message.prototype.seqno = null;
-
-            /**
-             * Message topic.
-             * @member {string} topic
-             * @memberof RPC.Message
-             * @instance
-             */
-            Message.prototype.topic = "";
-
-            /**
-             * Message signature.
-             * @member {Uint8Array|null|undefined} signature
-             * @memberof RPC.Message
-             * @instance
-             */
-            Message.prototype.signature = null;
-
-            /**
-             * Message key.
-             * @member {Uint8Array|null|undefined} key
-             * @memberof RPC.Message
-             * @instance
-             */
-            Message.prototype.key = null;
-
-            // OneOf field names bound to virtual getters and setters
-            var $oneOfFields;
-
-            /**
-             * Message _from.
-             * @member {"from"|undefined} _from
-             * @memberof RPC.Message
-             * @instance
-             */
-            Object.defineProperty(Message.prototype, "_from", {
-                get: $util.oneOfGetter($oneOfFields = ["from"]),
-                set: $util.oneOfSetter($oneOfFields)
-            });
-
-            /**
-             * Message _data.
-             * @member {"data"|undefined} _data
-             * @memberof RPC.Message
-             * @instance
-             */
-            Object.defineProperty(Message.prototype, "_data", {
-                get: $util.oneOfGetter($oneOfFields = ["data"]),
-                set: $util.oneOfSetter($oneOfFields)
-            });
-
-            /**
-             * Message _seqno.
-             * @member {"seqno"|undefined} _seqno
-             * @memberof RPC.Message
-             * @instance
-             */
-            Object.defineProperty(Message.prototype, "_seqno", {
-                get: $util.oneOfGetter($oneOfFields = ["seqno"]),
-                set: $util.oneOfSetter($oneOfFields)
-            });
-
-            /**
-             * Message _signature.
-             * @member {"signature"|undefined} _signature
-             * @memberof RPC.Message
-             * @instance
-             */
-            Object.defineProperty(Message.prototype, "_signature", {
-                get: $util.oneOfGetter($oneOfFields = ["signature"]),
-                set: $util.oneOfSetter($oneOfFields)
-            });
-
-            /**
-             * Message _key.
-             * @member {"key"|undefined} _key
-             * @memberof RPC.Message
-             * @instance
-             */
-            Object.defineProperty(Message.prototype, "_key", {
-                get: $util.oneOfGetter($oneOfFields = ["key"]),
-                set: $util.oneOfSetter($oneOfFields)
-            });
-
-            /**
-             * Encodes the specified Message message. Does not implicitly {@link RPC.Message.verify|verify} messages.
-             * @function encode
-             * @memberof RPC.Message
-             * @static
-             * @param {RPC.IMessage} m Message message or plain object to encode
-             * @param {$protobuf.Writer} [w] Writer to encode to
-             * @returns {$protobuf.Writer} Writer
-             */
-            Message.encode = function encode(m, w) {
-                if (!w)
-                    w = $Writer.create();
-                if (m.from != null && Object.hasOwnProperty.call(m, "from"))
-                    w.uint32(10).bytes(m.from);
-                if (m.data != null && Object.hasOwnProperty.call(m, "data"))
-                    w.uint32(18).bytes(m.data);
-                if (m.seqno != null && Object.hasOwnProperty.call(m, "seqno"))
-                    w.uint32(26).bytes(m.seqno);
-                w.uint32(34).string(m.topic);
-                if (m.signature != null && Object.hasOwnProperty.call(m, "signature"))
-                    w.uint32(42).bytes(m.signature);
-                if (m.key != null && Object.hasOwnProperty.call(m, "key"))
-                    w.uint32(50).bytes(m.key);
-                return w;
-            };
-
-            /**
-             * Decodes a Message message from the specified reader or buffer.
-             * @function decode
-             * @memberof RPC.Message
-             * @static
-             * @param {$protobuf.Reader|Uint8Array} r Reader or buffer to decode from
-             * @param {number} [l] Message length if known beforehand
-             * @returns {RPC.Message} Message
-             * @throws {Error} If the payload is not a reader or valid buffer
-             * @throws {$protobuf.util.ProtocolError} If required fields are missing
-             */
-            Message.decode = function decode(r, l) {
-                if (!(r instanceof $Reader))
-                    r = $Reader.create(r);
-                var c = l === undefined ? r.len : r.pos + l, m = new $root.RPC.Message();
-                while (r.pos < c) {
-                    var t = r.uint32();
-                    switch (t >>> 3) {
-                    case 1:
-                        m.from = r.bytes();
-                        break;
-                    case 2:
-                        m.data = r.bytes();
-                        break;
-                    case 3:
-                        m.seqno = r.bytes();
-                        break;
-                    case 4:
-                        m.topic = r.string();
-                        break;
-                    case 5:
-                        m.signature = r.bytes();
-                        break;
-                    case 6:
-                        m.key = r.bytes();
-                        break;
-                    default:
-                        r.skipType(t & 7);
-                        break;
-                    }
-                }
-                if (!m.hasOwnProperty("topic"))
-                    throw $util.ProtocolError("missing required 'topic'", { instance: m });
-                return m;
-            };
-
-            /**
-             * Creates a Message message from a plain object. Also converts values to their respective internal types.
-             * @function fromObject
-             * @memberof RPC.Message
-             * @static
-             * @param {Object.<string,*>} d Plain object
-             * @returns {RPC.Message} Message
-             */
-            Message.fromObject = function fromObject(d) {
-                if (d instanceof $root.RPC.Message)
-                    return d;
-                var m = new $root.RPC.Message();
-                if (d.from != null) {
-                    if (typeof d.from === "string")
-                        $util.base64.decode(d.from, m.from = $util.newBuffer($util.base64.length(d.from)), 0);
-                    else if (d.from.length)
-                        m.from = d.from;
-                }
-                if (d.data != null) {
-                    if (typeof d.data === "string")
-                        $util.base64.decode(d.data, m.data = $util.newBuffer($util.base64.length(d.data)), 0);
-                    else if (d.data.length)
-                        m.data = d.data;
-                }
-                if (d.seqno != null) {
-                    if (typeof d.seqno === "string")
-                        $util.base64.decode(d.seqno, m.seqno = $util.newBuffer($util.base64.length(d.seqno)), 0);
-                    else if (d.seqno.length)
-                        m.seqno = d.seqno;
-                }
-                if (d.topic != null) {
-                    m.topic = String(d.topic);
-                }
-                if (d.signature != null) {
-                    if (typeof d.signature === "string")
-                        $util.base64.decode(d.signature, m.signature = $util.newBuffer($util.base64.length(d.signature)), 0);
-                    else if (d.signature.length)
-                        m.signature = d.signature;
-                }
-                if (d.key != null) {
-                    if (typeof d.key === "string")
-                        $util.base64.decode(d.key, m.key = $util.newBuffer($util.base64.length(d.key)), 0);
-                    else if (d.key.length)
-                        m.key = d.key;
-                }
-                return m;
-            };
-
-            /**
-             * Creates a plain object from a Message message. Also converts values to other types if specified.
-             * @function toObject
-             * @memberof RPC.Message
-             * @static
-             * @param {RPC.Message} m Message
-             * @param {$protobuf.IConversionOptions} [o] Conversion options
-             * @returns {Object.<string,*>} Plain object
-             */
-            Message.toObject = function toObject(m, o) {
-                if (!o)
-                    o = {};
-                var d = {};
-                if (o.defaults) {
-                    d.topic = "";
-                }
-                if (m.from != null && m.hasOwnProperty("from")) {
-                    d.from = o.bytes === String ? $util.base64.encode(m.from, 0, m.from.length) : o.bytes === Array ? Array.prototype.slice.call(m.from) : m.from;
-                    if (o.oneofs)
-                        d._from = "from";
-                }
-                if (m.data != null && m.hasOwnProperty("data")) {
-                    d.data = o.bytes === String ? $util.base64.encode(m.data, 0, m.data.length) : o.bytes === Array ? Array.prototype.slice.call(m.data) : m.data;
-                    if (o.oneofs)
-                        d._data = "data";
-                }
-                if (m.seqno != null && m.hasOwnProperty("seqno")) {
-                    d.seqno = o.bytes === String ? $util.base64.encode(m.seqno, 0, m.seqno.length) : o.bytes === Array ? Array.prototype.slice.call(m.seqno) : m.seqno;
-                    if (o.oneofs)
-                        d._seqno = "seqno";
-                }
-                if (m.topic != null && m.hasOwnProperty("topic")) {
-                    d.topic = m.topic;
-                }
-                if (m.signature != null && m.hasOwnProperty("signature")) {
-                    d.signature = o.bytes === String ? $util.base64.encode(m.signature, 0, m.signature.length) : o.bytes === Array ? Array.prototype.slice.call(m.signature) : m.signature;
-                    if (o.oneofs)
-                        d._signature = "signature";
-                }
-                if (m.key != null && m.hasOwnProperty("key")) {
-                    d.key = o.bytes === String ? $util.base64.encode(m.key, 0, m.key.length) : o.bytes === Array ? Array.prototype.slice.call(m.key) : m.key;
-                    if (o.oneofs)
-                        d._key = "key";
-                }
-                return d;
-            };
-
-            /**
-             * Converts this Message to JSON.
-             * @function toJSON
-             * @memberof RPC.Message
-             * @instance
-             * @returns {Object.<string,*>} JSON object
-             */
-            Message.prototype.toJSON = function toJSON() {
-                return this.constructor.toObject(this, $protobuf.util.toJSONOptions);
-            };
-
-            return Message;
-        })();
-
-        RPC.ControlMessage = (function() {
-
-            /**
-             * Properties of a ControlMessage.
-             * @memberof RPC
-             * @interface IControlMessage
-             * @property {Array.<RPC.IControlIHave>|null} [ihave] ControlMessage ihave
-             * @property {Array.<RPC.IControlIWant>|null} [iwant] ControlMessage iwant
-             * @property {Array.<RPC.IControlGraft>|null} [graft] ControlMessage graft
-             * @property {Array.<RPC.IControlPrune>|null} [prune] ControlMessage prune
-             */
-
-            /**
-             * Constructs a new ControlMessage.
-             * @memberof RPC
-             * @classdesc Represents a ControlMessage.
-             * @implements IControlMessage
-             * @constructor
-             * @param {RPC.IControlMessage=} [p] Properties to set
-             */
-            function ControlMessage(p) {
-                this.ihave = [];
-                this.iwant = [];
-                this.graft = [];
-                this.prune = [];
-                if (p)
-                    for (var ks = Object.keys(p), i = 0; i < ks.length; ++i)
-                        if (p[ks[i]] != null)
-                            this[ks[i]] = p[ks[i]];
-            }
-
-            /**
-             * ControlMessage ihave.
-             * @member {Array.<RPC.IControlIHave>} ihave
-             * @memberof RPC.ControlMessage
-             * @instance
-             */
-            ControlMessage.prototype.ihave = $util.emptyArray;
-
-            /**
-             * ControlMessage iwant.
-             * @member {Array.<RPC.IControlIWant>} iwant
-             * @memberof RPC.ControlMessage
-             * @instance
-             */
-            ControlMessage.prototype.iwant = $util.emptyArray;
-
-            /**
-             * ControlMessage graft.
-             * @member {Array.<RPC.IControlGraft>} graft
-             * @memberof RPC.ControlMessage
-             * @instance
-             */
-            ControlMessage.prototype.graft = $util.emptyArray;
-
-            /**
-             * ControlMessage prune.
-             * @member {Array.<RPC.IControlPrune>} prune
-             * @memberof RPC.ControlMessage
-             * @instance
-             */
-            ControlMessage.prototype.prune = $util.emptyArray;
-
-            /**
-             * Encodes the specified ControlMessage message. Does not implicitly {@link RPC.ControlMessage.verify|verify} messages.
-             * @function encode
-             * @memberof RPC.ControlMessage
-             * @static
-             * @param {RPC.IControlMessage} m ControlMessage message or plain object to encode
-             * @param {$protobuf.Writer} [w] Writer to encode to
-             * @returns {$protobuf.Writer} Writer
-             */
-            ControlMessage.encode = function encode(m, w) {
-                if (!w)
-                    w = $Writer.create();
-                if (m.ihave != null && m.ihave.length) {
-                    for (var i = 0; i < m.ihave.length; ++i)
-                        $root.RPC.ControlIHave.encode(m.ihave[i], w.uint32(10).fork()).ldelim();
-                }
-                if (m.iwant != null && m.iwant.length) {
-                    for (var i = 0; i < m.iwant.length; ++i)
-                        $root.RPC.ControlIWant.encode(m.iwant[i], w.uint32(18).fork()).ldelim();
-                }
-                if (m.graft != null && m.graft.length) {
-                    for (var i = 0; i < m.graft.length; ++i)
-                        $root.RPC.ControlGraft.encode(m.graft[i], w.uint32(26).fork()).ldelim();
-                }
-                if (m.prune != null && m.prune.length) {
-                    for (var i = 0; i < m.prune.length; ++i)
-                        $root.RPC.ControlPrune.encode(m.prune[i], w.uint32(34).fork()).ldelim();
-                }
-                return w;
-            };
-
-            /**
-             * Decodes a ControlMessage message from the specified reader or buffer.
-             * @function decode
-             * @memberof RPC.ControlMessage
-             * @static
-             * @param {$protobuf.Reader|Uint8Array} r Reader or buffer to decode from
-             * @param {number} [l] Message length if known beforehand
-             * @returns {RPC.ControlMessage} ControlMessage
-             * @throws {Error} If the payload is not a reader or valid buffer
-             * @throws {$protobuf.util.ProtocolError} If required fields are missing
-             */
-            ControlMessage.decode = function decode(r, l) {
-                if (!(r instanceof $Reader))
-                    r = $Reader.create(r);
-                var c = l === undefined ? r.len : r.pos + l, m = new $root.RPC.ControlMessage();
-                while (r.pos < c) {
-                    var t = r.uint32();
-                    switch (t >>> 3) {
-                    case 1:
-                        if (!(m.ihave && m.ihave.length))
-                            m.ihave = [];
-                        m.ihave.push($root.RPC.ControlIHave.decode(r, r.uint32()));
-                        break;
-                    case 2:
-                        if (!(m.iwant && m.iwant.length))
-                            m.iwant = [];
-                        m.iwant.push($root.RPC.ControlIWant.decode(r, r.uint32()));
-                        break;
-                    case 3:
-                        if (!(m.graft && m.graft.length))
-                            m.graft = [];
-                        m.graft.push($root.RPC.ControlGraft.decode(r, r.uint32()));
-                        break;
-                    case 4:
-                        if (!(m.prune && m.prune.length))
-                            m.prune = [];
-                        m.prune.push($root.RPC.ControlPrune.decode(r, r.uint32()));
-                        break;
-                    default:
-                        r.skipType(t & 7);
-                        break;
-                    }
-                }
-                return m;
-            };
-
-            /**
-             * Creates a ControlMessage message from a plain object. Also converts values to their respective internal types.
-             * @function fromObject
-             * @memberof RPC.ControlMessage
-             * @static
-             * @param {Object.<string,*>} d Plain object
-             * @returns {RPC.ControlMessage} ControlMessage
-             */
-            ControlMessage.fromObject = function fromObject(d) {
-                if (d instanceof $root.RPC.ControlMessage)
-                    return d;
-                var m = new $root.RPC.ControlMessage();
-                if (d.ihave) {
-                    if (!Array.isArray(d.ihave))
-                        throw TypeError(".RPC.ControlMessage.ihave: array expected");
-                    m.ihave = [];
-                    for (var i = 0; i < d.ihave.length; ++i) {
-                        if (typeof d.ihave[i] !== "object")
-                            throw TypeError(".RPC.ControlMessage.ihave: object expected");
-                        m.ihave[i] = $root.RPC.ControlIHave.fromObject(d.ihave[i]);
-                    }
-                }
-                if (d.iwant) {
-                    if (!Array.isArray(d.iwant))
-                        throw TypeError(".RPC.ControlMessage.iwant: array expected");
-                    m.iwant = [];
-                    for (var i = 0; i < d.iwant.length; ++i) {
-                        if (typeof d.iwant[i] !== "object")
-                            throw TypeError(".RPC.ControlMessage.iwant: object expected");
-                        m.iwant[i] = $root.RPC.ControlIWant.fromObject(d.iwant[i]);
-                    }
-                }
-                if (d.graft) {
-                    if (!Array.isArray(d.graft))
-                        throw TypeError(".RPC.ControlMessage.graft: array expected");
-                    m.graft = [];
-                    for (var i = 0; i < d.graft.length; ++i) {
-                        if (typeof d.graft[i] !== "object")
-                            throw TypeError(".RPC.ControlMessage.graft: object expected");
-                        m.graft[i] = $root.RPC.ControlGraft.fromObject(d.graft[i]);
-                    }
-                }
-                if (d.prune) {
-                    if (!Array.isArray(d.prune))
-                        throw TypeError(".RPC.ControlMessage.prune: array expected");
-                    m.prune = [];
-                    for (var i = 0; i < d.prune.length; ++i) {
-                        if (typeof d.prune[i] !== "object")
-                            throw TypeError(".RPC.ControlMessage.prune: object expected");
-                        m.prune[i] = $root.RPC.ControlPrune.fromObject(d.prune[i]);
-                    }
-                }
-                return m;
-            };
-
-            /**
-             * Creates a plain object from a ControlMessage message. Also converts values to other types if specified.
-             * @function toObject
-             * @memberof RPC.ControlMessage
-             * @static
-             * @param {RPC.ControlMessage} m ControlMessage
-             * @param {$protobuf.IConversionOptions} [o] Conversion options
-             * @returns {Object.<string,*>} Plain object
-             */
-            ControlMessage.toObject = function toObject(m, o) {
-                if (!o)
-                    o = {};
-                var d = {};
-                if (o.arrays || o.defaults) {
-                    d.ihave = [];
-                    d.iwant = [];
-                    d.graft = [];
-                    d.prune = [];
-                }
-                if (m.ihave && m.ihave.length) {
-                    d.ihave = [];
-                    for (var j = 0; j < m.ihave.length; ++j) {
-                        d.ihave[j] = $root.RPC.ControlIHave.toObject(m.ihave[j], o);
-                    }
-                }
-                if (m.iwant && m.iwant.length) {
-                    d.iwant = [];
-                    for (var j = 0; j < m.iwant.length; ++j) {
-                        d.iwant[j] = $root.RPC.ControlIWant.toObject(m.iwant[j], o);
-                    }
-                }
-                if (m.graft && m.graft.length) {
-                    d.graft = [];
-                    for (var j = 0; j < m.graft.length; ++j) {
-                        d.graft[j] = $root.RPC.ControlGraft.toObject(m.graft[j], o);
-                    }
-                }
-                if (m.prune && m.prune.length) {
-                    d.prune = [];
-                    for (var j = 0; j < m.prune.length; ++j) {
-                        d.prune[j] = $root.RPC.ControlPrune.toObject(m.prune[j], o);
-                    }
-                }
-                return d;
-            };
-
-            /**
-             * Converts this ControlMessage to JSON.
-             * @function toJSON
-             * @memberof RPC.ControlMessage
-             * @instance
-             * @returns {Object.<string,*>} JSON object
-             */
-            ControlMessage.prototype.toJSON = function toJSON() {
-                return this.constructor.toObject(this, $protobuf.util.toJSONOptions);
-            };
-
-            return ControlMessage;
-        })();
-
-        RPC.ControlIHave = (function() {
-
-            /**
-             * Properties of a ControlIHave.
-             * @memberof RPC
-             * @interface IControlIHave
-             * @property {string|null} [topicID] ControlIHave topicID
-             * @property {Array.<Uint8Array>|null} [messageIDs] ControlIHave messageIDs
-             */
-
-            /**
-             * Constructs a new ControlIHave.
-             * @memberof RPC
-             * @classdesc Represents a ControlIHave.
-             * @implements IControlIHave
-             * @constructor
-             * @param {RPC.IControlIHave=} [p] Properties to set
-             */
-            function ControlIHave(p) {
-                this.messageIDs = [];
-                if (p)
-                    for (var ks = Object.keys(p), i = 0; i < ks.length; ++i)
-                        if (p[ks[i]] != null)
-                            this[ks[i]] = p[ks[i]];
-            }
-
-            /**
-             * ControlIHave topicID.
-             * @member {string|null|undefined} topicID
-             * @memberof RPC.ControlIHave
-             * @instance
-             */
-            ControlIHave.prototype.topicID = null;
-
-            /**
-             * ControlIHave messageIDs.
-             * @member {Array.<Uint8Array>} messageIDs
-             * @memberof RPC.ControlIHave
-             * @instance
-             */
-            ControlIHave.prototype.messageIDs = $util.emptyArray;
-
-            // OneOf field names bound to virtual getters and setters
-            var $oneOfFields;
-
-            /**
-             * ControlIHave _topicID.
-             * @member {"topicID"|undefined} _topicID
-             * @memberof RPC.ControlIHave
-             * @instance
-             */
-            Object.defineProperty(ControlIHave.prototype, "_topicID", {
-                get: $util.oneOfGetter($oneOfFields = ["topicID"]),
-                set: $util.oneOfSetter($oneOfFields)
-            });
-
-            /**
-             * Encodes the specified ControlIHave message. Does not implicitly {@link RPC.ControlIHave.verify|verify} messages.
-             * @function encode
-             * @memberof RPC.ControlIHave
-             * @static
-             * @param {RPC.IControlIHave} m ControlIHave message or plain object to encode
-             * @param {$protobuf.Writer} [w] Writer to encode to
-             * @returns {$protobuf.Writer} Writer
-             */
-            ControlIHave.encode = function encode(m, w) {
-                if (!w)
-                    w = $Writer.create();
-                if (m.topicID != null && Object.hasOwnProperty.call(m, "topicID"))
-                    w.uint32(10).string(m.topicID);
-                if (m.messageIDs != null && m.messageIDs.length) {
-                    for (var i = 0; i < m.messageIDs.length; ++i)
-                        w.uint32(18).bytes(m.messageIDs[i]);
-                }
-                return w;
-            };
-
-            /**
-             * Decodes a ControlIHave message from the specified reader or buffer.
-             * @function decode
-             * @memberof RPC.ControlIHave
-             * @static
-             * @param {$protobuf.Reader|Uint8Array} r Reader or buffer to decode from
-             * @param {number} [l] Message length if known beforehand
-             * @returns {RPC.ControlIHave} ControlIHave
-             * @throws {Error} If the payload is not a reader or valid buffer
-             * @throws {$protobuf.util.ProtocolError} If required fields are missing
-             */
-            ControlIHave.decode = function decode(r, l) {
-                if (!(r instanceof $Reader))
-                    r = $Reader.create(r);
-                var c = l === undefined ? r.len : r.pos + l, m = new $root.RPC.ControlIHave();
-                while (r.pos < c) {
-                    var t = r.uint32();
-                    switch (t >>> 3) {
-                    case 1:
-                        m.topicID = r.string();
-                        break;
-                    case 2:
-                        if (!(m.messageIDs && m.messageIDs.length))
-                            m.messageIDs = [];
-                        m.messageIDs.push(r.bytes());
-                        break;
-                    default:
-                        r.skipType(t & 7);
-                        break;
-                    }
-                }
-                return m;
-            };
-
-            /**
-             * Creates a ControlIHave message from a plain object. Also converts values to their respective internal types.
-             * @function fromObject
-             * @memberof RPC.ControlIHave
-             * @static
-             * @param {Object.<string,*>} d Plain object
-             * @returns {RPC.ControlIHave} ControlIHave
-             */
-            ControlIHave.fromObject = function fromObject(d) {
-                if (d instanceof $root.RPC.ControlIHave)
-                    return d;
-                var m = new $root.RPC.ControlIHave();
-                if (d.topicID != null) {
-                    m.topicID = String(d.topicID);
-                }
-                if (d.messageIDs) {
-                    if (!Array.isArray(d.messageIDs))
-                        throw TypeError(".RPC.ControlIHave.messageIDs: array expected");
-                    m.messageIDs = [];
-                    for (var i = 0; i < d.messageIDs.length; ++i) {
-                        if (typeof d.messageIDs[i] === "string")
-                            $util.base64.decode(d.messageIDs[i], m.messageIDs[i] = $util.newBuffer($util.base64.length(d.messageIDs[i])), 0);
-                        else if (d.messageIDs[i].length)
-                            m.messageIDs[i] = d.messageIDs[i];
-                    }
-                }
-                return m;
-            };
-
-            /**
-             * Creates a plain object from a ControlIHave message. Also converts values to other types if specified.
-             * @function toObject
-             * @memberof RPC.ControlIHave
-             * @static
-             * @param {RPC.ControlIHave} m ControlIHave
-             * @param {$protobuf.IConversionOptions} [o] Conversion options
-             * @returns {Object.<string,*>} Plain object
-             */
-            ControlIHave.toObject = function toObject(m, o) {
-                if (!o)
-                    o = {};
-                var d = {};
-                if (o.arrays || o.defaults) {
-                    d.messageIDs = [];
-                }
-                if (m.topicID != null && m.hasOwnProperty("topicID")) {
-                    d.topicID = m.topicID;
-                    if (o.oneofs)
-                        d._topicID = "topicID";
-                }
-                if (m.messageIDs && m.messageIDs.length) {
-                    d.messageIDs = [];
-                    for (var j = 0; j < m.messageIDs.length; ++j) {
-                        d.messageIDs[j] = o.bytes === String ? $util.base64.encode(m.messageIDs[j], 0, m.messageIDs[j].length) : o.bytes === Array ? Array.prototype.slice.call(m.messageIDs[j]) : m.messageIDs[j];
-                    }
-                }
-                return d;
-            };
-
-            /**
-             * Converts this ControlIHave to JSON.
-             * @function toJSON
-             * @memberof RPC.ControlIHave
-             * @instance
-             * @returns {Object.<string,*>} JSON object
-             */
-            ControlIHave.prototype.toJSON = function toJSON() {
-                return this.constructor.toObject(this, $protobuf.util.toJSONOptions);
-            };
-
-            return ControlIHave;
-        })();
-
-        RPC.ControlIWant = (function() {
-
-            /**
-             * Properties of a ControlIWant.
-             * @memberof RPC
-             * @interface IControlIWant
-             * @property {Array.<Uint8Array>|null} [messageIDs] ControlIWant messageIDs
-             */
-
-            /**
-             * Constructs a new ControlIWant.
-             * @memberof RPC
-             * @classdesc Represents a ControlIWant.
-             * @implements IControlIWant
-             * @constructor
-             * @param {RPC.IControlIWant=} [p] Properties to set
-             */
-            function ControlIWant(p) {
-                this.messageIDs = [];
-                if (p)
-                    for (var ks = Object.keys(p), i = 0; i < ks.length; ++i)
-                        if (p[ks[i]] != null)
-                            this[ks[i]] = p[ks[i]];
-            }
-
-            /**
-             * ControlIWant messageIDs.
-             * @member {Array.<Uint8Array>} messageIDs
-             * @memberof RPC.ControlIWant
-             * @instance
-             */
-            ControlIWant.prototype.messageIDs = $util.emptyArray;
-
-            /**
-             * Encodes the specified ControlIWant message. Does not implicitly {@link RPC.ControlIWant.verify|verify} messages.
-             * @function encode
-             * @memberof RPC.ControlIWant
-             * @static
-             * @param {RPC.IControlIWant} m ControlIWant message or plain object to encode
-             * @param {$protobuf.Writer} [w] Writer to encode to
-             * @returns {$protobuf.Writer} Writer
-             */
-            ControlIWant.encode = function encode(m, w) {
-                if (!w)
-                    w = $Writer.create();
-                if (m.messageIDs != null && m.messageIDs.length) {
-                    for (var i = 0; i < m.messageIDs.length; ++i)
-                        w.uint32(10).bytes(m.messageIDs[i]);
-                }
-                return w;
-            };
-
-            /**
-             * Decodes a ControlIWant message from the specified reader or buffer.
-             * @function decode
-             * @memberof RPC.ControlIWant
-             * @static
-             * @param {$protobuf.Reader|Uint8Array} r Reader or buffer to decode from
-             * @param {number} [l] Message length if known beforehand
-             * @returns {RPC.ControlIWant} ControlIWant
-             * @throws {Error} If the payload is not a reader or valid buffer
-             * @throws {$protobuf.util.ProtocolError} If required fields are missing
-             */
-            ControlIWant.decode = function decode(r, l) {
-                if (!(r instanceof $Reader))
-                    r = $Reader.create(r);
-                var c = l === undefined ? r.len : r.pos + l, m = new $root.RPC.ControlIWant();
-                while (r.pos < c) {
-                    var t = r.uint32();
-                    switch (t >>> 3) {
-                    case 1:
-                        if (!(m.messageIDs && m.messageIDs.length))
-                            m.messageIDs = [];
-                        m.messageIDs.push(r.bytes());
-                        break;
-                    default:
-                        r.skipType(t & 7);
-                        break;
-                    }
-                }
-                return m;
-            };
-
-            /**
-             * Creates a ControlIWant message from a plain object. Also converts values to their respective internal types.
-             * @function fromObject
-             * @memberof RPC.ControlIWant
-             * @static
-             * @param {Object.<string,*>} d Plain object
-             * @returns {RPC.ControlIWant} ControlIWant
-             */
-            ControlIWant.fromObject = function fromObject(d) {
-                if (d instanceof $root.RPC.ControlIWant)
-                    return d;
-                var m = new $root.RPC.ControlIWant();
-                if (d.messageIDs) {
-                    if (!Array.isArray(d.messageIDs))
-                        throw TypeError(".RPC.ControlIWant.messageIDs: array expected");
-                    m.messageIDs = [];
-                    for (var i = 0; i < d.messageIDs.length; ++i) {
-                        if (typeof d.messageIDs[i] === "string")
-                            $util.base64.decode(d.messageIDs[i], m.messageIDs[i] = $util.newBuffer($util.base64.length(d.messageIDs[i])), 0);
-                        else if (d.messageIDs[i].length)
-                            m.messageIDs[i] = d.messageIDs[i];
-                    }
-                }
-                return m;
-            };
-
-            /**
-             * Creates a plain object from a ControlIWant message. Also converts values to other types if specified.
-             * @function toObject
-             * @memberof RPC.ControlIWant
-             * @static
-             * @param {RPC.ControlIWant} m ControlIWant
-             * @param {$protobuf.IConversionOptions} [o] Conversion options
-             * @returns {Object.<string,*>} Plain object
-             */
-            ControlIWant.toObject = function toObject(m, o) {
-                if (!o)
-                    o = {};
-                var d = {};
-                if (o.arrays || o.defaults) {
-                    d.messageIDs = [];
-                }
-                if (m.messageIDs && m.messageIDs.length) {
-                    d.messageIDs = [];
-                    for (var j = 0; j < m.messageIDs.length; ++j) {
-                        d.messageIDs[j] = o.bytes === String ? $util.base64.encode(m.messageIDs[j], 0, m.messageIDs[j].length) : o.bytes === Array ? Array.prototype.slice.call(m.messageIDs[j]) : m.messageIDs[j];
-                    }
-                }
-                return d;
-            };
-
-            /**
-             * Converts this ControlIWant to JSON.
-             * @function toJSON
-             * @memberof RPC.ControlIWant
-             * @instance
-             * @returns {Object.<string,*>} JSON object
-             */
-            ControlIWant.prototype.toJSON = function toJSON() {
-                return this.constructor.toObject(this, $protobuf.util.toJSONOptions);
-            };
-
-            return ControlIWant;
-        })();
-
-        RPC.ControlGraft = (function() {
-
-            /**
-             * Properties of a ControlGraft.
-             * @memberof RPC
-             * @interface IControlGraft
-             * @property {string|null} [topicID] ControlGraft topicID
-             */
-
-            /**
-             * Constructs a new ControlGraft.
-             * @memberof RPC
-             * @classdesc Represents a ControlGraft.
-             * @implements IControlGraft
-             * @constructor
-             * @param {RPC.IControlGraft=} [p] Properties to set
-             */
-            function ControlGraft(p) {
-                if (p)
-                    for (var ks = Object.keys(p), i = 0; i < ks.length; ++i)
-                        if (p[ks[i]] != null)
-                            this[ks[i]] = p[ks[i]];
-            }
-
-            /**
-             * ControlGraft topicID.
-             * @member {string|null|undefined} topicID
-             * @memberof RPC.ControlGraft
-             * @instance
-             */
-            ControlGraft.prototype.topicID = null;
-
-            // OneOf field names bound to virtual getters and setters
-            var $oneOfFields;
-
-            /**
-             * ControlGraft _topicID.
-             * @member {"topicID"|undefined} _topicID
-             * @memberof RPC.ControlGraft
-             * @instance
-             */
-            Object.defineProperty(ControlGraft.prototype, "_topicID", {
-                get: $util.oneOfGetter($oneOfFields = ["topicID"]),
-                set: $util.oneOfSetter($oneOfFields)
-            });
-
-            /**
-             * Encodes the specified ControlGraft message. Does not implicitly {@link RPC.ControlGraft.verify|verify} messages.
-             * @function encode
-             * @memberof RPC.ControlGraft
-             * @static
-             * @param {RPC.IControlGraft} m ControlGraft message or plain object to encode
-             * @param {$protobuf.Writer} [w] Writer to encode to
-             * @returns {$protobuf.Writer} Writer
-             */
-            ControlGraft.encode = function encode(m, w) {
-                if (!w)
-                    w = $Writer.create();
-                if (m.topicID != null && Object.hasOwnProperty.call(m, "topicID"))
-                    w.uint32(10).string(m.topicID);
-                return w;
-            };
-
-            /**
-             * Decodes a ControlGraft message from the specified reader or buffer.
-             * @function decode
-             * @memberof RPC.ControlGraft
-             * @static
-             * @param {$protobuf.Reader|Uint8Array} r Reader or buffer to decode from
-             * @param {number} [l] Message length if known beforehand
-             * @returns {RPC.ControlGraft} ControlGraft
-             * @throws {Error} If the payload is not a reader or valid buffer
-             * @throws {$protobuf.util.ProtocolError} If required fields are missing
-             */
-            ControlGraft.decode = function decode(r, l) {
-                if (!(r instanceof $Reader))
-                    r = $Reader.create(r);
-                var c = l === undefined ? r.len : r.pos + l, m = new $root.RPC.ControlGraft();
-                while (r.pos < c) {
-                    var t = r.uint32();
-                    switch (t >>> 3) {
-                    case 1:
-                        m.topicID = r.string();
-                        break;
-                    default:
-                        r.skipType(t & 7);
-                        break;
-                    }
-                }
-                return m;
-            };
-
-            /**
-             * Creates a ControlGraft message from a plain object. Also converts values to their respective internal types.
-             * @function fromObject
-             * @memberof RPC.ControlGraft
-             * @static
-             * @param {Object.<string,*>} d Plain object
-             * @returns {RPC.ControlGraft} ControlGraft
-             */
-            ControlGraft.fromObject = function fromObject(d) {
-                if (d instanceof $root.RPC.ControlGraft)
-                    return d;
-                var m = new $root.RPC.ControlGraft();
-                if (d.topicID != null) {
-                    m.topicID = String(d.topicID);
-                }
-                return m;
-            };
-
-            /**
-             * Creates a plain object from a ControlGraft message. Also converts values to other types if specified.
-             * @function toObject
-             * @memberof RPC.ControlGraft
-             * @static
-             * @param {RPC.ControlGraft} m ControlGraft
-             * @param {$protobuf.IConversionOptions} [o] Conversion options
-             * @returns {Object.<string,*>} Plain object
-             */
-            ControlGraft.toObject = function toObject(m, o) {
-                if (!o)
-                    o = {};
-                var d = {};
-                if (m.topicID != null && m.hasOwnProperty("topicID")) {
-                    d.topicID = m.topicID;
-                    if (o.oneofs)
-                        d._topicID = "topicID";
-                }
-                return d;
-            };
-
-            /**
-             * Converts this ControlGraft to JSON.
-             * @function toJSON
-             * @memberof RPC.ControlGraft
-             * @instance
-             * @returns {Object.<string,*>} JSON object
-             */
-            ControlGraft.prototype.toJSON = function toJSON() {
-                return this.constructor.toObject(this, $protobuf.util.toJSONOptions);
-            };
-
-            return ControlGraft;
-        })();
-
-        RPC.ControlPrune = (function() {
-
-            /**
-             * Properties of a ControlPrune.
-             * @memberof RPC
-             * @interface IControlPrune
-             * @property {string|null} [topicID] ControlPrune topicID
-             * @property {Array.<RPC.IPeerInfo>|null} [peers] ControlPrune peers
-             * @property {number|null} [backoff] ControlPrune backoff
-             */
-
-            /**
-             * Constructs a new ControlPrune.
-             * @memberof RPC
-             * @classdesc Represents a ControlPrune.
-             * @implements IControlPrune
-             * @constructor
-             * @param {RPC.IControlPrune=} [p] Properties to set
-             */
-            function ControlPrune(p) {
-                this.peers = [];
-                if (p)
-                    for (var ks = Object.keys(p), i = 0; i < ks.length; ++i)
-                        if (p[ks[i]] != null)
-                            this[ks[i]] = p[ks[i]];
-            }
-
-            /**
-             * ControlPrune topicID.
-             * @member {string|null|undefined} topicID
-             * @memberof RPC.ControlPrune
-             * @instance
-             */
-            ControlPrune.prototype.topicID = null;
-
-            /**
-             * ControlPrune peers.
-             * @member {Array.<RPC.IPeerInfo>} peers
-             * @memberof RPC.ControlPrune
-             * @instance
-             */
-            ControlPrune.prototype.peers = $util.emptyArray;
-
-            /**
-             * ControlPrune backoff.
-             * @member {number|null|undefined} backoff
-             * @memberof RPC.ControlPrune
-             * @instance
-             */
-            ControlPrune.prototype.backoff = null;
-
-            // OneOf field names bound to virtual getters and setters
-            var $oneOfFields;
-
-            /**
-             * ControlPrune _topicID.
-             * @member {"topicID"|undefined} _topicID
-             * @memberof RPC.ControlPrune
-             * @instance
-             */
-            Object.defineProperty(ControlPrune.prototype, "_topicID", {
-                get: $util.oneOfGetter($oneOfFields = ["topicID"]),
-                set: $util.oneOfSetter($oneOfFields)
-            });
-
-            /**
-             * ControlPrune _backoff.
-             * @member {"backoff"|undefined} _backoff
-             * @memberof RPC.ControlPrune
-             * @instance
-             */
-            Object.defineProperty(ControlPrune.prototype, "_backoff", {
-                get: $util.oneOfGetter($oneOfFields = ["backoff"]),
-                set: $util.oneOfSetter($oneOfFields)
-            });
-
-            /**
-             * Encodes the specified ControlPrune message. Does not implicitly {@link RPC.ControlPrune.verify|verify} messages.
-             * @function encode
-             * @memberof RPC.ControlPrune
-             * @static
-             * @param {RPC.IControlPrune} m ControlPrune message or plain object to encode
-             * @param {$protobuf.Writer} [w] Writer to encode to
-             * @returns {$protobuf.Writer} Writer
-             */
-            ControlPrune.encode = function encode(m, w) {
-                if (!w)
-                    w = $Writer.create();
-                if (m.topicID != null && Object.hasOwnProperty.call(m, "topicID"))
-                    w.uint32(10).string(m.topicID);
-                if (m.peers != null && m.peers.length) {
-                    for (var i = 0; i < m.peers.length; ++i)
-                        $root.RPC.PeerInfo.encode(m.peers[i], w.uint32(18).fork()).ldelim();
-                }
-                if (m.backoff != null && Object.hasOwnProperty.call(m, "backoff"))
-                    w.uint32(24).uint64(m.backoff);
-                return w;
-            };
-
-            /**
-             * Decodes a ControlPrune message from the specified reader or buffer.
-             * @function decode
-             * @memberof RPC.ControlPrune
-             * @static
-             * @param {$protobuf.Reader|Uint8Array} r Reader or buffer to decode from
-             * @param {number} [l] Message length if known beforehand
-             * @returns {RPC.ControlPrune} ControlPrune
-             * @throws {Error} If the payload is not a reader or valid buffer
-             * @throws {$protobuf.util.ProtocolError} If required fields are missing
-             */
-            ControlPrune.decode = function decode(r, l) {
-                if (!(r instanceof $Reader))
-                    r = $Reader.create(r);
-                var c = l === undefined ? r.len : r.pos + l, m = new $root.RPC.ControlPrune();
-                while (r.pos < c) {
-                    var t = r.uint32();
-                    switch (t >>> 3) {
-                    case 1:
-                        m.topicID = r.string();
-                        break;
-                    case 2:
-                        if (!(m.peers && m.peers.length))
-                            m.peers = [];
-                        m.peers.push($root.RPC.PeerInfo.decode(r, r.uint32()));
-                        break;
-                    case 3:
-                        m.backoff = r.uint64();
-                        break;
-                    default:
-                        r.skipType(t & 7);
-                        break;
-                    }
-                }
-                return m;
-            };
-
-            /**
-             * Creates a ControlPrune message from a plain object. Also converts values to their respective internal types.
-             * @function fromObject
-             * @memberof RPC.ControlPrune
-             * @static
-             * @param {Object.<string,*>} d Plain object
-             * @returns {RPC.ControlPrune} ControlPrune
-             */
-            ControlPrune.fromObject = function fromObject(d) {
-                if (d instanceof $root.RPC.ControlPrune)
-                    return d;
-                var m = new $root.RPC.ControlPrune();
-                if (d.topicID != null) {
-                    m.topicID = String(d.topicID);
-                }
-                if (d.peers) {
-                    if (!Array.isArray(d.peers))
-                        throw TypeError(".RPC.ControlPrune.peers: array expected");
-                    m.peers = [];
-                    for (var i = 0; i < d.peers.length; ++i) {
-                        if (typeof d.peers[i] !== "object")
-                            throw TypeError(".RPC.ControlPrune.peers: object expected");
-                        m.peers[i] = $root.RPC.PeerInfo.fromObject(d.peers[i]);
-                    }
-                }
-                if (d.backoff != null) {
-                    if ($util.Long)
-                        (m.backoff = $util.Long.fromValue(d.backoff)).unsigned = true;
-                    else if (typeof d.backoff === "string")
-                        m.backoff = parseInt(d.backoff, 10);
-                    else if (typeof d.backoff === "number")
-                        m.backoff = d.backoff;
-                    else if (typeof d.backoff === "object")
-                        m.backoff = new $util.LongBits(d.backoff.low >>> 0, d.backoff.high >>> 0).toNumber(true);
-                }
-                return m;
-            };
-
-            /**
-             * Creates a plain object from a ControlPrune message. Also converts values to other types if specified.
-             * @function toObject
-             * @memberof RPC.ControlPrune
-             * @static
-             * @param {RPC.ControlPrune} m ControlPrune
-             * @param {$protobuf.IConversionOptions} [o] Conversion options
-             * @returns {Object.<string,*>} Plain object
-             */
-            ControlPrune.toObject = function toObject(m, o) {
-                if (!o)
-                    o = {};
-                var d = {};
-                if (o.arrays || o.defaults) {
-                    d.peers = [];
-                }
-                if (m.topicID != null && m.hasOwnProperty("topicID")) {
-                    d.topicID = m.topicID;
-                    if (o.oneofs)
-                        d._topicID = "topicID";
-                }
-                if (m.peers && m.peers.length) {
-                    d.peers = [];
-                    for (var j = 0; j < m.peers.length; ++j) {
-                        d.peers[j] = $root.RPC.PeerInfo.toObject(m.peers[j], o);
-                    }
-                }
-                if (m.backoff != null && m.hasOwnProperty("backoff")) {
-                    if (typeof m.backoff === "number")
-                        d.backoff = o.longs === String ? String(m.backoff) : m.backoff;
-                    else
-                        d.backoff = o.longs === String ? $util.Long.prototype.toString.call(m.backoff) : o.longs === Number ? new $util.LongBits(m.backoff.low >>> 0, m.backoff.high >>> 0).toNumber(true) : m.backoff;
-                    if (o.oneofs)
-                        d._backoff = "backoff";
-                }
-                return d;
-            };
-
-            /**
-             * Converts this ControlPrune to JSON.
-             * @function toJSON
-             * @memberof RPC.ControlPrune
-             * @instance
-             * @returns {Object.<string,*>} JSON object
-             */
-            ControlPrune.prototype.toJSON = function toJSON() {
-                return this.constructor.toObject(this, $protobuf.util.toJSONOptions);
-            };
-
-            return ControlPrune;
-        })();
-
-        RPC.PeerInfo = (function() {
-
-            /**
-             * Properties of a PeerInfo.
-             * @memberof RPC
-             * @interface IPeerInfo
-             * @property {Uint8Array|null} [peerID] PeerInfo peerID
-             * @property {Uint8Array|null} [signedPeerRecord] PeerInfo signedPeerRecord
-             */
-
-            /**
-             * Constructs a new PeerInfo.
-             * @memberof RPC
-             * @classdesc Represents a PeerInfo.
-             * @implements IPeerInfo
-             * @constructor
-             * @param {RPC.IPeerInfo=} [p] Properties to set
-             */
-            function PeerInfo(p) {
-                if (p)
-                    for (var ks = Object.keys(p), i = 0; i < ks.length; ++i)
-                        if (p[ks[i]] != null)
-                            this[ks[i]] = p[ks[i]];
-            }
-
-            /**
-             * PeerInfo peerID.
-             * @member {Uint8Array|null|undefined} peerID
-             * @memberof RPC.PeerInfo
-             * @instance
-             */
-            PeerInfo.prototype.peerID = null;
-
-            /**
-             * PeerInfo signedPeerRecord.
-             * @member {Uint8Array|null|undefined} signedPeerRecord
-             * @memberof RPC.PeerInfo
-             * @instance
-             */
-            PeerInfo.prototype.signedPeerRecord = null;
-
-            // OneOf field names bound to virtual getters and setters
-            var $oneOfFields;
-
-            /**
-             * PeerInfo _peerID.
-             * @member {"peerID"|undefined} _peerID
-             * @memberof RPC.PeerInfo
-             * @instance
-             */
-            Object.defineProperty(PeerInfo.prototype, "_peerID", {
-                get: $util.oneOfGetter($oneOfFields = ["peerID"]),
-                set: $util.oneOfSetter($oneOfFields)
-            });
-
-            /**
-             * PeerInfo _signedPeerRecord.
-             * @member {"signedPeerRecord"|undefined} _signedPeerRecord
-             * @memberof RPC.PeerInfo
-             * @instance
-             */
-            Object.defineProperty(PeerInfo.prototype, "_signedPeerRecord", {
-                get: $util.oneOfGetter($oneOfFields = ["signedPeerRecord"]),
-                set: $util.oneOfSetter($oneOfFields)
-            });
-
-            /**
-             * Encodes the specified PeerInfo message. Does not implicitly {@link RPC.PeerInfo.verify|verify} messages.
-             * @function encode
-             * @memberof RPC.PeerInfo
-             * @static
-             * @param {RPC.IPeerInfo} m PeerInfo message or plain object to encode
-             * @param {$protobuf.Writer} [w] Writer to encode to
-             * @returns {$protobuf.Writer} Writer
-             */
-            PeerInfo.encode = function encode(m, w) {
-                if (!w)
-                    w = $Writer.create();
-                if (m.peerID != null && Object.hasOwnProperty.call(m, "peerID"))
-                    w.uint32(10).bytes(m.peerID);
-                if (m.signedPeerRecord != null && Object.hasOwnProperty.call(m, "signedPeerRecord"))
-                    w.uint32(18).bytes(m.signedPeerRecord);
-                return w;
-            };
-
-            /**
-             * Decodes a PeerInfo message from the specified reader or buffer.
-             * @function decode
-             * @memberof RPC.PeerInfo
-             * @static
-             * @param {$protobuf.Reader|Uint8Array} r Reader or buffer to decode from
-             * @param {number} [l] Message length if known beforehand
-             * @returns {RPC.PeerInfo} PeerInfo
-             * @throws {Error} If the payload is not a reader or valid buffer
-             * @throws {$protobuf.util.ProtocolError} If required fields are missing
-             */
-            PeerInfo.decode = function decode(r, l) {
-                if (!(r instanceof $Reader))
-                    r = $Reader.create(r);
-                var c = l === undefined ? r.len : r.pos + l, m = new $root.RPC.PeerInfo();
-                while (r.pos < c) {
-                    var t = r.uint32();
-                    switch (t >>> 3) {
-                    case 1:
-                        m.peerID = r.bytes();
-                        break;
-                    case 2:
-                        m.signedPeerRecord = r.bytes();
-                        break;
-                    default:
-                        r.skipType(t & 7);
-                        break;
-                    }
-                }
-                return m;
-            };
-
-            /**
-             * Creates a PeerInfo message from a plain object. Also converts values to their respective internal types.
-             * @function fromObject
-             * @memberof RPC.PeerInfo
-             * @static
-             * @param {Object.<string,*>} d Plain object
-             * @returns {RPC.PeerInfo} PeerInfo
-             */
-            PeerInfo.fromObject = function fromObject(d) {
-                if (d instanceof $root.RPC.PeerInfo)
-                    return d;
-                var m = new $root.RPC.PeerInfo();
-                if (d.peerID != null) {
-                    if (typeof d.peerID === "string")
-                        $util.base64.decode(d.peerID, m.peerID = $util.newBuffer($util.base64.length(d.peerID)), 0);
-                    else if (d.peerID.length)
-                        m.peerID = d.peerID;
-                }
-                if (d.signedPeerRecord != null) {
-                    if (typeof d.signedPeerRecord === "string")
-                        $util.base64.decode(d.signedPeerRecord, m.signedPeerRecord = $util.newBuffer($util.base64.length(d.signedPeerRecord)), 0);
-                    else if (d.signedPeerRecord.length)
-                        m.signedPeerRecord = d.signedPeerRecord;
-                }
-                return m;
-            };
-
-            /**
-             * Creates a plain object from a PeerInfo message. Also converts values to other types if specified.
-             * @function toObject
-             * @memberof RPC.PeerInfo
-             * @static
-             * @param {RPC.PeerInfo} m PeerInfo
-             * @param {$protobuf.IConversionOptions} [o] Conversion options
-             * @returns {Object.<string,*>} Plain object
-             */
-            PeerInfo.toObject = function toObject(m, o) {
-                if (!o)
-                    o = {};
-                var d = {};
-                if (m.peerID != null && m.hasOwnProperty("peerID")) {
-                    d.peerID = o.bytes === String ? $util.base64.encode(m.peerID, 0, m.peerID.length) : o.bytes === Array ? Array.prototype.slice.call(m.peerID) : m.peerID;
-                    if (o.oneofs)
-                        d._peerID = "peerID";
-                }
-                if (m.signedPeerRecord != null && m.hasOwnProperty("signedPeerRecord")) {
-                    d.signedPeerRecord = o.bytes === String ? $util.base64.encode(m.signedPeerRecord, 0, m.signedPeerRecord.length) : o.bytes === Array ? Array.prototype.slice.call(m.signedPeerRecord) : m.signedPeerRecord;
-                    if (o.oneofs)
-                        d._signedPeerRecord = "signedPeerRecord";
-                }
-                return d;
-            };
-
-            /**
-             * Converts this PeerInfo to JSON.
-             * @function toJSON
-             * @memberof RPC.PeerInfo
-             * @instance
-             * @returns {Object.<string,*>} JSON object
-             */
-            PeerInfo.prototype.toJSON = function toJSON() {
-                return this.constructor.toObject(this, $protobuf.util.toJSONOptions);
-            };
-
-            return PeerInfo;
-        })();
-
-        return RPC;
-    })();
-
-    return $root;
-});
diff --git a/src/message/rpc.d.ts b/src/message/rpc.d.ts
deleted file mode 100644
index 3716d3cc..00000000
--- a/src/message/rpc.d.ts
+++ /dev/null
@@ -1,666 +0,0 @@
-import * as $protobuf from "protobufjs";
-/** Properties of a RPC. */
-export interface IRPC {
-
-    /** RPC subscriptions */
-    subscriptions?: (RPC.ISubOpts[]|null);
-
-    /** RPC messages */
-    messages?: (RPC.IMessage[]|null);
-
-    /** RPC control */
-    control?: (RPC.IControlMessage|null);
-}
-
-/** Represents a RPC. */
-export class RPC implements IRPC {
-
-    /**
-     * Constructs a new RPC.
-     * @param [p] Properties to set
-     */
-    constructor(p?: IRPC);
-
-    /** RPC subscriptions. */
-    public subscriptions: RPC.ISubOpts[];
-
-    /** RPC messages. */
-    public messages: RPC.IMessage[];
-
-    /** RPC control. */
-    public control?: (RPC.IControlMessage|null);
-
-    /** RPC _control. */
-    public _control?: "control";
-
-    /**
-     * Encodes the specified RPC message. Does not implicitly {@link RPC.verify|verify} messages.
-     * @param m RPC message or plain object to encode
-     * @param [w] Writer to encode to
-     * @returns Writer
-     */
-    public static encode(m: IRPC, w?: $protobuf.Writer): $protobuf.Writer;
-
-    /**
-     * Decodes a RPC message from the specified reader or buffer.
-     * @param r Reader or buffer to decode from
-     * @param [l] Message length if known beforehand
-     * @returns RPC
-     * @throws {Error} If the payload is not a reader or valid buffer
-     * @throws {$protobuf.util.ProtocolError} If required fields are missing
-     */
-    public static decode(r: ($protobuf.Reader|Uint8Array), l?: number): RPC;
-
-    /**
-     * Creates a RPC message from a plain object. Also converts values to their respective internal types.
-     * @param d Plain object
-     * @returns RPC
-     */
-    public static fromObject(d: { [k: string]: any }): RPC;
-
-    /**
-     * Creates a plain object from a RPC message. Also converts values to other types if specified.
-     * @param m RPC
-     * @param [o] Conversion options
-     * @returns Plain object
-     */
-    public static toObject(m: RPC, o?: $protobuf.IConversionOptions): { [k: string]: any };
-
-    /**
-     * Converts this RPC to JSON.
-     * @returns JSON object
-     */
-    public toJSON(): { [k: string]: any };
-}
-
-export namespace RPC {
-
-    /** Properties of a SubOpts. */
-    interface ISubOpts {
-
-        /** SubOpts subscribe */
-        subscribe?: (boolean|null);
-
-        /** SubOpts topic */
-        topic?: (string|null);
-    }
-
-    /** Represents a SubOpts. */
-    class SubOpts implements ISubOpts {
-
-        /**
-         * Constructs a new SubOpts.
-         * @param [p] Properties to set
-         */
-        constructor(p?: RPC.ISubOpts);
-
-        /** SubOpts subscribe. */
-        public subscribe?: (boolean|null);
-
-        /** SubOpts topic. */
-        public topic?: (string|null);
-
-        /** SubOpts _subscribe. */
-        public _subscribe?: "subscribe";
-
-        /** SubOpts _topic. */
-        public _topic?: "topic";
-
-        /**
-         * Encodes the specified SubOpts message. Does not implicitly {@link RPC.SubOpts.verify|verify} messages.
-         * @param m SubOpts message or plain object to encode
-         * @param [w] Writer to encode to
-         * @returns Writer
-         */
-        public static encode(m: RPC.ISubOpts, w?: $protobuf.Writer): $protobuf.Writer;
-
-        /**
-         * Decodes a SubOpts message from the specified reader or buffer.
-         * @param r Reader or buffer to decode from
-         * @param [l] Message length if known beforehand
-         * @returns SubOpts
-         * @throws {Error} If the payload is not a reader or valid buffer
-         * @throws {$protobuf.util.ProtocolError} If required fields are missing
-         */
-        public static decode(r: ($protobuf.Reader|Uint8Array), l?: number): RPC.SubOpts;
-
-        /**
-         * Creates a SubOpts message from a plain object. Also converts values to their respective internal types.
-         * @param d Plain object
-         * @returns SubOpts
-         */
-        public static fromObject(d: { [k: string]: any }): RPC.SubOpts;
-
-        /**
-         * Creates a plain object from a SubOpts message. Also converts values to other types if specified.
-         * @param m SubOpts
-         * @param [o] Conversion options
-         * @returns Plain object
-         */
-        public static toObject(m: RPC.SubOpts, o?: $protobuf.IConversionOptions): { [k: string]: any };
-
-        /**
-         * Converts this SubOpts to JSON.
-         * @returns JSON object
-         */
-        public toJSON(): { [k: string]: any };
-    }
-
-    /** Properties of a Message. */
-    interface IMessage {
-
-        /** Message from */
-        from?: (Uint8Array|null);
-
-        /** Message data */
-        data?: (Uint8Array|null);
-
-        /** Message seqno */
-        seqno?: (Uint8Array|null);
-
-        /** Message topic */
-        topic: string;
-
-        /** Message signature */
-        signature?: (Uint8Array|null);
-
-        /** Message key */
-        key?: (Uint8Array|null);
-    }
-
-    /** Represents a Message. */
-    class Message implements IMessage {
-
-        /**
-         * Constructs a new Message.
-         * @param [p] Properties to set
-         */
-        constructor(p?: RPC.IMessage);
-
-        /** Message from. */
-        public from?: (Uint8Array|null);
-
-        /** Message data. */
-        public data?: (Uint8Array|null);
-
-        /** Message seqno. */
-        public seqno?: (Uint8Array|null);
-
-        /** Message topic. */
-        public topic: string;
-
-        /** Message signature. */
-        public signature?: (Uint8Array|null);
-
-        /** Message key. */
-        public key?: (Uint8Array|null);
-
-        /** Message _from. */
-        public _from?: "from";
-
-        /** Message _data. */
-        public _data?: "data";
-
-        /** Message _seqno. */
-        public _seqno?: "seqno";
-
-        /** Message _signature. */
-        public _signature?: "signature";
-
-        /** Message _key. */
-        public _key?: "key";
-
-        /**
-         * Encodes the specified Message message. Does not implicitly {@link RPC.Message.verify|verify} messages.
-         * @param m Message message or plain object to encode
-         * @param [w] Writer to encode to
-         * @returns Writer
-         */
-        public static encode(m: RPC.IMessage, w?: $protobuf.Writer): $protobuf.Writer;
-
-        /**
-         * Decodes a Message message from the specified reader or buffer.
-         * @param r Reader or buffer to decode from
-         * @param [l] Message length if known beforehand
-         * @returns Message
-         * @throws {Error} If the payload is not a reader or valid buffer
-         * @throws {$protobuf.util.ProtocolError} If required fields are missing
-         */
-        public static decode(r: ($protobuf.Reader|Uint8Array), l?: number): RPC.Message;
-
-        /**
-         * Creates a Message message from a plain object. Also converts values to their respective internal types.
-         * @param d Plain object
-         * @returns Message
-         */
-        public static fromObject(d: { [k: string]: any }): RPC.Message;
-
-        /**
-         * Creates a plain object from a Message message. Also converts values to other types if specified.
-         * @param m Message
-         * @param [o] Conversion options
-         * @returns Plain object
-         */
-        public static toObject(m: RPC.Message, o?: $protobuf.IConversionOptions): { [k: string]: any };
-
-        /**
-         * Converts this Message to JSON.
-         * @returns JSON object
-         */
-        public toJSON(): { [k: string]: any };
-    }
-
-    /** Properties of a ControlMessage. */
-    interface IControlMessage {
-
-        /** ControlMessage ihave */
-        ihave?: (RPC.IControlIHave[]|null);
-
-        /** ControlMessage iwant */
-        iwant?: (RPC.IControlIWant[]|null);
-
-        /** ControlMessage graft */
-        graft?: (RPC.IControlGraft[]|null);
-
-        /** ControlMessage prune */
-        prune?: (RPC.IControlPrune[]|null);
-    }
-
-    /** Represents a ControlMessage. */
-    class ControlMessage implements IControlMessage {
-
-        /**
-         * Constructs a new ControlMessage.
-         * @param [p] Properties to set
-         */
-        constructor(p?: RPC.IControlMessage);
-
-        /** ControlMessage ihave. */
-        public ihave: RPC.IControlIHave[];
-
-        /** ControlMessage iwant. */
-        public iwant: RPC.IControlIWant[];
-
-        /** ControlMessage graft. */
-        public graft: RPC.IControlGraft[];
-
-        /** ControlMessage prune. */
-        public prune: RPC.IControlPrune[];
-
-        /**
-         * Encodes the specified ControlMessage message. Does not implicitly {@link RPC.ControlMessage.verify|verify} messages.
-         * @param m ControlMessage message or plain object to encode
-         * @param [w] Writer to encode to
-         * @returns Writer
-         */
-        public static encode(m: RPC.IControlMessage, w?: $protobuf.Writer): $protobuf.Writer;
-
-        /**
-         * Decodes a ControlMessage message from the specified reader or buffer.
-         * @param r Reader or buffer to decode from
-         * @param [l] Message length if known beforehand
-         * @returns ControlMessage
-         * @throws {Error} If the payload is not a reader or valid buffer
-         * @throws {$protobuf.util.ProtocolError} If required fields are missing
-         */
-        public static decode(r: ($protobuf.Reader|Uint8Array), l?: number): RPC.ControlMessage;
-
-        /**
-         * Creates a ControlMessage message from a plain object. Also converts values to their respective internal types.
-         * @param d Plain object
-         * @returns ControlMessage
-         */
-        public static fromObject(d: { [k: string]: any }): RPC.ControlMessage;
-
-        /**
-         * Creates a plain object from a ControlMessage message. Also converts values to other types if specified.
-         * @param m ControlMessage
-         * @param [o] Conversion options
-         * @returns Plain object
-         */
-        public static toObject(m: RPC.ControlMessage, o?: $protobuf.IConversionOptions): { [k: string]: any };
-
-        /**
-         * Converts this ControlMessage to JSON.
-         * @returns JSON object
-         */
-        public toJSON(): { [k: string]: any };
-    }
-
-    /** Properties of a ControlIHave. */
-    interface IControlIHave {
-
-        /** ControlIHave topicID */
-        topicID?: (string|null);
-
-        /** ControlIHave messageIDs */
-        messageIDs?: (Uint8Array[]|null);
-    }
-
-    /** Represents a ControlIHave. */
-    class ControlIHave implements IControlIHave {
-
-        /**
-         * Constructs a new ControlIHave.
-         * @param [p] Properties to set
-         */
-        constructor(p?: RPC.IControlIHave);
-
-        /** ControlIHave topicID. */
-        public topicID?: (string|null);
-
-        /** ControlIHave messageIDs. */
-        public messageIDs: Uint8Array[];
-
-        /** ControlIHave _topicID. */
-        public _topicID?: "topicID";
-
-        /**
-         * Encodes the specified ControlIHave message. Does not implicitly {@link RPC.ControlIHave.verify|verify} messages.
-         * @param m ControlIHave message or plain object to encode
-         * @param [w] Writer to encode to
-         * @returns Writer
-         */
-        public static encode(m: RPC.IControlIHave, w?: $protobuf.Writer): $protobuf.Writer;
-
-        /**
-         * Decodes a ControlIHave message from the specified reader or buffer.
-         * @param r Reader or buffer to decode from
-         * @param [l] Message length if known beforehand
-         * @returns ControlIHave
-         * @throws {Error} If the payload is not a reader or valid buffer
-         * @throws {$protobuf.util.ProtocolError} If required fields are missing
-         */
-        public static decode(r: ($protobuf.Reader|Uint8Array), l?: number): RPC.ControlIHave;
-
-        /**
-         * Creates a ControlIHave message from a plain object. Also converts values to their respective internal types.
-         * @param d Plain object
-         * @returns ControlIHave
-         */
-        public static fromObject(d: { [k: string]: any }): RPC.ControlIHave;
-
-        /**
-         * Creates a plain object from a ControlIHave message. Also converts values to other types if specified.
-         * @param m ControlIHave
-         * @param [o] Conversion options
-         * @returns Plain object
-         */
-        public static toObject(m: RPC.ControlIHave, o?: $protobuf.IConversionOptions): { [k: string]: any };
-
-        /**
-         * Converts this ControlIHave to JSON.
-         * @returns JSON object
-         */
-        public toJSON(): { [k: string]: any };
-    }
-
-    /** Properties of a ControlIWant. */
-    interface IControlIWant {
-
-        /** ControlIWant messageIDs */
-        messageIDs?: (Uint8Array[]|null);
-    }
-
-    /** Represents a ControlIWant. */
-    class ControlIWant implements IControlIWant {
-
-        /**
-         * Constructs a new ControlIWant.
-         * @param [p] Properties to set
-         */
-        constructor(p?: RPC.IControlIWant);
-
-        /** ControlIWant messageIDs. */
-        public messageIDs: Uint8Array[];
-
-        /**
-         * Encodes the specified ControlIWant message. Does not implicitly {@link RPC.ControlIWant.verify|verify} messages.
-         * @param m ControlIWant message or plain object to encode
-         * @param [w] Writer to encode to
-         * @returns Writer
-         */
-        public static encode(m: RPC.IControlIWant, w?: $protobuf.Writer): $protobuf.Writer;
-
-        /**
-         * Decodes a ControlIWant message from the specified reader or buffer.
-         * @param r Reader or buffer to decode from
-         * @param [l] Message length if known beforehand
-         * @returns ControlIWant
-         * @throws {Error} If the payload is not a reader or valid buffer
-         * @throws {$protobuf.util.ProtocolError} If required fields are missing
-         */
-        public static decode(r: ($protobuf.Reader|Uint8Array), l?: number): RPC.ControlIWant;
-
-        /**
-         * Creates a ControlIWant message from a plain object. Also converts values to their respective internal types.
-         * @param d Plain object
-         * @returns ControlIWant
-         */
-        public static fromObject(d: { [k: string]: any }): RPC.ControlIWant;
-
-        /**
-         * Creates a plain object from a ControlIWant message. Also converts values to other types if specified.
-         * @param m ControlIWant
-         * @param [o] Conversion options
-         * @returns Plain object
-         */
-        public static toObject(m: RPC.ControlIWant, o?: $protobuf.IConversionOptions): { [k: string]: any };
-
-        /**
-         * Converts this ControlIWant to JSON.
-         * @returns JSON object
-         */
-        public toJSON(): { [k: string]: any };
-    }
-
-    /** Properties of a ControlGraft. */
-    interface IControlGraft {
-
-        /** ControlGraft topicID */
-        topicID?: (string|null);
-    }
-
-    /** Represents a ControlGraft. */
-    class ControlGraft implements IControlGraft {
-
-        /**
-         * Constructs a new ControlGraft.
-         * @param [p] Properties to set
-         */
-        constructor(p?: RPC.IControlGraft);
-
-        /** ControlGraft topicID. */
-        public topicID?: (string|null);
-
-        /** ControlGraft _topicID. */
-        public _topicID?: "topicID";
-
-        /**
-         * Encodes the specified ControlGraft message. Does not implicitly {@link RPC.ControlGraft.verify|verify} messages.
-         * @param m ControlGraft message or plain object to encode
-         * @param [w] Writer to encode to
-         * @returns Writer
-         */
-        public static encode(m: RPC.IControlGraft, w?: $protobuf.Writer): $protobuf.Writer;
-
-        /**
-         * Decodes a ControlGraft message from the specified reader or buffer.
-         * @param r Reader or buffer to decode from
-         * @param [l] Message length if known beforehand
-         * @returns ControlGraft
-         * @throws {Error} If the payload is not a reader or valid buffer
-         * @throws {$protobuf.util.ProtocolError} If required fields are missing
-         */
-        public static decode(r: ($protobuf.Reader|Uint8Array), l?: number): RPC.ControlGraft;
-
-        /**
-         * Creates a ControlGraft message from a plain object. Also converts values to their respective internal types.
-         * @param d Plain object
-         * @returns ControlGraft
-         */
-        public static fromObject(d: { [k: string]: any }): RPC.ControlGraft;
-
-        /**
-         * Creates a plain object from a ControlGraft message. Also converts values to other types if specified.
-         * @param m ControlGraft
-         * @param [o] Conversion options
-         * @returns Plain object
-         */
-        public static toObject(m: RPC.ControlGraft, o?: $protobuf.IConversionOptions): { [k: string]: any };
-
-        /**
-         * Converts this ControlGraft to JSON.
-         * @returns JSON object
-         */
-        public toJSON(): { [k: string]: any };
-    }
-
-    /** Properties of a ControlPrune. */
-    interface IControlPrune {
-
-        /** ControlPrune topicID */
-        topicID?: (string|null);
-
-        /** ControlPrune peers */
-        peers?: (RPC.IPeerInfo[]|null);
-
-        /** ControlPrune backoff */
-        backoff?: (number|null);
-    }
-
-    /** Represents a ControlPrune. */
-    class ControlPrune implements IControlPrune {
-
-        /**
-         * Constructs a new ControlPrune.
-         * @param [p] Properties to set
-         */
-        constructor(p?: RPC.IControlPrune);
-
-        /** ControlPrune topicID. */
-        public topicID?: (string|null);
-
-        /** ControlPrune peers. */
-        public peers: RPC.IPeerInfo[];
-
-        /** ControlPrune backoff. */
-        public backoff?: (number|null);
-
-        /** ControlPrune _topicID. */
-        public _topicID?: "topicID";
-
-        /** ControlPrune _backoff. */
-        public _backoff?: "backoff";
-
-        /**
-         * Encodes the specified ControlPrune message. Does not implicitly {@link RPC.ControlPrune.verify|verify} messages.
-         * @param m ControlPrune message or plain object to encode
-         * @param [w] Writer to encode to
-         * @returns Writer
-         */
-        public static encode(m: RPC.IControlPrune, w?: $protobuf.Writer): $protobuf.Writer;
-
-        /**
-         * Decodes a ControlPrune message from the specified reader or buffer.
-         * @param r Reader or buffer to decode from
-         * @param [l] Message length if known beforehand
-         * @returns ControlPrune
-         * @throws {Error} If the payload is not a reader or valid buffer
-         * @throws {$protobuf.util.ProtocolError} If required fields are missing
-         */
-        public static decode(r: ($protobuf.Reader|Uint8Array), l?: number): RPC.ControlPrune;
-
-        /**
-         * Creates a ControlPrune message from a plain object. Also converts values to their respective internal types.
-         * @param d Plain object
-         * @returns ControlPrune
-         */
-        public static fromObject(d: { [k: string]: any }): RPC.ControlPrune;
-
-        /**
-         * Creates a plain object from a ControlPrune message. Also converts values to other types if specified.
-         * @param m ControlPrune
-         * @param [o] Conversion options
-         * @returns Plain object
-         */
-        public static toObject(m: RPC.ControlPrune, o?: $protobuf.IConversionOptions): { [k: string]: any };
-
-        /**
-         * Converts this ControlPrune to JSON.
-         * @returns JSON object
-         */
-        public toJSON(): { [k: string]: any };
-    }
-
-    /** Properties of a PeerInfo. */
-    interface IPeerInfo {
-
-        /** PeerInfo peerID */
-        peerID?: (Uint8Array|null);
-
-        /** PeerInfo signedPeerRecord */
-        signedPeerRecord?: (Uint8Array|null);
-    }
-
-    /** Represents a PeerInfo. */
-    class PeerInfo implements IPeerInfo {
-
-        /**
-         * Constructs a new PeerInfo.
-         * @param [p] Properties to set
-         */
-        constructor(p?: RPC.IPeerInfo);
-
-        /** PeerInfo peerID. */
-        public peerID?: (Uint8Array|null);
-
-        /** PeerInfo signedPeerRecord. */
-        public signedPeerRecord?: (Uint8Array|null);
-
-        /** PeerInfo _peerID. */
-        public _peerID?: "peerID";
-
-        /** PeerInfo _signedPeerRecord. */
-        public _signedPeerRecord?: "signedPeerRecord";
-
-        /**
-         * Encodes the specified PeerInfo message. Does not implicitly {@link RPC.PeerInfo.verify|verify} messages.
-         * @param m PeerInfo message or plain object to encode
-         * @param [w] Writer to encode to
-         * @returns Writer
-         */
-        public static encode(m: RPC.IPeerInfo, w?: $protobuf.Writer): $protobuf.Writer;
-
-        /**
-         * Decodes a PeerInfo message from the specified reader or buffer.
-         * @param r Reader or buffer to decode from
-         * @param [l] Message length if known beforehand
-         * @returns PeerInfo
-         * @throws {Error} If the payload is not a reader or valid buffer
-         * @throws {$protobuf.util.ProtocolError} If required fields are missing
-         */
-        public static decode(r: ($protobuf.Reader|Uint8Array), l?: number): RPC.PeerInfo;
-
-        /**
-         * Creates a PeerInfo message from a plain object. Also converts values to their respective internal types.
-         * @param d Plain object
-         * @returns PeerInfo
-         */
-        public static fromObject(d: { [k: string]: any }): RPC.PeerInfo;
-
-        /**
-         * Creates a plain object from a PeerInfo message. Also converts values to other types if specified.
-         * @param m PeerInfo
-         * @param [o] Conversion options
-         * @returns Plain object
-         */
-        public static toObject(m: RPC.PeerInfo, o?: $protobuf.IConversionOptions): { [k: string]: any };
-
-        /**
-         * Converts this PeerInfo to JSON.
-         * @returns JSON object
-         */
-        public toJSON(): { [k: string]: any };
-    }
-}
diff --git a/src/message/rpc.js b/src/message/rpc.js
deleted file mode 100644
index b569a782..00000000
--- a/src/message/rpc.js
+++ /dev/null
@@ -1,3 +0,0 @@
-import cjs from "./rpc.cjs"
-
-export const {RPC} = cjs
diff --git a/src/message/rpc.ts b/src/message/rpc.ts
new file mode 100644
index 00000000..6af7801d
--- /dev/null
+++ b/src/message/rpc.ts
@@ -0,0 +1,703 @@
+/* eslint-disable import/export */
+/* eslint-disable @typescript-eslint/no-namespace */
+
+import { encodeMessage, decodeMessage, message } from 'protons-runtime'
+import type { Uint8ArrayList } from 'uint8arraylist'
+import type { Codec } from 'protons-runtime'
+
+export interface RPC {
+  subscriptions: RPC.SubOpts[]
+  messages: RPC.Message[]
+  control?: RPC.ControlMessage
+}
+
+export namespace RPC {
+  export interface SubOpts {
+    subscribe?: boolean
+    topic?: string
+  }
+
+  export namespace SubOpts {
+    let _codec: Codec<SubOpts>
+
+    export const codec = (): Codec<SubOpts> => {
+      if (_codec == null) {
+        _codec = message<SubOpts>((obj, writer, opts = {}) => {
+          if (opts.lengthDelimited !== false) {
+            writer.fork()
+          }
+
+          if (obj.subscribe != null) {
+            writer.uint32(8)
+            writer.bool(obj.subscribe)
+          }
+
+          if (obj.topic != null) {
+            writer.uint32(18)
+            writer.string(obj.topic)
+          }
+
+          if (opts.lengthDelimited !== false) {
+            writer.ldelim()
+          }
+        }, (reader, length) => {
+          const obj: any = {}
+
+          const end = length == null ? reader.len : reader.pos + length
+
+          while (reader.pos < end) {
+            const tag = reader.uint32()
+
+            switch (tag >>> 3) {
+              case 1:
+                obj.subscribe = reader.bool()
+                break
+              case 2:
+                obj.topic = reader.string()
+                break
+              default:
+                reader.skipType(tag & 7)
+                break
+            }
+          }
+
+          return obj
+        })
+      }
+
+      return _codec
+    }
+
+    export const encode = (obj: SubOpts): Uint8Array => {
+      return encodeMessage(obj, SubOpts.codec())
+    }
+
+    export const decode = (buf: Uint8Array | Uint8ArrayList): SubOpts => {
+      return decodeMessage(buf, SubOpts.codec())
+    }
+  }
+
+  export interface Message {
+    from?: Uint8Array
+    data?: Uint8Array
+    seqno?: Uint8Array
+    topic: string
+    signature?: Uint8Array
+    key?: Uint8Array
+  }
+
+  export namespace Message {
+    let _codec: Codec<Message>
+
+    export const codec = (): Codec<Message> => {
+      if (_codec == null) {
+        _codec = message<Message>((obj, writer, opts = {}) => {
+          if (opts.lengthDelimited !== false) {
+            writer.fork()
+          }
+
+          if (obj.from != null) {
+            writer.uint32(10)
+            writer.bytes(obj.from)
+          }
+
+          if (obj.data != null) {
+            writer.uint32(18)
+            writer.bytes(obj.data)
+          }
+
+          if (obj.seqno != null) {
+            writer.uint32(26)
+            writer.bytes(obj.seqno)
+          }
+
+          if (obj.topic != null) {
+            writer.uint32(34)
+            writer.string(obj.topic)
+          } else {
+            throw new Error('Protocol error: required field "topic" was not found in object')
+          }
+
+          if (obj.signature != null) {
+            writer.uint32(42)
+            writer.bytes(obj.signature)
+          }
+
+          if (obj.key != null) {
+            writer.uint32(50)
+            writer.bytes(obj.key)
+          }
+
+          if (opts.lengthDelimited !== false) {
+            writer.ldelim()
+          }
+        }, (reader, length) => {
+          const obj: any = {
+            topic: ''
+          }
+
+          const end = length == null ? reader.len : reader.pos + length
+
+          while (reader.pos < end) {
+            const tag = reader.uint32()
+
+            switch (tag >>> 3) {
+              case 1:
+                obj.from = reader.bytes()
+                break
+              case 2:
+                obj.data = reader.bytes()
+                break
+              case 3:
+                obj.seqno = reader.bytes()
+                break
+              case 4:
+                obj.topic = reader.string()
+                break
+              case 5:
+                obj.signature = reader.bytes()
+                break
+              case 6:
+                obj.key = reader.bytes()
+                break
+              default:
+                reader.skipType(tag & 7)
+                break
+            }
+          }
+
+          if (obj.topic == null) {
+            throw new Error('Protocol error: value for required field "topic" was not found in protobuf')
+          }
+
+          return obj
+        })
+      }
+
+      return _codec
+    }
+
+    export const encode = (obj: Message): Uint8Array => {
+      return encodeMessage(obj, Message.codec())
+    }
+
+    export const decode = (buf: Uint8Array | Uint8ArrayList): Message => {
+      return decodeMessage(buf, Message.codec())
+    }
+  }
+
+  export interface ControlMessage {
+    ihave: RPC.ControlIHave[]
+    iwant: RPC.ControlIWant[]
+    graft: RPC.ControlGraft[]
+    prune: RPC.ControlPrune[]
+  }
+
+  export namespace ControlMessage {
+    let _codec: Codec<ControlMessage>
+
+    export const codec = (): Codec<ControlMessage> => {
+      if (_codec == null) {
+        _codec = message<ControlMessage>((obj, writer, opts = {}) => {
+          if (opts.lengthDelimited !== false) {
+            writer.fork()
+          }
+
+          if (obj.ihave != null) {
+            for (const value of obj.ihave) {
+              writer.uint32(10)
+              RPC.ControlIHave.codec().encode(value, writer)
+            }
+          } else {
+            throw new Error('Protocol error: required field "ihave" was not found in object')
+          }
+
+          if (obj.iwant != null) {
+            for (const value of obj.iwant) {
+              writer.uint32(18)
+              RPC.ControlIWant.codec().encode(value, writer)
+            }
+          } else {
+            throw new Error('Protocol error: required field "iwant" was not found in object')
+          }
+
+          if (obj.graft != null) {
+            for (const value of obj.graft) {
+              writer.uint32(26)
+              RPC.ControlGraft.codec().encode(value, writer)
+            }
+          } else {
+            throw new Error('Protocol error: required field "graft" was not found in object')
+          }
+
+          if (obj.prune != null) {
+            for (const value of obj.prune) {
+              writer.uint32(34)
+              RPC.ControlPrune.codec().encode(value, writer)
+            }
+          } else {
+            throw new Error('Protocol error: required field "prune" was not found in object')
+          }
+
+          if (opts.lengthDelimited !== false) {
+            writer.ldelim()
+          }
+        }, (reader, length) => {
+          const obj: any = {
+            ihave: [],
+            iwant: [],
+            graft: [],
+            prune: []
+          }
+
+          const end = length == null ? reader.len : reader.pos + length
+
+          while (reader.pos < end) {
+            const tag = reader.uint32()
+
+            switch (tag >>> 3) {
+              case 1:
+                obj.ihave.push(RPC.ControlIHave.codec().decode(reader, reader.uint32()))
+                break
+              case 2:
+                obj.iwant.push(RPC.ControlIWant.codec().decode(reader, reader.uint32()))
+                break
+              case 3:
+                obj.graft.push(RPC.ControlGraft.codec().decode(reader, reader.uint32()))
+                break
+              case 4:
+                obj.prune.push(RPC.ControlPrune.codec().decode(reader, reader.uint32()))
+                break
+              default:
+                reader.skipType(tag & 7)
+                break
+            }
+          }
+
+          return obj
+        })
+      }
+
+      return _codec
+    }
+
+    export const encode = (obj: ControlMessage): Uint8Array => {
+      return encodeMessage(obj, ControlMessage.codec())
+    }
+
+    export const decode = (buf: Uint8Array | Uint8ArrayList): ControlMessage => {
+      return decodeMessage(buf, ControlMessage.codec())
+    }
+  }
+
+  export interface ControlIHave {
+    topicID?: string
+    messageIDs: Uint8Array[]
+  }
+
+  export namespace ControlIHave {
+    let _codec: Codec<ControlIHave>
+
+    export const codec = (): Codec<ControlIHave> => {
+      if (_codec == null) {
+        _codec = message<ControlIHave>((obj, writer, opts = {}) => {
+          if (opts.lengthDelimited !== false) {
+            writer.fork()
+          }
+
+          if (obj.topicID != null) {
+            writer.uint32(10)
+            writer.string(obj.topicID)
+          }
+
+          if (obj.messageIDs != null) {
+            for (const value of obj.messageIDs) {
+              writer.uint32(18)
+              writer.bytes(value)
+            }
+          } else {
+            throw new Error('Protocol error: required field "messageIDs" was not found in object')
+          }
+
+          if (opts.lengthDelimited !== false) {
+            writer.ldelim()
+          }
+        }, (reader, length) => {
+          const obj: any = {
+            messageIDs: []
+          }
+
+          const end = length == null ? reader.len : reader.pos + length
+
+          while (reader.pos < end) {
+            const tag = reader.uint32()
+
+            switch (tag >>> 3) {
+              case 1:
+                obj.topicID = reader.string()
+                break
+              case 2:
+                obj.messageIDs.push(reader.bytes())
+                break
+              default:
+                reader.skipType(tag & 7)
+                break
+            }
+          }
+
+          return obj
+        })
+      }
+
+      return _codec
+    }
+
+    export const encode = (obj: ControlIHave): Uint8Array => {
+      return encodeMessage(obj, ControlIHave.codec())
+    }
+
+    export const decode = (buf: Uint8Array | Uint8ArrayList): ControlIHave => {
+      return decodeMessage(buf, ControlIHave.codec())
+    }
+  }
+
+  export interface ControlIWant {
+    messageIDs: Uint8Array[]
+  }
+
+  export namespace ControlIWant {
+    let _codec: Codec<ControlIWant>
+
+    export const codec = (): Codec<ControlIWant> => {
+      if (_codec == null) {
+        _codec = message<ControlIWant>((obj, writer, opts = {}) => {
+          if (opts.lengthDelimited !== false) {
+            writer.fork()
+          }
+
+          if (obj.messageIDs != null) {
+            for (const value of obj.messageIDs) {
+              writer.uint32(10)
+              writer.bytes(value)
+            }
+          } else {
+            throw new Error('Protocol error: required field "messageIDs" was not found in object')
+          }
+
+          if (opts.lengthDelimited !== false) {
+            writer.ldelim()
+          }
+        }, (reader, length) => {
+          const obj: any = {
+            messageIDs: []
+          }
+
+          const end = length == null ? reader.len : reader.pos + length
+
+          while (reader.pos < end) {
+            const tag = reader.uint32()
+
+            switch (tag >>> 3) {
+              case 1:
+                obj.messageIDs.push(reader.bytes())
+                break
+              default:
+                reader.skipType(tag & 7)
+                break
+            }
+          }
+
+          return obj
+        })
+      }
+
+      return _codec
+    }
+
+    export const encode = (obj: ControlIWant): Uint8Array => {
+      return encodeMessage(obj, ControlIWant.codec())
+    }
+
+    export const decode = (buf: Uint8Array | Uint8ArrayList): ControlIWant => {
+      return decodeMessage(buf, ControlIWant.codec())
+    }
+  }
+
+  export interface ControlGraft {
+    topicID?: string
+  }
+
+  export namespace ControlGraft {
+    let _codec: Codec<ControlGraft>
+
+    export const codec = (): Codec<ControlGraft> => {
+      if (_codec == null) {
+        _codec = message<ControlGraft>((obj, writer, opts = {}) => {
+          if (opts.lengthDelimited !== false) {
+            writer.fork()
+          }
+
+          if (obj.topicID != null) {
+            writer.uint32(10)
+            writer.string(obj.topicID)
+          }
+
+          if (opts.lengthDelimited !== false) {
+            writer.ldelim()
+          }
+        }, (reader, length) => {
+          const obj: any = {}
+
+          const end = length == null ? reader.len : reader.pos + length
+
+          while (reader.pos < end) {
+            const tag = reader.uint32()
+
+            switch (tag >>> 3) {
+              case 1:
+                obj.topicID = reader.string()
+                break
+              default:
+                reader.skipType(tag & 7)
+                break
+            }
+          }
+
+          return obj
+        })
+      }
+
+      return _codec
+    }
+
+    export const encode = (obj: ControlGraft): Uint8Array => {
+      return encodeMessage(obj, ControlGraft.codec())
+    }
+
+    export const decode = (buf: Uint8Array | Uint8ArrayList): ControlGraft => {
+      return decodeMessage(buf, ControlGraft.codec())
+    }
+  }
+
+  export interface ControlPrune {
+    topicID?: string
+    peers: RPC.PeerInfo[]
+    backoff?: bigint
+  }
+
+  export namespace ControlPrune {
+    let _codec: Codec<ControlPrune>
+
+    export const codec = (): Codec<ControlPrune> => {
+      if (_codec == null) {
+        _codec = message<ControlPrune>((obj, writer, opts = {}) => {
+          if (opts.lengthDelimited !== false) {
+            writer.fork()
+          }
+
+          if (obj.topicID != null) {
+            writer.uint32(10)
+            writer.string(obj.topicID)
+          }
+
+          if (obj.peers != null) {
+            for (const value of obj.peers) {
+              writer.uint32(18)
+              RPC.PeerInfo.codec().encode(value, writer)
+            }
+          } else {
+            throw new Error('Protocol error: required field "peers" was not found in object')
+          }
+
+          if (obj.backoff != null) {
+            writer.uint32(24)
+            writer.uint64(obj.backoff)
+          }
+
+          if (opts.lengthDelimited !== false) {
+            writer.ldelim()
+          }
+        }, (reader, length) => {
+          const obj: any = {
+            peers: []
+          }
+
+          const end = length == null ? reader.len : reader.pos + length
+
+          while (reader.pos < end) {
+            const tag = reader.uint32()
+
+            switch (tag >>> 3) {
+              case 1:
+                obj.topicID = reader.string()
+                break
+              case 2:
+                obj.peers.push(RPC.PeerInfo.codec().decode(reader, reader.uint32()))
+                break
+              case 3:
+                obj.backoff = reader.uint64()
+                break
+              default:
+                reader.skipType(tag & 7)
+                break
+            }
+          }
+
+          return obj
+        })
+      }
+
+      return _codec
+    }
+
+    export const encode = (obj: ControlPrune): Uint8Array => {
+      return encodeMessage(obj, ControlPrune.codec())
+    }
+
+    export const decode = (buf: Uint8Array | Uint8ArrayList): ControlPrune => {
+      return decodeMessage(buf, ControlPrune.codec())
+    }
+  }
+
+  export interface PeerInfo {
+    peerID?: Uint8Array
+    signedPeerRecord?: Uint8Array
+  }
+
+  export namespace PeerInfo {
+    let _codec: Codec<PeerInfo>
+
+    export const codec = (): Codec<PeerInfo> => {
+      if (_codec == null) {
+        _codec = message<PeerInfo>((obj, writer, opts = {}) => {
+          if (opts.lengthDelimited !== false) {
+            writer.fork()
+          }
+
+          if (obj.peerID != null) {
+            writer.uint32(10)
+            writer.bytes(obj.peerID)
+          }
+
+          if (obj.signedPeerRecord != null) {
+            writer.uint32(18)
+            writer.bytes(obj.signedPeerRecord)
+          }
+
+          if (opts.lengthDelimited !== false) {
+            writer.ldelim()
+          }
+        }, (reader, length) => {
+          const obj: any = {}
+
+          const end = length == null ? reader.len : reader.pos + length
+
+          while (reader.pos < end) {
+            const tag = reader.uint32()
+
+            switch (tag >>> 3) {
+              case 1:
+                obj.peerID = reader.bytes()
+                break
+              case 2:
+                obj.signedPeerRecord = reader.bytes()
+                break
+              default:
+                reader.skipType(tag & 7)
+                break
+            }
+          }
+
+          return obj
+        })
+      }
+
+      return _codec
+    }
+
+    export const encode = (obj: PeerInfo): Uint8Array => {
+      return encodeMessage(obj, PeerInfo.codec())
+    }
+
+    export const decode = (buf: Uint8Array | Uint8ArrayList): PeerInfo => {
+      return decodeMessage(buf, PeerInfo.codec())
+    }
+  }
+
+  let _codec: Codec<RPC>
+
+  export const codec = (): Codec<RPC> => {
+    if (_codec == null) {
+      _codec = message<RPC>((obj, writer, opts = {}) => {
+        if (opts.lengthDelimited !== false) {
+          writer.fork()
+        }
+
+        if (obj.subscriptions != null) {
+          for (const value of obj.subscriptions) {
+            writer.uint32(10)
+            RPC.SubOpts.codec().encode(value, writer)
+          }
+        } else {
+          throw new Error('Protocol error: required field "subscriptions" was not found in object')
+        }
+
+        if (obj.messages != null) {
+          for (const value of obj.messages) {
+            writer.uint32(18)
+            RPC.Message.codec().encode(value, writer)
+          }
+        } else {
+          throw new Error('Protocol error: required field "messages" was not found in object')
+        }
+
+        if (obj.control != null) {
+          writer.uint32(26)
+          RPC.ControlMessage.codec().encode(obj.control, writer)
+        }
+
+        if (opts.lengthDelimited !== false) {
+          writer.ldelim()
+        }
+      }, (reader, length) => {
+        const obj: any = {
+          subscriptions: [],
+          messages: []
+        }
+
+        const end = length == null ? reader.len : reader.pos + length
+
+        while (reader.pos < end) {
+          const tag = reader.uint32()
+
+          switch (tag >>> 3) {
+            case 1:
+              obj.subscriptions.push(RPC.SubOpts.codec().decode(reader, reader.uint32()))
+              break
+            case 2:
+              obj.messages.push(RPC.Message.codec().decode(reader, reader.uint32()))
+              break
+            case 3:
+              obj.control = RPC.ControlMessage.codec().decode(reader, reader.uint32())
+              break
+            default:
+              reader.skipType(tag & 7)
+              break
+          }
+        }
+
+        return obj
+      })
+    }
+
+    return _codec
+  }
+
+  export const encode = (obj: RPC): Uint8Array => {
+    return encodeMessage(obj, RPC.codec())
+  }
+
+  export const decode = (buf: Uint8Array | Uint8ArrayList): RPC => {
+    return decodeMessage(buf, RPC.codec())
+  }
+}
diff --git a/src/metrics.ts b/src/metrics.ts
index 888a0c17..7a1278b7 100644
--- a/src/metrics.ts
+++ b/src/metrics.ts
@@ -1,5 +1,5 @@
 import { TopicValidatorResult } from '@libp2p/interface/pubsub'
-import type { IRPC } from './message/rpc.js'
+import type { RPC } from './message/rpc.js'
 import type { PeerScoreThresholds } from './score/peer-score-thresholds.js'
 import {
   MessageStatus,
@@ -898,30 +898,30 @@ export function getMetrics(
       this.rpcDataError.inc(1)
     },
 
-    onRpcRecv(rpc: IRPC, rpcBytes: number): void {
+    onRpcRecv(rpc: RPC, rpcBytes: number): void {
       this.rpcRecvBytes.inc(rpcBytes)
       this.rpcRecvCount.inc(1)
-      if (rpc.subscriptions) this.rpcRecvSubscription.inc(rpc.subscriptions.length)
-      if (rpc.messages) this.rpcRecvMessage.inc(rpc.messages.length)
+      this.rpcRecvSubscription.inc(rpc.subscriptions.length)
+      this.rpcRecvMessage.inc(rpc.messages.length)
       if (rpc.control) {
         this.rpcRecvControl.inc(1)
-        if (rpc.control.ihave) this.rpcRecvIHave.inc(rpc.control.ihave.length)
-        if (rpc.control.iwant) this.rpcRecvIWant.inc(rpc.control.iwant.length)
-        if (rpc.control.graft) this.rpcRecvGraft.inc(rpc.control.graft.length)
-        if (rpc.control.prune) this.rpcRecvPrune.inc(rpc.control.prune.length)
+        this.rpcRecvIHave.inc(rpc.control.ihave.length)
+        this.rpcRecvIWant.inc(rpc.control.iwant.length)
+        this.rpcRecvGraft.inc(rpc.control.graft.length)
+        this.rpcRecvPrune.inc(rpc.control.prune.length)
       }
     },
 
-    onRpcSent(rpc: IRPC, rpcBytes: number): void {
+    onRpcSent(rpc: RPC, rpcBytes: number): void {
       this.rpcSentBytes.inc(rpcBytes)
       this.rpcSentCount.inc(1)
-      if (rpc.subscriptions) this.rpcSentSubscription.inc(rpc.subscriptions.length)
-      if (rpc.messages) this.rpcSentMessage.inc(rpc.messages.length)
+      this.rpcSentSubscription.inc(rpc.subscriptions.length)
+      this.rpcSentMessage.inc(rpc.messages.length)
       if (rpc.control) {
-        const ihave = rpc.control.ihave?.length ?? 0
-        const iwant = rpc.control.iwant?.length ?? 0
-        const graft = rpc.control.graft?.length ?? 0
-        const prune = rpc.control.prune?.length ?? 0
+        const ihave = rpc.control.ihave.length
+        const iwant = rpc.control.iwant.length
+        const graft = rpc.control.graft.length
+        const prune = rpc.control.prune.length
         if (ihave > 0) this.rpcSentIHave.inc(ihave)
         if (iwant > 0) this.rpcSentIWant.inc(iwant)
         if (graft > 0) this.rpcSentGraft.inc(graft)
diff --git a/src/types.ts b/src/types.ts
index 9fb512d7..4fdd131a 100644
--- a/src/types.ts
+++ b/src/types.ts
@@ -18,7 +18,7 @@ export interface AddrInfo {
  * Compute a local non-spec'ed msg-id for faster de-duplication of seen messages.
  * Used exclusively for a local seen_cache
  */
-export type FastMsgIdFn = (msg: RPC.IMessage) => string | number
+export type FastMsgIdFn = (msg: RPC.Message) => string | number
 
 /**
  * By default, gossipsub only provide a browser friendly function to convert Uint8Array message id to string.
diff --git a/src/utils/buildRawMessage.ts b/src/utils/buildRawMessage.ts
index 407de658..60916581 100644
--- a/src/utils/buildRawMessage.ts
+++ b/src/utils/buildRawMessage.ts
@@ -14,7 +14,7 @@ import { toString as uint8ArrayToString } from 'uint8arrays/to-string'
 export const SignPrefix = uint8ArrayFromString('libp2p-pubsub:')
 
 export type RawMessageAndMessage = {
-  raw: RPC.IMessage
+  raw: RPC.Message
   msg: Message
 }
 
@@ -26,7 +26,7 @@ export async function buildRawMessage(
 ): Promise<RawMessageAndMessage> {
   switch (publishConfig.type) {
     case PublishConfigType.Signing: {
-      const rpcMsg: RPC.IMessage = {
+      const rpcMsg: RPC.Message = {
         from: publishConfig.author.toBytes(),
         data: transformedData,
         seqno: randomBytes(8),
@@ -37,7 +37,7 @@ export async function buildRawMessage(
 
       // Get the message in bytes, and prepend with the pubsub prefix
       // the signature is over the bytes "libp2p-pubsub:<protobuf-message>"
-      const bytes = uint8ArrayConcat([SignPrefix, RPC.Message.encode(rpcMsg).finish()])
+      const bytes = uint8ArrayConcat([SignPrefix, RPC.Message.encode(rpcMsg)])
 
       rpcMsg.signature = await publishConfig.privateKey.sign(bytes)
       rpcMsg.key = publishConfig.key
@@ -81,7 +81,7 @@ export type ValidationResult = { valid: true; message: Message } | { valid: fals
 
 export async function validateToRawMessage(
   signaturePolicy: typeof StrictNoSign | typeof StrictSign,
-  msg: RPC.IMessage
+  msg: RPC.Message
 ): Promise<ValidationResult> {
   // If strict-sign, verify all
   // If anonymous (no-sign), ensure no preven
@@ -133,7 +133,7 @@ export async function validateToRawMessage(
         publicKey = unmarshalPublicKey(fromPeerId.publicKey)
       }
 
-      const rpcMsgPreSign: RPC.IMessage = {
+      const rpcMsgPreSign: RPC.Message = {
         from: msg.from,
         data: msg.data,
         seqno: msg.seqno,
@@ -144,7 +144,7 @@ export async function validateToRawMessage(
 
       // Get the message in bytes, and prepend with the pubsub prefix
       // the signature is over the bytes "libp2p-pubsub:<protobuf-message>"
-      const bytes = uint8ArrayConcat([SignPrefix, RPC.Message.encode(rpcMsgPreSign).finish()])
+      const bytes = uint8ArrayConcat([SignPrefix, RPC.Message.encode(rpcMsgPreSign)])
 
       if (!(await publicKey.verify(bytes, msg.signature))) {
         return { valid: false, error: ValidateError.InvalidSignature }
diff --git a/src/utils/create-gossip-rpc.ts b/src/utils/create-gossip-rpc.ts
new file mode 100644
index 00000000..93a80dc6
--- /dev/null
+++ b/src/utils/create-gossip-rpc.ts
@@ -0,0 +1,32 @@
+import type { RPC } from '../message/rpc.js'
+
+/**
+ * Create a gossipsub RPC object
+ */
+export function createGossipRpc(messages: RPC.Message[] = [], control?: Partial<RPC.ControlMessage>): RPC {
+  return {
+    subscriptions: [],
+    messages,
+    control: control
+      ? {
+          graft: control.graft || [],
+          prune: control.prune || [],
+          ihave: control.ihave || [],
+          iwant: control.iwant || []
+        }
+      : undefined
+  }
+}
+
+export function ensureControl(rpc: RPC): Required<RPC> {
+  if (!rpc.control) {
+    rpc.control = {
+      graft: [],
+      prune: [],
+      ihave: [],
+      iwant: []
+    }
+  }
+
+  return rpc as Required<RPC>
+}
diff --git a/test/benchmark/protobuf.test.ts b/test/benchmark/protobuf.test.ts
index f9017d35..4979a7c1 100644
--- a/test/benchmark/protobuf.test.ts
+++ b/test/benchmark/protobuf.test.ts
@@ -1,15 +1,14 @@
 import { itBench, setBenchOpts } from '@dapplion/benchmark'
-import { type IRPC, RPC } from '../../src/message/rpc.js'
+import { RPC } from '../../src/message/rpc.js'
 
 describe('protobuf', function () {
   this.timeout(0)
   setBenchOpts({
     maxMs: 200 * 1000,
-    minMs: 120 * 1000,
-    minRuns: 200
+    minMs: 60 * 1000
   })
 
-  const rpc: IRPC = {
+  const rpc: RPC = {
     subscriptions: [],
     messages: [
       {
@@ -25,22 +24,26 @@ describe('protobuf', function () {
     control: undefined
   }
 
-  const bytes = RPC.encode(rpc).finish()
+  const bytes = RPC.encode(rpc)
 
-  // console.log('@@@ encoded to', Buffer.from(bytes.slice()).toString('hex'), 'length', bytes.length)
+  const runsFactor = 100
 
   itBench({
-    id: 'decode Attestation message using protobufjs',
+    id: 'decode Attestation message using protons 5.1.0',
     fn: () => {
-      RPC.decode(bytes)
+      for (let i = 0; i < runsFactor; i++) {
+        RPC.decode(bytes)
+      }
     },
     runsFactor: 100
   })
 
   itBench({
-    id: 'encode Attestation message using protobufjs',
+    id: 'encode Attestation message using protons 5.1.0',
     fn: () => {
-      RPC.encode(rpc).finish()
+      for (let i = 0; i < runsFactor; i++) {
+        RPC.encode(rpc)
+      }
     },
     runsFactor: 100
   })
diff --git a/test/decodeRpc.spec.ts b/test/decodeRpc.spec.ts
index 745d0c47..f5c5f813 100644
--- a/test/decodeRpc.spec.ts
+++ b/test/decodeRpc.spec.ts
@@ -1,22 +1,22 @@
 import { expect } from 'aegir/chai'
 import { decodeRpc, type DecodeRPCLimits, defaultDecodeRpcLimits } from '../src/message/decodeRpc.js'
-import { RPC, type IRPC } from '../src/message/index.js'
+import { RPC } from '../src/message/index.js'
 
 describe('decodeRpc', () => {
   const topicID = 'topic'
   const msgID = new Uint8Array(8)
 
-  const subscription: RPC.ISubOpts = { subscribe: true, topic: topicID }
-  const message: RPC.IMessage = { topic: topicID, data: new Uint8Array(100) }
-  const peerInfo: RPC.IPeerInfo = { peerID: msgID, signedPeerRecord: msgID }
-  const ihave: RPC.IControlIHave = { topicID, messageIDs: [msgID] }
-  const iwant: RPC.IControlIWant = { messageIDs: [msgID] }
-  const graft: RPC.IControlGraft = { topicID }
-  const prune: RPC.IControlPrune = { topicID, peers: [peerInfo] }
+  const subscription: RPC.SubOpts = { subscribe: true, topic: topicID }
+  const message: RPC.Message = { topic: topicID, data: new Uint8Array(100) }
+  const peerInfo: RPC.PeerInfo = { peerID: msgID, signedPeerRecord: msgID }
+  const ihave: RPC.ControlIHave = { topicID, messageIDs: [msgID] }
+  const iwant: RPC.ControlIWant = { messageIDs: [msgID] }
+  const graft: RPC.ControlGraft = { topicID }
+  const prune: RPC.ControlPrune = { topicID, peers: [peerInfo] }
 
   describe('decode correctness', () => {
     it('Should decode full RPC', () => {
-      const rpc: IRPC = {
+      const rpc: RPC = {
         subscriptions: [subscription, subscription],
         messages: [message, message],
         control: {
@@ -27,10 +27,10 @@ describe('decodeRpc', () => {
         }
       }
 
-      const bytes = RPC.encode(rpc).finish()
+      const bytes = RPC.encode(rpc)
 
       // Compare as JSON
-      expect(RPC.fromObject(decodeRpc(bytes, defaultDecodeRpcLimits)).toJSON()).deep.equals(RPC.decode(bytes).toJSON())
+      expect(decodeRpc(bytes, defaultDecodeRpcLimits)).to.be.deep.equals(RPC.decode(bytes))
     })
   })
 
@@ -51,7 +51,7 @@ describe('decodeRpc', () => {
       expect(limitsAfter).deep.equals(decodeRpcLimits)
     })
 
-    const rpcEmpty: IRPC = {
+    const rpcEmpty: RPC = {
       subscriptions: [],
       messages: [],
       control: {
@@ -62,7 +62,7 @@ describe('decodeRpc', () => {
       }
     }
 
-    const rpcEmptyBytes = RPC.encode(rpcEmpty).finish()
+    const rpcEmptyBytes = RPC.encode(rpcEmpty)
 
     it('limit subscriptions.length', () => {
       // Decode a fresh instance to allow safe mutations
@@ -79,32 +79,38 @@ describe('decodeRpc', () => {
 
     it('limit control.ihave.length', () => {
       const rpc = RPC.decode(rpcEmptyBytes)
-      rpc.control = { ihave: [ihave, ihave, ihave] }
+      rpc.control = { ihave: [ihave, ihave, ihave], iwant: [], graft: [], prune: [] }
       expect(endecode(rpc).control?.ihave).length(decodeRpcLimits.maxControlMessages)
     })
 
     it('limit control.iwant.length', () => {
       const rpc = RPC.decode(rpcEmptyBytes)
-      rpc.control = { iwant: [iwant, iwant, iwant] }
+      // rpc.control = { iwant: [iwant, iwant, iwant], ihave: [], graft: [], prune: [] }
+      rpc.control?.iwant.push(...[iwant, iwant, iwant])
       expect(endecode(rpc).control?.iwant).length(decodeRpcLimits.maxControlMessages)
     })
 
     it('limit control.graft.length', () => {
       const rpc = RPC.decode(rpcEmptyBytes)
-      rpc.control = { graft: [graft, graft, graft] }
+      rpc.control = { graft: [graft, graft, graft], ihave: [], iwant: [], prune: [] }
       expect(endecode(rpc).control?.graft).length(decodeRpcLimits.maxControlMessages)
     })
 
     it('limit control.prune.length', () => {
       const rpc = RPC.decode(rpcEmptyBytes)
-      rpc.control = { prune: [prune, prune, prune] }
+      rpc.control = { prune: [prune, prune, prune], ihave: [], iwant: [], graft: [] }
       expect(endecode(rpc).control?.prune).length(decodeRpcLimits.maxControlMessages)
     })
 
     it('limit ihave.messageIDs.length', () => {
       const rpc = RPC.decode(rpcEmptyBytes)
       // Limit to 3 items total, 2 (all) on the first one, 1 on the second one
-      rpc.control = { ihave: [{ messageIDs: [msgID, msgID] }, { messageIDs: [msgID, msgID] }] }
+      rpc.control = {
+        ihave: [{ messageIDs: [msgID, msgID] }, { messageIDs: [msgID, msgID] }],
+        iwant: [],
+        graft: [],
+        prune: []
+      }
       expect(decodeRpcLimits.maxIhaveMessageIDs).equals(3, 'Wrong maxIhaveMessageIDs')
       expect(endecode(rpc).control?.ihave?.[0].messageIDs).length(2, 'Wrong ihave?.[0].messageIDs len')
       expect(endecode(rpc).control?.ihave?.[1].messageIDs).length(1, 'Wrong ihave?.[1].messageIDs len')
@@ -113,7 +119,12 @@ describe('decodeRpc', () => {
     it('limit iwant.messageIDs.length', () => {
       const rpc = RPC.decode(rpcEmptyBytes)
       // Limit to 3 items total, 2 (all) on the first one, 1 on the second one
-      rpc.control = { iwant: [{ messageIDs: [msgID, msgID] }, { messageIDs: [msgID, msgID] }] }
+      rpc.control = {
+        iwant: [{ messageIDs: [msgID, msgID] }, { messageIDs: [msgID, msgID] }],
+        ihave: [],
+        graft: [],
+        prune: []
+      }
       expect(decodeRpcLimits.maxIwantMessageIDs).equals(3, 'Wrong maxIwantMessageIDs')
       expect(endecode(rpc).control?.iwant?.[0].messageIDs).length(2, 'Wrong iwant?.[0].messageIDs len')
       expect(endecode(rpc).control?.iwant?.[1].messageIDs).length(1, 'Wrong iwant?.[1].messageIDs len')
@@ -122,14 +133,19 @@ describe('decodeRpc', () => {
     it('limit prune.peers.length', () => {
       const rpc = RPC.decode(rpcEmptyBytes)
       // Limit to 3 items total, 2 (all) on the first one, 1 on the second one
-      rpc.control = { prune: [{ peers: [peerInfo, peerInfo] }, { peers: [peerInfo, peerInfo] }] }
+      rpc.control = {
+        prune: [{ peers: [peerInfo, peerInfo] }, { peers: [peerInfo, peerInfo] }],
+        ihave: [],
+        iwant: [],
+        graft: []
+      }
       expect(decodeRpcLimits.maxPeerInfos).equals(3, 'Wrong maxPeerInfos')
       expect(endecode(rpc).control?.prune?.[0].peers).length(2, 'Wrong prune?.[0].peers len')
       expect(endecode(rpc).control?.prune?.[1].peers).length(1, 'Wrong prune?.[1].peers len')
     })
 
-    function endecode(rpc: IRPC): IRPC {
-      return decodeRpc(RPC.encode(rpc).finish(), decodeRpcLimits)
+    function endecode(rpc: RPC): RPC {
+      return decodeRpc(RPC.encode(rpc), decodeRpcLimits)
     }
   })
 })
diff --git a/test/e2e/go-gossipsub.spec.ts b/test/e2e/go-gossipsub.spec.ts
index 643f5543..5730d302 100644
--- a/test/e2e/go-gossipsub.spec.ts
+++ b/test/e2e/go-gossipsub.spec.ts
@@ -7,7 +7,7 @@ import type { GossipSub } from '../../src/index.js'
 import { GossipsubD } from '../../src/constants.js'
 import { fastMsgIdFn } from '../utils/index.js'
 import { type Message, TopicValidatorResult } from '@libp2p/interface/pubsub'
-import type { IRPC, RPC } from '../../src/message/rpc.js'
+import type { RPC } from '../../src/message/rpc.js'
 import type { Libp2pEvents } from '@libp2p/interface'
 import pWaitFor from 'p-wait-for'
 import {
@@ -1184,13 +1184,13 @@ describe('go-libp2p-pubsub gossipsub tests', function () {
     psub.mesh.set(topic1, new Set([otherId]))
     psub.mesh.set(topic2, new Set())
 
-    const rpc: IRPC = {
+    const rpc: RPC = {
       subscriptions: [],
       messages: []
     }
 
-    const toGraft = (topicID: string): RPC.IControlGraft => ({ topicID })
-    const toPrune = (topicID: string): RPC.IControlPrune => ({ topicID, peers: [] })
+    const toGraft = (topicID: string): RPC.ControlGraft => ({ topicID })
+    const toPrune = (topicID: string): RPC.ControlPrune => ({ topicID, peers: [] })
 
     psub.piggybackControl(otherId, rpc, {
       graft: [toGraft(topic1), toGraft(topic2), toGraft(topic3)],
@@ -1199,12 +1199,14 @@ describe('go-libp2p-pubsub gossipsub tests', function () {
       iwant: []
     })
 
-    const expectedRpc: IRPC = {
+    const expectedRpc: RPC = {
       subscriptions: [],
       messages: [],
       control: {
         graft: [toGraft(topic1)],
-        prune: [toPrune(topic2), toPrune(topic3)]
+        prune: [toPrune(topic2), toPrune(topic3)],
+        ihave: [],
+        iwant: []
       }
     }
 
diff --git a/test/message-cache.spec.ts b/test/message-cache.spec.ts
index 265f1a59..61e64a08 100644
--- a/test/message-cache.spec.ts
+++ b/test/message-cache.spec.ts
@@ -16,7 +16,7 @@ const toMessageId = (msgId: Uint8Array): MessageId => {
 
 describe('Testing Message Cache Operations', () => {
   const messageCache = new MessageCache(3, 5, messageIdToString)
-  const testMessages: RPC.IMessage[] = []
+  const testMessages: RPC.Message[] = []
   const topic = 'test'
   const getGossipIDs = (mcache: MessageCache, topic: string): Uint8Array[] => {
     const gossipIDsByTopic = mcache.getGossipIDs(new Set([topic]))
@@ -24,7 +24,7 @@ describe('Testing Message Cache Operations', () => {
   }
 
   before(async () => {
-    const makeTestMessage = (n: number): RPC.IMessage => {
+    const makeTestMessage = (n: number): RPC.Message => {
       return {
         from: new Uint8Array(0),
         data: uint8ArrayFromString(n.toString()),
diff --git a/test/utils/index.ts b/test/utils/index.ts
index 850aa15d..abdb49a2 100644
--- a/test/utils/index.ts
+++ b/test/utils/index.ts
@@ -15,7 +15,7 @@ export const createPeerId = async (): Promise<PeerId> => {
 let seq = 0n
 const defaultPeer = uint8ArrayFromString('12D3KooWBsYhazxNL7aeisdwttzc6DejNaM48889t5ifiS6tTrBf', 'base58btc')
 
-export function makeTestMessage(i: number, topic: TopicStr, from?: PeerId): RPC.IMessage {
+export function makeTestMessage(i: number, topic: TopicStr, from?: PeerId): RPC.Message {
   return {
     seqno: uint8ArrayFromString((seq++).toString(16).padStart(16, '0'), 'base16'),
     data: Uint8Array.from([i]),
diff --git a/test/utils/msgId.ts b/test/utils/msgId.ts
index b3d445b0..1a5f463d 100644
--- a/test/utils/msgId.ts
+++ b/test/utils/msgId.ts
@@ -3,7 +3,7 @@ import type { RPC } from '../../src/message/rpc.js'
 import { fromString as uint8ArrayFromString } from 'uint8arrays/from-string'
 import { messageIdToString } from '../../src/utils/messageIdToString.js'
 
-export const getMsgId = (msg: RPC.IMessage): Uint8Array => {
+export const getMsgId = (msg: RPC.Message): Uint8Array => {
   const from = msg.from != null ? msg.from : new Uint8Array(0)
   const seqno = msg.seqno instanceof Uint8Array ? msg.seqno : uint8ArrayFromString(msg.seqno ?? '')
   const result = new Uint8Array(from.length + seqno.length)
@@ -12,9 +12,9 @@ export const getMsgId = (msg: RPC.IMessage): Uint8Array => {
   return result
 }
 
-export const getMsgIdStr = (msg: RPC.IMessage): string => messageIdToString(getMsgId(msg))
+export const getMsgIdStr = (msg: RPC.Message): string => messageIdToString(getMsgId(msg))
 
-export const fastMsgIdFn = (msg: RPC.IMessage): string =>
+export const fastMsgIdFn = (msg: RPC.Message): string =>
   // eslint-disable-next-line @typescript-eslint/ban-ts-comment
   // @ts-expect-error @chainsafe/as-sha256 types are wrong
   msg.data != null ? messageIdToString(SHA256.default.digest(msg.data)) : '0'
diff --git a/tsconfig.json b/tsconfig.json
index 10e6081b..13e1e3bb 100644
--- a/tsconfig.json
+++ b/tsconfig.json
@@ -5,8 +5,6 @@
     "noImplicitReturns": true,
     "outDir": "dist",
     "useUnknownInCatchVariables": true,
-    "allowJs": false,
-    "checkJs": false
   },
   "include": [
     "src",