diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml new file mode 100644 index 0000000..fd1650c --- /dev/null +++ b/.github/workflows/release.yml @@ -0,0 +1,59 @@ +on: workflow_dispatch + +name: Publish VS Code Extension to Microsoft Marketplace and Open VSX +jobs: + deploy: + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v3 + - name: 'Install nodejs 18' + uses: actions/setup-node@v2 + with: + node-version: '18' + # Run install dependencies + - name: Install dependencies + run: | + corepack enable + yarn install + # Run tests + - name: Build + run: yarn run build + - name: Get current package version + id: package_version + uses: martinbeentjes/npm-get-version-action@v1.1.0 + - name: Check version is mentioned in Changelog + uses: mindsers/changelog-reader-action@v2.0.0 + with: + version: ${{ steps.package_version.outputs.current-version }} + path: 'CHANGELOG.md' + - name: Create a Release + id: create_release + uses: actions/create-release@v1 + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + with: + tag_name : ${{ steps.package_version.outputs.current-version}} + release_name: ${{ steps.package_version.outputs.current-version}} + body: Publish ${{ steps.package_version.outputs.current-version}} + - name: Publish extension to Visual Studio Marketplace + id: create_vsix + uses: HaaLeo/publish-vscode-extension@v1 + with: + pat: ${{ secrets.VS_MARKETPLACE_TOKEN }} + registryUrl: https://marketplace.visualstudio.com + - name: Publish extension to Open VSX + uses: HaaLeo/publish-vscode-extension@v1 + with: + pat: ${{ secrets.OPEN_VSX_TOKEN }} + extensionFile: ${{ steps.create_vsix.outputs.vsixPath }} + packagePath: '' + - name: Attach vsix to release + uses: actions/upload-release-asset@v1 + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + with: + upload_url: ${{ steps.create_release.outputs.upload_url }} + asset_path: ${{ steps.create_vsix.outputs.vsixPath}} + asset_name: ${{ steps.create_vsix.outputs.vsixPath}} + asset_content_type: application/vsix \ No newline at end of file diff --git a/LICENSE.md b/LICENSE.md new file mode 100644 index 0000000..19363d0 --- /dev/null +++ b/LICENSE.md @@ -0,0 +1,29 @@ +BSD 3-Clause License + +Copyright (c) 2023-2024, Runtime Verification Inc. +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +1. Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + +2. Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +3. Neither the name of the copyright holder nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. \ No newline at end of file diff --git a/package-lock.json b/package-lock.json index 1d85a90..be64ba0 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,15 +1,16 @@ { "name": "simbolik", - "version": "1.1.0-beta", + "version": "2.0.0-beta", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "simbolik", - "version": "1.1.0-beta", + "version": "2.0.0-beta", "dependencies": { "@solidity-parser/parser": "^0.18.0", "@types/ws": "^8.5.10", + "toml": "^3.0.0", "ws": "^8.16.0" }, "devDependencies": { @@ -34,12 +35,12 @@ } }, "node_modules/@babel/code-frame": { - "version": "7.24.2", - "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.24.2.tgz", - "integrity": "sha512-y5+tLQyV8pg3fsiln67BVLD1P13Eg4lh5RW9mF0zUuvLrv9uIQ4MCL+CRT+FTsBlBjcIan6PGsLcBN0m3ClUyQ==", + "version": "7.24.6", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.24.6.tgz", + "integrity": "sha512-ZJhac6FkEd1yhG2AHOmfcXG4ceoLltoCVJjN5XsWN9BifBQr+cHJbWi0h68HZuSORq+3WtJ2z0hwF2NG1b5kcA==", "dev": true, "dependencies": { - "@babel/highlight": "^7.24.2", + "@babel/highlight": "^7.24.6", "picocolors": "^1.0.0" }, "engines": { @@ -47,21 +48,21 @@ } }, "node_modules/@babel/helper-validator-identifier": { - "version": "7.22.20", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.22.20.tgz", - "integrity": "sha512-Y4OZ+ytlatR8AI+8KZfKuL5urKp7qey08ha31L8b3BwewJAoJamTzyvxPR/5D+KkdJCGPq/+8TukHBlY10FX9A==", + "version": "7.24.6", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.24.6.tgz", + "integrity": "sha512-4yA7s865JHaqUdRbnaxarZREuPTHrjpDT+pXoAZ1yhyo6uFnIEpS8VMu16siFOHDpZNKYv5BObhsB//ycbICyw==", "dev": true, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/highlight": { - "version": "7.24.2", - "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.24.2.tgz", - "integrity": "sha512-Yac1ao4flkTxTteCDZLEvdxg2fZfz1v8M4QpaGypq/WPDqg3ijHYbDfs+LG5hvzSoqaSZ9/Z9lKSP3CjZjv+pA==", + "version": "7.24.6", + "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.24.6.tgz", + "integrity": "sha512-2YnuOp4HAk2BsBrJJvYCbItHx0zWscI1C3zgWkz+wDyD9I7GIVrfnLyrR4Y1VR+7p+chAEcrgRQYZAGIKMV7vQ==", "dev": true, "dependencies": { - "@babel/helper-validator-identifier": "^7.22.20", + "@babel/helper-validator-identifier": "^7.24.6", "chalk": "^2.4.2", "js-tokens": "^4.0.0", "picocolors": "^1.0.0" @@ -807,9 +808,9 @@ "dev": true }, "node_modules/@types/vscode": { - "version": "1.88.0", - "resolved": "https://registry.npmjs.org/@types/vscode/-/vscode-1.88.0.tgz", - "integrity": "sha512-rWY+Bs6j/f1lvr8jqZTyp5arRMfovdxolcqGi+//+cPDOh8SBvzXH90e7BiSXct5HJ9HGW6jATchbRTpTJpEkw==", + "version": "1.89.0", + "resolved": "https://registry.npmjs.org/@types/vscode/-/vscode-1.89.0.tgz", + "integrity": "sha512-TMfGKLSVxfGfoO8JfIE/neZqv7QLwS4nwPwL/NwMvxtAY2230H2I4Z5xx6836pmJvMAzqooRQ4pmLm7RUicP3A==", "dev": true }, "node_modules/@types/ws": { @@ -1015,9 +1016,9 @@ "dev": true }, "node_modules/@vscode/test-electron": { - "version": "2.3.9", - "resolved": "https://registry.npmjs.org/@vscode/test-electron/-/test-electron-2.3.9.tgz", - "integrity": "sha512-z3eiChaCQXMqBnk2aHHSEkobmC2VRalFQN0ApOAtydL172zXGxTwGrRtviT5HnUB+Q+G3vtEYFtuQkYqBzYgMA==", + "version": "2.3.10", + "resolved": "https://registry.npmjs.org/@vscode/test-electron/-/test-electron-2.3.10.tgz", + "integrity": "sha512-FxMqrvUm6a8S5tP4CymNJ40e6kD+wUTWTc6K32U629yrCCa+kl/rmpkC2gKpN4F4zjg1r+0Hnk9sl0+N2atsYA==", "dev": true, "dependencies": { "http-proxy-agent": "^4.0.1", @@ -1399,12 +1400,12 @@ } }, "node_modules/braces": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", - "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz", + "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==", "dev": true, "dependencies": { - "fill-range": "^7.0.1" + "fill-range": "^7.1.1" }, "engines": { "node": ">=8" @@ -1492,9 +1493,9 @@ } }, "node_modules/caniuse-lite": { - "version": "1.0.30001613", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001613.tgz", - "integrity": "sha512-BNjJULJfOONQERivfxte7alLfeLW4QnwHvNW4wEcLEbXfV6VSCYvr+REbf2Sojv8tC1THpjPXBxWgDbq4NtLWg==", + "version": "1.0.30001621", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001621.tgz", + "integrity": "sha512-+NLXZiviFFKX0fk8Piwv3PfLPGtRqJeq2TiNoUff/qB5KJgwecJTvCXDpmlyP/eCI/GUEmp/h/y5j0yckiiZrA==", "dev": true, "funding": [ { @@ -1757,9 +1758,9 @@ } }, "node_modules/electron-to-chromium": { - "version": "1.4.750", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.750.tgz", - "integrity": "sha512-9ItEpeu15hW5m8jKdriL+BQrgwDTXEL9pn4SkillWFu73ZNNNQ2BKKLS+ZHv2vC9UkNhosAeyfxOf/5OSeTCPA==", + "version": "1.4.783", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.783.tgz", + "integrity": "sha512-bT0jEz/Xz1fahQpbZ1D7LgmPYZ3iHVY39NcWWro1+hA2IvjiPeaXtfSqrQ+nXjApMvQRE2ASt1itSLRrebHMRQ==", "dev": true, "peer": true }, @@ -1770,9 +1771,9 @@ "dev": true }, "node_modules/enhanced-resolve": { - "version": "5.16.0", - "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.16.0.tgz", - "integrity": "sha512-O+QWCviPNSSLAD9Ucn8Awv+poAkqn3T1XY5/N7kR7rQO9yfSGWkYZDwpJ+iKF7B8rxaQKWngSqACpgzeapSyoA==", + "version": "5.16.1", + "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.16.1.tgz", + "integrity": "sha512-4U5pNsuDl0EhuZpq46M5xPslstkviJuhrdobaRDBk2Jy2KO37FDAJl4lb2KlNabxT0m4MTK2UHNrsAcphE8nyw==", "dev": true, "dependencies": { "graceful-fs": "^4.2.4", @@ -1792,9 +1793,9 @@ } }, "node_modules/es-module-lexer": { - "version": "1.5.2", - "resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-1.5.2.tgz", - "integrity": "sha512-l60ETUTmLqbVbVHv1J4/qj+M8nq7AwMzEcg3kmJDt9dCNrTk+yHcYFf/Kw75pMDwd9mPcIGCG5LcS20SxYRzFA==", + "version": "1.5.3", + "resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-1.5.3.tgz", + "integrity": "sha512-i1gCgmR9dCl6Vil6UKPI/trA69s08g/syhiDK9TG0Nf1RJjjFI+AzoWW7sPufzkgYAn861skuCwJa0pIIHYxvg==", "dev": true, "peer": true }, @@ -2298,9 +2299,9 @@ } }, "node_modules/fill-range": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", - "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", + "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==", "dev": true, "dependencies": { "to-regex-range": "^5.0.1" @@ -2783,6 +2784,7 @@ "version": "1.0.6", "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", + "deprecated": "This module is not supported, and leaks memory. Do not use it. Check out lru-cache if you want a good and tested way to coalesce async requests by a key value, which is much more comprehensive and powerful.", "dev": true, "dependencies": { "once": "^1.3.0", @@ -3216,12 +3218,12 @@ } }, "node_modules/micromatch": { - "version": "4.0.5", - "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.5.tgz", - "integrity": "sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA==", + "version": "4.0.7", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.7.tgz", + "integrity": "sha512-LPP/3KorzCwBxfeUuZmaR6bG2kdeHSbe0P2tY3FLRU4vYrjYz5hI4QZwV0njUx3jeuKe67YukQ1LSPZBKDqO/Q==", "dev": true, "dependencies": { - "braces": "^3.0.2", + "braces": "^3.0.3", "picomatch": "^2.3.1" }, "engines": { @@ -3632,9 +3634,9 @@ } }, "node_modules/picocolors": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz", - "integrity": "sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==", + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.1.tgz", + "integrity": "sha512-anP1Z8qwhkbmu7MFP5iTt+wQKXgwzf7zTyGlcdzabySa9vd0Xt392U0rVmz9poOaBj0uHJKyyo9/upk0HrEQew==", "dev": true }, "node_modules/picomatch": { @@ -4097,13 +4099,10 @@ } }, "node_modules/semver": { - "version": "7.6.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.0.tgz", - "integrity": "sha512-EnwXhrlwXMk9gKu5/flx5sv/an57AkRplG3hTK68W7FRDN+k+OWBj65M7719OkA82XLBxrcX0KSHj+X5COhOVg==", + "version": "7.6.2", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.2.tgz", + "integrity": "sha512-FNAIBWCx9qcRhoHcgcJ0gvU7SN1lYU2ZXuSfl04bSC5OpvDHFyJCjdNHomPXxjQlCBU67YW64PzY7/VIEH7F2w==", "dev": true, - "dependencies": { - "lru-cache": "^6.0.0" - }, "bin": { "semver": "bin/semver.js" }, @@ -4219,9 +4218,9 @@ } }, "node_modules/spdx-license-ids": { - "version": "3.0.17", - "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.17.tgz", - "integrity": "sha512-sh8PWc/ftMqAAdFiBu6Fy6JUOYjqDJBJvIhpfDMyHrr0Rbp5liZqd4TjtQ/RgfLjKFZb+LMx5hpml5qOWy0qvg==", + "version": "3.0.18", + "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.18.tgz", + "integrity": "sha512-xxRs31BqRYHwiMzudOrpSiHtZ8i/GeionCBDSilhYRj+9gIcI8wCZTlXZKu9vZIVqViP3dcp9qE5G6AlIaD+TQ==", "dev": true }, "node_modules/string_decoder": { @@ -4348,9 +4347,9 @@ } }, "node_modules/terser": { - "version": "5.30.4", - "resolved": "https://registry.npmjs.org/terser/-/terser-5.30.4.tgz", - "integrity": "sha512-xRdd0v64a8mFK9bnsKVdoNP9GQIKUAaJPTaqEQDL4w/J8WaW4sWXXoMZ+6SimPkfT5bElreXf8m9HnmPc3E1BQ==", + "version": "5.31.0", + "resolved": "https://registry.npmjs.org/terser/-/terser-5.31.0.tgz", + "integrity": "sha512-Q1JFAoUKE5IMfI4Z/lkE/E6+SwgzO+x4tq4v1AyBLRj8VSYvRO6A/rQrPg1yud4g0En9EKI1TvFRF2tQFcoUkg==", "dev": true, "peer": true, "dependencies": { @@ -4447,6 +4446,11 @@ "node": ">=8.0" } }, + "node_modules/toml": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/toml/-/toml-3.0.0.tgz", + "integrity": "sha512-y/mWCZinnvxjTKYhJ+pYxwD0mRLVvOtdS2Awbgxln6iEnt4rk0yBxeSBHkGJcPucRiG0e55mwWp+g/05rsrd6w==" + }, "node_modules/trim-newlines": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/trim-newlines/-/trim-newlines-3.0.1.tgz", @@ -4540,9 +4544,9 @@ "integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==" }, "node_modules/update-browserslist-db": { - "version": "1.0.13", - "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.0.13.tgz", - "integrity": "sha512-xebP81SNcPuNpPP3uzeW1NYXxI3rxyJzF3pD6sH4jE7o/IX+WtSpwnVU+qIsDPyk0d3hmFQ7mjqc6AtV604hbg==", + "version": "1.0.16", + "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.0.16.tgz", + "integrity": "sha512-KVbTxlBYlckhF5wgfyZXTWnMn7MMZjMu9XG8bPlliUOP9ThaF4QnhP8qrjrH7DRzHfSk0oQv1wToW+iA5GajEQ==", "dev": true, "funding": [ { @@ -4560,8 +4564,8 @@ ], "peer": true, "dependencies": { - "escalade": "^3.1.1", - "picocolors": "^1.0.0" + "escalade": "^3.1.2", + "picocolors": "^1.0.1" }, "bin": { "update-browserslist-db": "cli.js" diff --git a/package.json b/package.json index 5a50b6f..203123d 100644 --- a/package.json +++ b/package.json @@ -11,7 +11,7 @@ }, "publisher": "runtimeverification", "description": "Advanced Solidity and EVM Debugger", - "version": "1.1.0-beta", + "version": "2.0.0-beta", "engines": { "vscode": "^1.79.0" }, @@ -43,70 +43,89 @@ "configuration": { "title": "Simbolik: Solidity Debugger", "properties": { + "simbolik.api-key": { + "type": "string", + "default": "valid-api-key", + "description": "The API key to authenticate with the simbolik server.", + "order": 0 + }, "simbolik.server": { "type": "string", - "default": "ws://localhost:6789", - "description": "The websocket URL where the simbolik server is listening." + "default": "ws://beta.simbolik.runtimeverification.com:3000", + "description": "The websocket URL where the simbolik server is listening. Do not change this unless you are running your own simbolik server.", + "order": 1 }, - "simbolik.bmc-depth": { - "type": "integer", - "description": "Signals the backend that it should only unroll loops up to this depth. This parameter is only relevant for symbolic execution backends.", - "default": 3 + "simbolik.forge-path": { + "type": "string", + "default": "forge", + "description": "The path to the forge executable. Simbolik uses forge to compile the smart contracts. If forge is not in the PATH, you can set the path here.", + "order": 2 }, - "simbolik.stop-at-first-opcode": { + "simbolik.autobuild": { "type": "boolean", - "description": "If set to true, the debugger will stop at the first opcode. Otherwise it will stop at the function entry.", - "default": true + "default": true, + "description": "If set to true, the debugger will automatically build the project before starting the debugger.", + "order": 3 }, - "simbolik.show-sourcemaps": { + "simbolik.incremental-build": { "type": "boolean", "default": false, - "description": "If set to true, the debugger will include sourcemaps in the disassembly view. This is useful when debugging sourcemaps." + "description": "If autobuild is eanbled and incremental-build is set to true, the debugger will use incremental builds. Notice, that the support for incremental builds is experimental and sometimes leads to unexpected behavior.", + "order": 4 + }, + "simbolik.stop-at-first-opcode": { + "type": "boolean", + "description": "If set to true, the debugger will stop at the first opcode. Otherwise it will stop at the function entry. Disabling this option is experimental and may lead to unexpected behavior.", + "default": true, + "order": 5 }, "simbolik.anvil-port": { "type": "integer", "default": 8545, - "description": "The port where the Anvil server is listening. If anvil-autostart is set to true, the debugger passes this port to the anvil server." + "description": "The port where the Anvil server is listening. If anvil-autostart is set to true, the debugger passes this port to the anvil server. This options is only used when you're running your own simbolik server.", + "order": 6 }, "simbolik.simbolik-autostart": { "type": "boolean", - "default": true, - "description": "If set to true, the debugger will start the simbolik server automatically with VSCode." + "default": false, + "description": "If set to true, the debugger will start the simbolik server automatically with VSCode. This option can only be used if you have the simbolik server installed on your machine.", + "order": 7 }, "simbolik.simbolik-path": { "type": "string", "default": "simbolik", - "description": "The path to the simbolik executable. If simbolik-autostart is set to true, the debugger will start the simbolik server with this executable. If simbolik is not in the PATH, you can set the path here." + "description": "The path to the simbolik executable. If simbolik-autostart is set to true, the debugger will start the simbolik server with this executable. If simbolik is not in the PATH, you can set the path here.", + "order": 8 }, "simbolik.anvil-autostart": { "type": "boolean", - "default": true, - "description": "If set to true, the debugger will start the anvil server automatically with VSCode." + "default": false, + "description": "If set to true, the debugger will start the anvil server for every debugging session. Only set this to true if you are running your own simbolik server.", + "order": 9 }, "simbolik.anvil-path": { "type": "string", "default": "anvil", - "description": "The path to the anvil executable. If anvil-autostart is set to true, the debugger will start the anvil server with this executable. If anvil is not in the PATH, you can set the path here." - }, - "simbolik.forge-path": { - "type": "string", - "default": "forge", - "description": "The path to the forge executable. Simbolik uses forge to compile the smart contracts. If forge is not in the PATH, you can set the path here." - }, - "simbolik.autobuild": { - "type": "boolean", - "default": true, - "description": "If set to true, the debugger will automatically build the project before starting the debugger." + "description": "The path to the anvil executable. If anvil-autostart is set to true, the debugger will start the anvil server with this executable. If anvil is not in the PATH, you can set the path here.", + "order": 10 }, - "simbolik.incremental-build": { + "simbolik.enable-parameters": { "type": "boolean", "default": false, - "description": "If autobuild is eanbled and incremental-build is set to true, the debugger will use incremental builds. Notice, that the support for incremental builds is experimental and sometimes leads to unexpected sourcemaps." + "description": "If set to true, the debugger will show a debug button above functions with parameters. Notice, that this requires a backend that supports parameter debugging. The default Foundry backend does not support parameter debugging.", + "order": 11 }, - "simbolik.enable-parameters": { + "simbolik.show-sourcemaps": { "type": "boolean", "default": false, - "description": "If set to true, the debugger will show a debug button above functions with parameters. Notice, that this requires a backend that supports parameter debugging. The default Foundry backend does not support parameter debugging." + "description": "If set to true, the debugger will include sourcemaps in the disassembly view. This is useful when debugging sourcemaps.", + "order": 12 + }, + "simbolik.bmc-depth": { + "type": "integer", + "description": "Signals the backend that it should only unroll loops up to this depth. This parameter is only relevant for symbolic execution backends.", + "default": 3, + "order": 13 } } }, @@ -190,6 +209,7 @@ "dependencies": { "@solidity-parser/parser": "^0.18.0", "@types/ws": "^8.5.10", - "ws": "^8.16.0" + "ws": "^8.16.0", + "toml": "^3.0.0" } } diff --git a/src/DebugAdapter.ts b/src/DebugAdapter.ts index dc393d5..1ebfed1 100644 --- a/src/DebugAdapter.ts +++ b/src/DebugAdapter.ts @@ -60,6 +60,11 @@ function retry( }); } +type WithApiKey = { apiKey: string }; +type WithClientVersion = { clientVersion: string }; + +type DebugProtocolMessage = vscode.DebugProtocolMessage & WithApiKey & WithClientVersion; + class WebsocketDebugAdapter implements vscode.DebugAdapter { _onDidSendMessage = new vscode.EventEmitter(); @@ -74,7 +79,10 @@ class WebsocketDebugAdapter implements vscode.DebugAdapter { onDidSendMessage = this._onDidSendMessage.event; handleMessage(message: vscode.DebugProtocolMessage): void { - this.websocket.send(JSON.stringify(message)); + const apiKey = getConfigValue('api-key', ''); + const clientVersion = vscode.extensions.getExtension('simbolik.simbolik')?.packageJSON.version; + const messageWithApiKey : DebugProtocolMessage = Object.assign({}, message, {apiKey, clientVersion}); + this.websocket.send(JSON.stringify(messageWithApiKey)); } dispose() { diff --git a/src/extension.ts b/src/extension.ts index ccad50a..90cfb35 100644 --- a/src/extension.ts +++ b/src/extension.ts @@ -66,6 +66,33 @@ export function activate(context: vscode.ExtensionContext) { supervisor.anvilTerminate(); outputChannel.info(`Debug session ended: ${session.id}`); }); + + vscode.debug.onDidReceiveDebugSessionCustomEvent(async event => { + if (event.event === 'api-key-validation-failed') { + const action = await vscode.window.showErrorMessage( + 'API key validation failed', + 'Open Settings', + 'Learn More' + ); + if (action === 'Open Settings') { + vscode.commands.executeCommand( + 'workbench.action.openSettings', + 'simbolik.api-key' + ); + } + if (action === 'Learn More') { + vscode.env.openExternal( + vscode.Uri.parse('https://simbolik.runtimeverification.com') + ); + } + } + if (event.event === 'api-key-sessions-limit-exceeded') { + const action = await vscode.window.showErrorMessage( + 'Too many debugging sessions running in parallel' + ); + } + console.log(event); + }); } // This method is called when your extension is deactivated diff --git a/src/foundry.ts b/src/foundry.ts new file mode 100644 index 0000000..58128f6 --- /dev/null +++ b/src/foundry.ts @@ -0,0 +1,113 @@ +import * as vscode from 'vscode'; +import {getConfigValue} from './utils'; +import {parse as parseToml} from 'toml'; + + +export +function forgeBuildTask(file: string) { + const incrementalBuild = getConfigValue('incremental-build', false); + const forgePath = getConfigValue('forge-path', 'forge'); + const cwd = file.substring(0, file.lastIndexOf('/')); + const task = new vscode.Task( + { + label: 'forge build', + type: 'shell', + }, + vscode.TaskScope.Workspace, + 'forge', + 'simbolik', + new vscode.ShellExecution(forgePath, ['build'], { + cwd, + env: { + 'FOUNDRY_OPTIMIZER': 'false', + 'FOUNDRY_BUILD_INFO': 'true', + 'FOUNDRY_EXTRA_OUTPUT': '["storageLayout", "evm.bytecode.generatedSources"]', + 'FOUNDRY_BYTECODE_HASH': 'ipfs', + 'FOUNDRY_CBOR_METADATA': 'true', + 'FOUNDRY_CACHE': incrementalBuild ? 'true' : 'false', + } + }) + ); + task.isBackground = true; + task.presentationOptions.reveal = vscode.TaskRevealKind.Always; + return task; +} + +export +async function loadBuildInfo(file: string): Promise { + const root = await foundryRoot(file); + const buildInfo = await forgeBuildInfo(root); + return buildInfo; +} + +export +async function foundryRoot(file: string) { + // Find the root of the project, which is the directory containing the foundry.toml file + let root = file; + let stat; + try { + stat = await vscode.workspace.fs.stat(vscode.Uri.file(`${root}/foundry.toml`)); + } catch (e) { + stat = false; + } + while (!stat) { + const lastSlash = root.lastIndexOf('/'); + if (lastSlash === -1) { + throw new Error('Could not find foundry.toml'); + } + root = root.substring(0, lastSlash); + try { + stat = await vscode.workspace.fs.stat(vscode.Uri.file(`${root}/foundry.toml`)); + } catch (e) { + stat = false; + } + } + return root; +} + +export +type FoundryConfig = { 'profile'?: { [profile: string]: { [key: string]: string } } }; + +export +async function foundryConfig(root: string): Promise { + const configPath = `${root}/foundry.toml`; + const config = await vscode.workspace.fs.readFile(vscode.Uri.file(configPath)); + return parseToml(config.toString()); +} + +async function forgeBuildInfo(root: string): Promise { + const config = await foundryConfig(root); + const out = config?.profile?.default?.out ?? 'out'; + + // Get the contents of the youngest build-info file + const buildInfoDir = `${root}/${out}/build-info`; + + // Get list of build-info files + const files = await vscode.workspace.fs.readDirectory(vscode.Uri.file(buildInfoDir)); + const buildInfoFiles = files.filter(([file, type]) => type === vscode.FileType.File && file.endsWith('.json')); + + if (buildInfoFiles.length === 0) { + vscode.window.showErrorMessage('No build-info files found'); + return ''; + } + + // Retrieve file stats and sort by creation timestamp + const sortedFiles = await getSortedFilesByCreationTime(buildInfoDir, buildInfoFiles); + + // Read the youngest build-info file + const youngestBuildInfo = await vscode.workspace.fs.readFile(sortedFiles[0].uri); + return youngestBuildInfo.toString(); +} + +async function getSortedFilesByCreationTime(buildInfoDir: string, buildInfoFiles: [string, vscode.FileType][]): Promise<{ file: string, uri: vscode.Uri, ctime: number }[]> { + const filesWithStats = await Promise.all( + buildInfoFiles.map(async ([file]) => { + const fileUri = vscode.Uri.file(`${buildInfoDir}/${file}`); + const fileStat = await vscode.workspace.fs.stat(fileUri); + return { file, uri: fileUri, ctime: fileStat.ctime }; + }) + ); + + // Sort files by creation time (ctime) + return filesWithStats.sort((a, b) => b.ctime - a.ctime); +} \ No newline at end of file diff --git a/src/startDebugging.ts b/src/startDebugging.ts index 8926bca..f8589fd 100644 --- a/src/startDebugging.ts +++ b/src/startDebugging.ts @@ -5,8 +5,9 @@ import { VariableDeclaration, } from '@solidity-parser/parser/dist/src/ast-types'; import * as vscode from 'vscode'; -import {getConfigValue} from './utils'; -import {Supervisor} from './supevervisor'; +import { getConfigValue } from './utils'; +import { Supervisor } from './supevervisor'; +import { forgeBuildTask, foundryRoot, loadBuildInfo } from './foundry'; export async function startDebugging( this: Supervisor, @@ -55,42 +56,38 @@ export async function startDebugging( const file = activeTextEditor.document.uri.toString(); const contractName = contract['name']; const methodSignature = `${method['name']}(${parameters.join(',')})`; - const stopAtFirstOpcode = getConfigValue('stop-at-first-opcode', false); const showSourcemaps = getConfigValue('show-sourcemaps', false); const debugConfigName = `${contractName}.${methodSignature}`; const anvilPort = getConfigValue('anvil-port', '8545'); const rpcUrl = `http://localhost:${anvilPort}`; - const myDebugConfig = debugConfig( - debugConfigName, - file, - contractName, - methodSignature, - stopAtFirstOpcode, - showSourcemaps, - rpcUrl - ); - const autobuild = getConfigValue('autobuild', true); - if (autobuild) { const build = forgeBuildTask(activeTextEditor.document.uri.fsPath); const buildExecution = await vscode.tasks.executeTask(build); try { await completed(buildExecution); - const session = await vscode.debug.startDebugging( - workspaceFolder, - myDebugConfig - ); } catch (e) { vscode.window.showErrorMessage('Failed to build project.'); } - } else { - const session = await vscode.debug.startDebugging( - workspaceFolder, - myDebugConfig - ); } + const myFoundryRoot = await foundryRoot(activeTextEditor.document.uri.fsPath); + const buildInfo = await loadBuildInfo(activeTextEditor.document.uri.fsPath); + const myDebugConfig = debugConfig( + debugConfigName, + file, + contractName, + methodSignature, + stopAtFirstOpcode, + showSourcemaps, + rpcUrl, + buildInfo, + myFoundryRoot + ); + const session = await vscode.debug.startDebugging( + workspaceFolder, + myDebugConfig + ); } function completed(tastkExecution: vscode.TaskExecution): Promise { @@ -114,7 +111,9 @@ function debugConfig( methodSignature: string, stopAtFirstOpcode: boolean, showSourcemaps: boolean, - rpcUrl: string + rpcUrl: string, + buildInfo: string, + clientMount: string ) { return { name: name, @@ -125,35 +124,8 @@ function debugConfig( methodSignature: methodSignature, stopAtFirstOpcode: stopAtFirstOpcode, showSourcemaps: showSourcemaps, - rpcUrl: rpcUrl + rpcUrl: rpcUrl, + buildInfo: buildInfo, + clientMount: clientMount, }; } - -function forgeBuildTask(file: string) { - const incrementalBuild = getConfigValue('incremental-build', false); - const forgePath = getConfigValue('forge-path', 'forge'); - const cwd = file.substring(0, file.lastIndexOf('/')); - const task = new vscode.Task( - { - label: 'forge build', - type: 'shell', - }, - vscode.TaskScope.Workspace, - 'forge', - 'simbolik', - new vscode.ShellExecution(forgePath, ['build'], { - cwd, - env: { - 'FOUNDRY_OPTIMIZER': 'false', - 'FOUNDRY_BUILD_INFO': 'true', - 'FOUNDRY_EXTRA_OUTPUT': '["storageLayout", "evm.bytecode.generatedSources"]', - 'FOUNDRY_BYTECODE_HASH': 'ipfs', - 'FOUNDRY_CBOR_METADATA': 'true', - 'FOUNDRY_CACHE': incrementalBuild ? 'true' : 'false', - } - }) - ); - task.isBackground = true; - task.presentationOptions.reveal = vscode.TaskRevealKind.Always; - return task; -} diff --git a/tsconfig.json b/tsconfig.json index 262a284..bceb86a 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -15,5 +15,6 @@ "src/extension.web.ts", "src/DebugAdapter.web.ts", "sampleWorkspace", + "node_modules", ] }