diff --git a/src/app/debugger/remix-debugger/README.md b/src/app/debugger/remix-debugger/README.md new file mode 100644 index 0000000000..99745fc323 --- /dev/null +++ b/src/app/debugger/remix-debugger/README.md @@ -0,0 +1,73 @@ +# `remix-debugger` + +The Remix Debugger is a webapp to debug the Ethereum VM and transactions. + ++ [Installation](#installation) ++ [Development](#development) ++ [First steps](#firststeps) ++ [Tests](#tests) + +## Installation + +Make sure Node is [installed on your setup](https://docs.npmjs.com/getting-started/installing-node), and that a [local `geth`/`eth` node is running](../README.md#how-to-use). + +```bash +git clone https://github.com/ethereum/remix +cd remix/remix-debugger +npm install +``` + +This will build the debugger. Start it by opening `index.html` in your browser. + +## Development + +Run `npm run start_dev` to start a local webserver, accessible at `http://127.0.0.1:8080`. Your browser will reload when files are updated. + +## First steps + +Once Remix is connected to a node, you will be able to debug transactions. + +You can do that: + - using a block number and a transaction index. + - using a transaction hash. + +After loading the transaction succeeded, the hash, from and to field will show up. The VM trace is then loaded. + +The debugger itself contains several controls that allow stepping over the trace and seing the current state of a selected step: + +#### Slider and Stepping action + +The slider allows to move quickly from a state to another. + +Stepping actions are: +- Step Into Back +- Step Over Back +- Step Over Forward +- Step Into Forward +- Jump Next Call: this will select the next state that refers to a context changes - CALL, CALLCODE, DELEGATECALL, CREATE. + +#### State Viewer + +The upper right panel contains basic informations about the current step: +- VMTraceStep: the index in the trace of the current step. +- Step +- Add memory +- Gas: gas used by this step +- Remaining gas: gas left +- Loaded address: the current code loaded, refers to the executing code. + +The other 6 panels describe the current selected state: + - Instructions list: list of all the instruction that defines the current executing code. + - Stack + - Storage Changes + - Memory + - Call Data$ + - Call Stack + +## Tests + +* To run unit tests, run `npm test`. + +* For local headless browser tests: + * To install `selenium`: `npm run selenium-install` + * Every time you want to run local browser tests, run: `npm run test-browser` diff --git a/src/app/debugger/remix-debugger/assets/css/font-awesome.min.css b/src/app/debugger/remix-debugger/assets/css/font-awesome.min.css new file mode 100644 index 0000000000..540440ce89 --- /dev/null +++ b/src/app/debugger/remix-debugger/assets/css/font-awesome.min.css @@ -0,0 +1,4 @@ +/*! + * Font Awesome 4.7.0 by @davegandy - http://fontawesome.io - @fontawesome + * License - http://fontawesome.io/license (Font: SIL OFL 1.1, CSS: MIT License) + */@font-face{font-family:'FontAwesome';src:url('../fonts/fontawesome-webfont.eot?v=4.7.0');src:url('../fonts/fontawesome-webfont.eot?#iefix&v=4.7.0') format('embedded-opentype'),url('../fonts/fontawesome-webfont.woff2?v=4.7.0') format('woff2'),url('../fonts/fontawesome-webfont.woff?v=4.7.0') format('woff'),url('../fonts/fontawesome-webfont.ttf?v=4.7.0') format('truetype'),url('../fonts/fontawesome-webfont.svg?v=4.7.0#fontawesomeregular') format('svg');font-weight:normal;font-style:normal}.fa{display:inline-block;font:normal normal normal 14px/1 FontAwesome;font-size:inherit;text-rendering:auto;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}.fa-lg{font-size:1.33333333em;line-height:.75em;vertical-align:-15%}.fa-2x{font-size:2em}.fa-3x{font-size:3em}.fa-4x{font-size:4em}.fa-5x{font-size:5em}.fa-fw{width:1.28571429em;text-align:center}.fa-ul{padding-left:0;margin-left:2.14285714em;list-style-type:none}.fa-ul>li{position:relative}.fa-li{position:absolute;left:-2.14285714em;width:2.14285714em;top:.14285714em;text-align:center}.fa-li.fa-lg{left:-1.85714286em}.fa-border{padding:.2em .25em .15em;border:solid .08em #eee;border-radius:.1em}.fa-pull-left{float:left}.fa-pull-right{float:right}.fa.fa-pull-left{margin-right:.3em}.fa.fa-pull-right{margin-left:.3em}.pull-right{float:right}.pull-left{float:left}.fa.pull-left{margin-right:.3em}.fa.pull-right{margin-left:.3em}.fa-spin{-webkit-animation:fa-spin 2s infinite linear;animation:fa-spin 2s infinite linear}.fa-pulse{-webkit-animation:fa-spin 1s infinite steps(8);animation:fa-spin 1s infinite steps(8)}@-webkit-keyframes fa-spin{0%{-webkit-transform:rotate(0deg);transform:rotate(0deg)}100%{-webkit-transform:rotate(359deg);transform:rotate(359deg)}}@keyframes fa-spin{0%{-webkit-transform:rotate(0deg);transform:rotate(0deg)}100%{-webkit-transform:rotate(359deg);transform:rotate(359deg)}}.fa-rotate-90{-ms-filter:"progid:DXImageTransform.Microsoft.BasicImage(rotation=1)";-webkit-transform:rotate(90deg);-ms-transform:rotate(90deg);transform:rotate(90deg)}.fa-rotate-180{-ms-filter:"progid:DXImageTransform.Microsoft.BasicImage(rotation=2)";-webkit-transform:rotate(180deg);-ms-transform:rotate(180deg);transform:rotate(180deg)}.fa-rotate-270{-ms-filter:"progid:DXImageTransform.Microsoft.BasicImage(rotation=3)";-webkit-transform:rotate(270deg);-ms-transform:rotate(270deg);transform:rotate(270deg)}.fa-flip-horizontal{-ms-filter:"progid:DXImageTransform.Microsoft.BasicImage(rotation=0, mirror=1)";-webkit-transform:scale(-1, 1);-ms-transform:scale(-1, 1);transform:scale(-1, 1)}.fa-flip-vertical{-ms-filter:"progid:DXImageTransform.Microsoft.BasicImage(rotation=2, mirror=1)";-webkit-transform:scale(1, -1);-ms-transform:scale(1, -1);transform:scale(1, -1)}:root .fa-rotate-90,:root .fa-rotate-180,:root .fa-rotate-270,:root .fa-flip-horizontal,:root .fa-flip-vertical{filter:none}.fa-stack{position:relative;display:inline-block;width:2em;height:2em;line-height:2em;vertical-align:middle}.fa-stack-1x,.fa-stack-2x{position:absolute;left:0;width:100%;text-align:center}.fa-stack-1x{line-height:inherit}.fa-stack-2x{font-size:2em}.fa-inverse{color:#fff}.fa-glass:before{content:"\f000"}.fa-music:before{content:"\f001"}.fa-search:before{content:"\f002"}.fa-envelope-o:before{content:"\f003"}.fa-heart:before{content:"\f004"}.fa-star:before{content:"\f005"}.fa-star-o:before{content:"\f006"}.fa-user:before{content:"\f007"}.fa-film:before{content:"\f008"}.fa-th-large:before{content:"\f009"}.fa-th:before{content:"\f00a"}.fa-th-list:before{content:"\f00b"}.fa-check:before{content:"\f00c"}.fa-remove:before,.fa-close:before,.fa-times:before{content:"\f00d"}.fa-search-plus:before{content:"\f00e"}.fa-search-minus:before{content:"\f010"}.fa-power-off:before{content:"\f011"}.fa-signal:before{content:"\f012"}.fa-gear:before,.fa-cog:before{content:"\f013"}.fa-trash-o:before{content:"\f014"}.fa-home:before{content:"\f015"}.fa-file-o:before{content:"\f016"}.fa-clock-o:before{content:"\f017"}.fa-road:before{content:"\f018"}.fa-download:before{content:"\f019"}.fa-arrow-circle-o-down:before{content:"\f01a"}.fa-arrow-circle-o-up:before{content:"\f01b"}.fa-inbox:before{content:"\f01c"}.fa-play-circle-o:before{content:"\f01d"}.fa-rotate-right:before,.fa-repeat:before{content:"\f01e"}.fa-refresh:before{content:"\f021"}.fa-list-alt:before{content:"\f022"}.fa-lock:before{content:"\f023"}.fa-flag:before{content:"\f024"}.fa-headphones:before{content:"\f025"}.fa-volume-off:before{content:"\f026"}.fa-volume-down:before{content:"\f027"}.fa-volume-up:before{content:"\f028"}.fa-qrcode:before{content:"\f029"}.fa-barcode:before{content:"\f02a"}.fa-tag:before{content:"\f02b"}.fa-tags:before{content:"\f02c"}.fa-book:before{content:"\f02d"}.fa-bookmark:before{content:"\f02e"}.fa-print:before{content:"\f02f"}.fa-camera:before{content:"\f030"}.fa-font:before{content:"\f031"}.fa-bold:before{content:"\f032"}.fa-italic:before{content:"\f033"}.fa-text-height:before{content:"\f034"}.fa-text-width:before{content:"\f035"}.fa-align-left:before{content:"\f036"}.fa-align-center:before{content:"\f037"}.fa-align-right:before{content:"\f038"}.fa-align-justify:before{content:"\f039"}.fa-list:before{content:"\f03a"}.fa-dedent:before,.fa-outdent:before{content:"\f03b"}.fa-indent:before{content:"\f03c"}.fa-video-camera:before{content:"\f03d"}.fa-photo:before,.fa-image:before,.fa-picture-o:before{content:"\f03e"}.fa-pencil:before{content:"\f040"}.fa-map-marker:before{content:"\f041"}.fa-adjust:before{content:"\f042"}.fa-tint:before{content:"\f043"}.fa-edit:before,.fa-pencil-square-o:before{content:"\f044"}.fa-share-square-o:before{content:"\f045"}.fa-check-square-o:before{content:"\f046"}.fa-arrows:before{content:"\f047"}.fa-step-backward:before{content:"\f048"}.fa-fast-backward:before{content:"\f049"}.fa-backward:before{content:"\f04a"}.fa-play:before{content:"\f04b"}.fa-pause:before{content:"\f04c"}.fa-stop:before{content:"\f04d"}.fa-forward:before{content:"\f04e"}.fa-fast-forward:before{content:"\f050"}.fa-step-forward:before{content:"\f051"}.fa-eject:before{content:"\f052"}.fa-chevron-left:before{content:"\f053"}.fa-chevron-right:before{content:"\f054"}.fa-plus-circle:before{content:"\f055"}.fa-minus-circle:before{content:"\f056"}.fa-times-circle:before{content:"\f057"}.fa-check-circle:before{content:"\f058"}.fa-question-circle:before{content:"\f059"}.fa-info-circle:before{content:"\f05a"}.fa-crosshairs:before{content:"\f05b"}.fa-times-circle-o:before{content:"\f05c"}.fa-check-circle-o:before{content:"\f05d"}.fa-ban:before{content:"\f05e"}.fa-arrow-left:before{content:"\f060"}.fa-arrow-right:before{content:"\f061"}.fa-arrow-up:before{content:"\f062"}.fa-arrow-down:before{content:"\f063"}.fa-mail-forward:before,.fa-share:before{content:"\f064"}.fa-expand:before{content:"\f065"}.fa-compress:before{content:"\f066"}.fa-plus:before{content:"\f067"}.fa-minus:before{content:"\f068"}.fa-asterisk:before{content:"\f069"}.fa-exclamation-circle:before{content:"\f06a"}.fa-gift:before{content:"\f06b"}.fa-leaf:before{content:"\f06c"}.fa-fire:before{content:"\f06d"}.fa-eye:before{content:"\f06e"}.fa-eye-slash:before{content:"\f070"}.fa-warning:before,.fa-exclamation-triangle:before{content:"\f071"}.fa-plane:before{content:"\f072"}.fa-calendar:before{content:"\f073"}.fa-random:before{content:"\f074"}.fa-comment:before{content:"\f075"}.fa-magnet:before{content:"\f076"}.fa-chevron-up:before{content:"\f077"}.fa-chevron-down:before{content:"\f078"}.fa-retweet:before{content:"\f079"}.fa-shopping-cart:before{content:"\f07a"}.fa-folder:before{content:"\f07b"}.fa-folder-open:before{content:"\f07c"}.fa-arrows-v:before{content:"\f07d"}.fa-arrows-h:before{content:"\f07e"}.fa-bar-chart-o:before,.fa-bar-chart:before{content:"\f080"}.fa-twitter-square:before{content:"\f081"}.fa-facebook-square:before{content:"\f082"}.fa-camera-retro:before{content:"\f083"}.fa-key:before{content:"\f084"}.fa-gears:before,.fa-cogs:before{content:"\f085"}.fa-comments:before{content:"\f086"}.fa-thumbs-o-up:before{content:"\f087"}.fa-thumbs-o-down:before{content:"\f088"}.fa-star-half:before{content:"\f089"}.fa-heart-o:before{content:"\f08a"}.fa-sign-out:before{content:"\f08b"}.fa-linkedin-square:before{content:"\f08c"}.fa-thumb-tack:before{content:"\f08d"}.fa-external-link:before{content:"\f08e"}.fa-sign-in:before{content:"\f090"}.fa-trophy:before{content:"\f091"}.fa-github-square:before{content:"\f092"}.fa-upload:before{content:"\f093"}.fa-lemon-o:before{content:"\f094"}.fa-phone:before{content:"\f095"}.fa-square-o:before{content:"\f096"}.fa-bookmark-o:before{content:"\f097"}.fa-phone-square:before{content:"\f098"}.fa-twitter:before{content:"\f099"}.fa-facebook-f:before,.fa-facebook:before{content:"\f09a"}.fa-github:before{content:"\f09b"}.fa-unlock:before{content:"\f09c"}.fa-credit-card:before{content:"\f09d"}.fa-feed:before,.fa-rss:before{content:"\f09e"}.fa-hdd-o:before{content:"\f0a0"}.fa-bullhorn:before{content:"\f0a1"}.fa-bell:before{content:"\f0f3"}.fa-certificate:before{content:"\f0a3"}.fa-hand-o-right:before{content:"\f0a4"}.fa-hand-o-left:before{content:"\f0a5"}.fa-hand-o-up:before{content:"\f0a6"}.fa-hand-o-down:before{content:"\f0a7"}.fa-arrow-circle-left:before{content:"\f0a8"}.fa-arrow-circle-right:before{content:"\f0a9"}.fa-arrow-circle-up:before{content:"\f0aa"}.fa-arrow-circle-down:before{content:"\f0ab"}.fa-globe:before{content:"\f0ac"}.fa-wrench:before{content:"\f0ad"}.fa-tasks:before{content:"\f0ae"}.fa-filter:before{content:"\f0b0"}.fa-briefcase:before{content:"\f0b1"}.fa-arrows-alt:before{content:"\f0b2"}.fa-group:before,.fa-users:before{content:"\f0c0"}.fa-chain:before,.fa-link:before{content:"\f0c1"}.fa-cloud:before{content:"\f0c2"}.fa-flask:before{content:"\f0c3"}.fa-cut:before,.fa-scissors:before{content:"\f0c4"}.fa-copy:before,.fa-files-o:before{content:"\f0c5"}.fa-paperclip:before{content:"\f0c6"}.fa-save:before,.fa-floppy-o:before{content:"\f0c7"}.fa-square:before{content:"\f0c8"}.fa-navicon:before,.fa-reorder:before,.fa-bars:before{content:"\f0c9"}.fa-list-ul:before{content:"\f0ca"}.fa-list-ol:before{content:"\f0cb"}.fa-strikethrough:before{content:"\f0cc"}.fa-underline:before{content:"\f0cd"}.fa-table:before{content:"\f0ce"}.fa-magic:before{content:"\f0d0"}.fa-truck:before{content:"\f0d1"}.fa-pinterest:before{content:"\f0d2"}.fa-pinterest-square:before{content:"\f0d3"}.fa-google-plus-square:before{content:"\f0d4"}.fa-google-plus:before{content:"\f0d5"}.fa-money:before{content:"\f0d6"}.fa-caret-down:before{content:"\f0d7"}.fa-caret-up:before{content:"\f0d8"}.fa-caret-left:before{content:"\f0d9"}.fa-caret-right:before{content:"\f0da"}.fa-columns:before{content:"\f0db"}.fa-unsorted:before,.fa-sort:before{content:"\f0dc"}.fa-sort-down:before,.fa-sort-desc:before{content:"\f0dd"}.fa-sort-up:before,.fa-sort-asc:before{content:"\f0de"}.fa-envelope:before{content:"\f0e0"}.fa-linkedin:before{content:"\f0e1"}.fa-rotate-left:before,.fa-undo:before{content:"\f0e2"}.fa-legal:before,.fa-gavel:before{content:"\f0e3"}.fa-dashboard:before,.fa-tachometer:before{content:"\f0e4"}.fa-comment-o:before{content:"\f0e5"}.fa-comments-o:before{content:"\f0e6"}.fa-flash:before,.fa-bolt:before{content:"\f0e7"}.fa-sitemap:before{content:"\f0e8"}.fa-umbrella:before{content:"\f0e9"}.fa-paste:before,.fa-clipboard:before{content:"\f0ea"}.fa-lightbulb-o:before{content:"\f0eb"}.fa-exchange:before{content:"\f0ec"}.fa-cloud-download:before{content:"\f0ed"}.fa-cloud-upload:before{content:"\f0ee"}.fa-user-md:before{content:"\f0f0"}.fa-stethoscope:before{content:"\f0f1"}.fa-suitcase:before{content:"\f0f2"}.fa-bell-o:before{content:"\f0a2"}.fa-coffee:before{content:"\f0f4"}.fa-cutlery:before{content:"\f0f5"}.fa-file-text-o:before{content:"\f0f6"}.fa-building-o:before{content:"\f0f7"}.fa-hospital-o:before{content:"\f0f8"}.fa-ambulance:before{content:"\f0f9"}.fa-medkit:before{content:"\f0fa"}.fa-fighter-jet:before{content:"\f0fb"}.fa-beer:before{content:"\f0fc"}.fa-h-square:before{content:"\f0fd"}.fa-plus-square:before{content:"\f0fe"}.fa-angle-double-left:before{content:"\f100"}.fa-angle-double-right:before{content:"\f101"}.fa-angle-double-up:before{content:"\f102"}.fa-angle-double-down:before{content:"\f103"}.fa-angle-left:before{content:"\f104"}.fa-angle-right:before{content:"\f105"}.fa-angle-up:before{content:"\f106"}.fa-angle-down:before{content:"\f107"}.fa-desktop:before{content:"\f108"}.fa-laptop:before{content:"\f109"}.fa-tablet:before{content:"\f10a"}.fa-mobile-phone:before,.fa-mobile:before{content:"\f10b"}.fa-circle-o:before{content:"\f10c"}.fa-quote-left:before{content:"\f10d"}.fa-quote-right:before{content:"\f10e"}.fa-spinner:before{content:"\f110"}.fa-circle:before{content:"\f111"}.fa-mail-reply:before,.fa-reply:before{content:"\f112"}.fa-github-alt:before{content:"\f113"}.fa-folder-o:before{content:"\f114"}.fa-folder-open-o:before{content:"\f115"}.fa-smile-o:before{content:"\f118"}.fa-frown-o:before{content:"\f119"}.fa-meh-o:before{content:"\f11a"}.fa-gamepad:before{content:"\f11b"}.fa-keyboard-o:before{content:"\f11c"}.fa-flag-o:before{content:"\f11d"}.fa-flag-checkered:before{content:"\f11e"}.fa-terminal:before{content:"\f120"}.fa-code:before{content:"\f121"}.fa-mail-reply-all:before,.fa-reply-all:before{content:"\f122"}.fa-star-half-empty:before,.fa-star-half-full:before,.fa-star-half-o:before{content:"\f123"}.fa-location-arrow:before{content:"\f124"}.fa-crop:before{content:"\f125"}.fa-code-fork:before{content:"\f126"}.fa-unlink:before,.fa-chain-broken:before{content:"\f127"}.fa-question:before{content:"\f128"}.fa-info:before{content:"\f129"}.fa-exclamation:before{content:"\f12a"}.fa-superscript:before{content:"\f12b"}.fa-subscript:before{content:"\f12c"}.fa-eraser:before{content:"\f12d"}.fa-puzzle-piece:before{content:"\f12e"}.fa-microphone:before{content:"\f130"}.fa-microphone-slash:before{content:"\f131"}.fa-shield:before{content:"\f132"}.fa-calendar-o:before{content:"\f133"}.fa-fire-extinguisher:before{content:"\f134"}.fa-rocket:before{content:"\f135"}.fa-maxcdn:before{content:"\f136"}.fa-chevron-circle-left:before{content:"\f137"}.fa-chevron-circle-right:before{content:"\f138"}.fa-chevron-circle-up:before{content:"\f139"}.fa-chevron-circle-down:before{content:"\f13a"}.fa-html5:before{content:"\f13b"}.fa-css3:before{content:"\f13c"}.fa-anchor:before{content:"\f13d"}.fa-unlock-alt:before{content:"\f13e"}.fa-bullseye:before{content:"\f140"}.fa-ellipsis-h:before{content:"\f141"}.fa-ellipsis-v:before{content:"\f142"}.fa-rss-square:before{content:"\f143"}.fa-play-circle:before{content:"\f144"}.fa-ticket:before{content:"\f145"}.fa-minus-square:before{content:"\f146"}.fa-minus-square-o:before{content:"\f147"}.fa-level-up:before{content:"\f148"}.fa-level-down:before{content:"\f149"}.fa-check-square:before{content:"\f14a"}.fa-pencil-square:before{content:"\f14b"}.fa-external-link-square:before{content:"\f14c"}.fa-share-square:before{content:"\f14d"}.fa-compass:before{content:"\f14e"}.fa-toggle-down:before,.fa-caret-square-o-down:before{content:"\f150"}.fa-toggle-up:before,.fa-caret-square-o-up:before{content:"\f151"}.fa-toggle-right:before,.fa-caret-square-o-right:before{content:"\f152"}.fa-euro:before,.fa-eur:before{content:"\f153"}.fa-gbp:before{content:"\f154"}.fa-dollar:before,.fa-usd:before{content:"\f155"}.fa-rupee:before,.fa-inr:before{content:"\f156"}.fa-cny:before,.fa-rmb:before,.fa-yen:before,.fa-jpy:before{content:"\f157"}.fa-ruble:before,.fa-rouble:before,.fa-rub:before{content:"\f158"}.fa-won:before,.fa-krw:before{content:"\f159"}.fa-bitcoin:before,.fa-btc:before{content:"\f15a"}.fa-file:before{content:"\f15b"}.fa-file-text:before{content:"\f15c"}.fa-sort-alpha-asc:before{content:"\f15d"}.fa-sort-alpha-desc:before{content:"\f15e"}.fa-sort-amount-asc:before{content:"\f160"}.fa-sort-amount-desc:before{content:"\f161"}.fa-sort-numeric-asc:before{content:"\f162"}.fa-sort-numeric-desc:before{content:"\f163"}.fa-thumbs-up:before{content:"\f164"}.fa-thumbs-down:before{content:"\f165"}.fa-youtube-square:before{content:"\f166"}.fa-youtube:before{content:"\f167"}.fa-xing:before{content:"\f168"}.fa-xing-square:before{content:"\f169"}.fa-youtube-play:before{content:"\f16a"}.fa-dropbox:before{content:"\f16b"}.fa-stack-overflow:before{content:"\f16c"}.fa-instagram:before{content:"\f16d"}.fa-flickr:before{content:"\f16e"}.fa-adn:before{content:"\f170"}.fa-bitbucket:before{content:"\f171"}.fa-bitbucket-square:before{content:"\f172"}.fa-tumblr:before{content:"\f173"}.fa-tumblr-square:before{content:"\f174"}.fa-long-arrow-down:before{content:"\f175"}.fa-long-arrow-up:before{content:"\f176"}.fa-long-arrow-left:before{content:"\f177"}.fa-long-arrow-right:before{content:"\f178"}.fa-apple:before{content:"\f179"}.fa-windows:before{content:"\f17a"}.fa-android:before{content:"\f17b"}.fa-linux:before{content:"\f17c"}.fa-dribbble:before{content:"\f17d"}.fa-skype:before{content:"\f17e"}.fa-foursquare:before{content:"\f180"}.fa-trello:before{content:"\f181"}.fa-female:before{content:"\f182"}.fa-male:before{content:"\f183"}.fa-gittip:before,.fa-gratipay:before{content:"\f184"}.fa-sun-o:before{content:"\f185"}.fa-moon-o:before{content:"\f186"}.fa-archive:before{content:"\f187"}.fa-bug:before{content:"\f188"}.fa-vk:before{content:"\f189"}.fa-weibo:before{content:"\f18a"}.fa-renren:before{content:"\f18b"}.fa-pagelines:before{content:"\f18c"}.fa-stack-exchange:before{content:"\f18d"}.fa-arrow-circle-o-right:before{content:"\f18e"}.fa-arrow-circle-o-left:before{content:"\f190"}.fa-toggle-left:before,.fa-caret-square-o-left:before{content:"\f191"}.fa-dot-circle-o:before{content:"\f192"}.fa-wheelchair:before{content:"\f193"}.fa-vimeo-square:before{content:"\f194"}.fa-turkish-lira:before,.fa-try:before{content:"\f195"}.fa-plus-square-o:before{content:"\f196"}.fa-space-shuttle:before{content:"\f197"}.fa-slack:before{content:"\f198"}.fa-envelope-square:before{content:"\f199"}.fa-wordpress:before{content:"\f19a"}.fa-openid:before{content:"\f19b"}.fa-institution:before,.fa-bank:before,.fa-university:before{content:"\f19c"}.fa-mortar-board:before,.fa-graduation-cap:before{content:"\f19d"}.fa-yahoo:before{content:"\f19e"}.fa-google:before{content:"\f1a0"}.fa-reddit:before{content:"\f1a1"}.fa-reddit-square:before{content:"\f1a2"}.fa-stumbleupon-circle:before{content:"\f1a3"}.fa-stumbleupon:before{content:"\f1a4"}.fa-delicious:before{content:"\f1a5"}.fa-digg:before{content:"\f1a6"}.fa-pied-piper-pp:before{content:"\f1a7"}.fa-pied-piper-alt:before{content:"\f1a8"}.fa-drupal:before{content:"\f1a9"}.fa-joomla:before{content:"\f1aa"}.fa-language:before{content:"\f1ab"}.fa-fax:before{content:"\f1ac"}.fa-building:before{content:"\f1ad"}.fa-child:before{content:"\f1ae"}.fa-paw:before{content:"\f1b0"}.fa-spoon:before{content:"\f1b1"}.fa-cube:before{content:"\f1b2"}.fa-cubes:before{content:"\f1b3"}.fa-behance:before{content:"\f1b4"}.fa-behance-square:before{content:"\f1b5"}.fa-steam:before{content:"\f1b6"}.fa-steam-square:before{content:"\f1b7"}.fa-recycle:before{content:"\f1b8"}.fa-automobile:before,.fa-car:before{content:"\f1b9"}.fa-cab:before,.fa-taxi:before{content:"\f1ba"}.fa-tree:before{content:"\f1bb"}.fa-spotify:before{content:"\f1bc"}.fa-deviantart:before{content:"\f1bd"}.fa-soundcloud:before{content:"\f1be"}.fa-database:before{content:"\f1c0"}.fa-file-pdf-o:before{content:"\f1c1"}.fa-file-word-o:before{content:"\f1c2"}.fa-file-excel-o:before{content:"\f1c3"}.fa-file-powerpoint-o:before{content:"\f1c4"}.fa-file-photo-o:before,.fa-file-picture-o:before,.fa-file-image-o:before{content:"\f1c5"}.fa-file-zip-o:before,.fa-file-archive-o:before{content:"\f1c6"}.fa-file-sound-o:before,.fa-file-audio-o:before{content:"\f1c7"}.fa-file-movie-o:before,.fa-file-video-o:before{content:"\f1c8"}.fa-file-code-o:before{content:"\f1c9"}.fa-vine:before{content:"\f1ca"}.fa-codepen:before{content:"\f1cb"}.fa-jsfiddle:before{content:"\f1cc"}.fa-life-bouy:before,.fa-life-buoy:before,.fa-life-saver:before,.fa-support:before,.fa-life-ring:before{content:"\f1cd"}.fa-circle-o-notch:before{content:"\f1ce"}.fa-ra:before,.fa-resistance:before,.fa-rebel:before{content:"\f1d0"}.fa-ge:before,.fa-empire:before{content:"\f1d1"}.fa-git-square:before{content:"\f1d2"}.fa-git:before{content:"\f1d3"}.fa-y-combinator-square:before,.fa-yc-square:before,.fa-hacker-news:before{content:"\f1d4"}.fa-tencent-weibo:before{content:"\f1d5"}.fa-qq:before{content:"\f1d6"}.fa-wechat:before,.fa-weixin:before{content:"\f1d7"}.fa-send:before,.fa-paper-plane:before{content:"\f1d8"}.fa-send-o:before,.fa-paper-plane-o:before{content:"\f1d9"}.fa-history:before{content:"\f1da"}.fa-circle-thin:before{content:"\f1db"}.fa-header:before{content:"\f1dc"}.fa-paragraph:before{content:"\f1dd"}.fa-sliders:before{content:"\f1de"}.fa-share-alt:before{content:"\f1e0"}.fa-share-alt-square:before{content:"\f1e1"}.fa-bomb:before{content:"\f1e2"}.fa-soccer-ball-o:before,.fa-futbol-o:before{content:"\f1e3"}.fa-tty:before{content:"\f1e4"}.fa-binoculars:before{content:"\f1e5"}.fa-plug:before{content:"\f1e6"}.fa-slideshare:before{content:"\f1e7"}.fa-twitch:before{content:"\f1e8"}.fa-yelp:before{content:"\f1e9"}.fa-newspaper-o:before{content:"\f1ea"}.fa-wifi:before{content:"\f1eb"}.fa-calculator:before{content:"\f1ec"}.fa-paypal:before{content:"\f1ed"}.fa-google-wallet:before{content:"\f1ee"}.fa-cc-visa:before{content:"\f1f0"}.fa-cc-mastercard:before{content:"\f1f1"}.fa-cc-discover:before{content:"\f1f2"}.fa-cc-amex:before{content:"\f1f3"}.fa-cc-paypal:before{content:"\f1f4"}.fa-cc-stripe:before{content:"\f1f5"}.fa-bell-slash:before{content:"\f1f6"}.fa-bell-slash-o:before{content:"\f1f7"}.fa-trash:before{content:"\f1f8"}.fa-copyright:before{content:"\f1f9"}.fa-at:before{content:"\f1fa"}.fa-eyedropper:before{content:"\f1fb"}.fa-paint-brush:before{content:"\f1fc"}.fa-birthday-cake:before{content:"\f1fd"}.fa-area-chart:before{content:"\f1fe"}.fa-pie-chart:before{content:"\f200"}.fa-line-chart:before{content:"\f201"}.fa-lastfm:before{content:"\f202"}.fa-lastfm-square:before{content:"\f203"}.fa-toggle-off:before{content:"\f204"}.fa-toggle-on:before{content:"\f205"}.fa-bicycle:before{content:"\f206"}.fa-bus:before{content:"\f207"}.fa-ioxhost:before{content:"\f208"}.fa-angellist:before{content:"\f209"}.fa-cc:before{content:"\f20a"}.fa-shekel:before,.fa-sheqel:before,.fa-ils:before{content:"\f20b"}.fa-meanpath:before{content:"\f20c"}.fa-buysellads:before{content:"\f20d"}.fa-connectdevelop:before{content:"\f20e"}.fa-dashcube:before{content:"\f210"}.fa-forumbee:before{content:"\f211"}.fa-leanpub:before{content:"\f212"}.fa-sellsy:before{content:"\f213"}.fa-shirtsinbulk:before{content:"\f214"}.fa-simplybuilt:before{content:"\f215"}.fa-skyatlas:before{content:"\f216"}.fa-cart-plus:before{content:"\f217"}.fa-cart-arrow-down:before{content:"\f218"}.fa-diamond:before{content:"\f219"}.fa-ship:before{content:"\f21a"}.fa-user-secret:before{content:"\f21b"}.fa-motorcycle:before{content:"\f21c"}.fa-street-view:before{content:"\f21d"}.fa-heartbeat:before{content:"\f21e"}.fa-venus:before{content:"\f221"}.fa-mars:before{content:"\f222"}.fa-mercury:before{content:"\f223"}.fa-intersex:before,.fa-transgender:before{content:"\f224"}.fa-transgender-alt:before{content:"\f225"}.fa-venus-double:before{content:"\f226"}.fa-mars-double:before{content:"\f227"}.fa-venus-mars:before{content:"\f228"}.fa-mars-stroke:before{content:"\f229"}.fa-mars-stroke-v:before{content:"\f22a"}.fa-mars-stroke-h:before{content:"\f22b"}.fa-neuter:before{content:"\f22c"}.fa-genderless:before{content:"\f22d"}.fa-facebook-official:before{content:"\f230"}.fa-pinterest-p:before{content:"\f231"}.fa-whatsapp:before{content:"\f232"}.fa-server:before{content:"\f233"}.fa-user-plus:before{content:"\f234"}.fa-user-times:before{content:"\f235"}.fa-hotel:before,.fa-bed:before{content:"\f236"}.fa-viacoin:before{content:"\f237"}.fa-train:before{content:"\f238"}.fa-subway:before{content:"\f239"}.fa-medium:before{content:"\f23a"}.fa-yc:before,.fa-y-combinator:before{content:"\f23b"}.fa-optin-monster:before{content:"\f23c"}.fa-opencart:before{content:"\f23d"}.fa-expeditedssl:before{content:"\f23e"}.fa-battery-4:before,.fa-battery:before,.fa-battery-full:before{content:"\f240"}.fa-battery-3:before,.fa-battery-three-quarters:before{content:"\f241"}.fa-battery-2:before,.fa-battery-half:before{content:"\f242"}.fa-battery-1:before,.fa-battery-quarter:before{content:"\f243"}.fa-battery-0:before,.fa-battery-empty:before{content:"\f244"}.fa-mouse-pointer:before{content:"\f245"}.fa-i-cursor:before{content:"\f246"}.fa-object-group:before{content:"\f247"}.fa-object-ungroup:before{content:"\f248"}.fa-sticky-note:before{content:"\f249"}.fa-sticky-note-o:before{content:"\f24a"}.fa-cc-jcb:before{content:"\f24b"}.fa-cc-diners-club:before{content:"\f24c"}.fa-clone:before{content:"\f24d"}.fa-balance-scale:before{content:"\f24e"}.fa-hourglass-o:before{content:"\f250"}.fa-hourglass-1:before,.fa-hourglass-start:before{content:"\f251"}.fa-hourglass-2:before,.fa-hourglass-half:before{content:"\f252"}.fa-hourglass-3:before,.fa-hourglass-end:before{content:"\f253"}.fa-hourglass:before{content:"\f254"}.fa-hand-grab-o:before,.fa-hand-rock-o:before{content:"\f255"}.fa-hand-stop-o:before,.fa-hand-paper-o:before{content:"\f256"}.fa-hand-scissors-o:before{content:"\f257"}.fa-hand-lizard-o:before{content:"\f258"}.fa-hand-spock-o:before{content:"\f259"}.fa-hand-pointer-o:before{content:"\f25a"}.fa-hand-peace-o:before{content:"\f25b"}.fa-trademark:before{content:"\f25c"}.fa-registered:before{content:"\f25d"}.fa-creative-commons:before{content:"\f25e"}.fa-gg:before{content:"\f260"}.fa-gg-circle:before{content:"\f261"}.fa-tripadvisor:before{content:"\f262"}.fa-odnoklassniki:before{content:"\f263"}.fa-odnoklassniki-square:before{content:"\f264"}.fa-get-pocket:before{content:"\f265"}.fa-wikipedia-w:before{content:"\f266"}.fa-safari:before{content:"\f267"}.fa-chrome:before{content:"\f268"}.fa-firefox:before{content:"\f269"}.fa-opera:before{content:"\f26a"}.fa-internet-explorer:before{content:"\f26b"}.fa-tv:before,.fa-television:before{content:"\f26c"}.fa-contao:before{content:"\f26d"}.fa-500px:before{content:"\f26e"}.fa-amazon:before{content:"\f270"}.fa-calendar-plus-o:before{content:"\f271"}.fa-calendar-minus-o:before{content:"\f272"}.fa-calendar-times-o:before{content:"\f273"}.fa-calendar-check-o:before{content:"\f274"}.fa-industry:before{content:"\f275"}.fa-map-pin:before{content:"\f276"}.fa-map-signs:before{content:"\f277"}.fa-map-o:before{content:"\f278"}.fa-map:before{content:"\f279"}.fa-commenting:before{content:"\f27a"}.fa-commenting-o:before{content:"\f27b"}.fa-houzz:before{content:"\f27c"}.fa-vimeo:before{content:"\f27d"}.fa-black-tie:before{content:"\f27e"}.fa-fonticons:before{content:"\f280"}.fa-reddit-alien:before{content:"\f281"}.fa-edge:before{content:"\f282"}.fa-credit-card-alt:before{content:"\f283"}.fa-codiepie:before{content:"\f284"}.fa-modx:before{content:"\f285"}.fa-fort-awesome:before{content:"\f286"}.fa-usb:before{content:"\f287"}.fa-product-hunt:before{content:"\f288"}.fa-mixcloud:before{content:"\f289"}.fa-scribd:before{content:"\f28a"}.fa-pause-circle:before{content:"\f28b"}.fa-pause-circle-o:before{content:"\f28c"}.fa-stop-circle:before{content:"\f28d"}.fa-stop-circle-o:before{content:"\f28e"}.fa-shopping-bag:before{content:"\f290"}.fa-shopping-basket:before{content:"\f291"}.fa-hashtag:before{content:"\f292"}.fa-bluetooth:before{content:"\f293"}.fa-bluetooth-b:before{content:"\f294"}.fa-percent:before{content:"\f295"}.fa-gitlab:before{content:"\f296"}.fa-wpbeginner:before{content:"\f297"}.fa-wpforms:before{content:"\f298"}.fa-envira:before{content:"\f299"}.fa-universal-access:before{content:"\f29a"}.fa-wheelchair-alt:before{content:"\f29b"}.fa-question-circle-o:before{content:"\f29c"}.fa-blind:before{content:"\f29d"}.fa-audio-description:before{content:"\f29e"}.fa-volume-control-phone:before{content:"\f2a0"}.fa-braille:before{content:"\f2a1"}.fa-assistive-listening-systems:before{content:"\f2a2"}.fa-asl-interpreting:before,.fa-american-sign-language-interpreting:before{content:"\f2a3"}.fa-deafness:before,.fa-hard-of-hearing:before,.fa-deaf:before{content:"\f2a4"}.fa-glide:before{content:"\f2a5"}.fa-glide-g:before{content:"\f2a6"}.fa-signing:before,.fa-sign-language:before{content:"\f2a7"}.fa-low-vision:before{content:"\f2a8"}.fa-viadeo:before{content:"\f2a9"}.fa-viadeo-square:before{content:"\f2aa"}.fa-snapchat:before{content:"\f2ab"}.fa-snapchat-ghost:before{content:"\f2ac"}.fa-snapchat-square:before{content:"\f2ad"}.fa-pied-piper:before{content:"\f2ae"}.fa-first-order:before{content:"\f2b0"}.fa-yoast:before{content:"\f2b1"}.fa-themeisle:before{content:"\f2b2"}.fa-google-plus-circle:before,.fa-google-plus-official:before{content:"\f2b3"}.fa-fa:before,.fa-font-awesome:before{content:"\f2b4"}.fa-handshake-o:before{content:"\f2b5"}.fa-envelope-open:before{content:"\f2b6"}.fa-envelope-open-o:before{content:"\f2b7"}.fa-linode:before{content:"\f2b8"}.fa-address-book:before{content:"\f2b9"}.fa-address-book-o:before{content:"\f2ba"}.fa-vcard:before,.fa-address-card:before{content:"\f2bb"}.fa-vcard-o:before,.fa-address-card-o:before{content:"\f2bc"}.fa-user-circle:before{content:"\f2bd"}.fa-user-circle-o:before{content:"\f2be"}.fa-user-o:before{content:"\f2c0"}.fa-id-badge:before{content:"\f2c1"}.fa-drivers-license:before,.fa-id-card:before{content:"\f2c2"}.fa-drivers-license-o:before,.fa-id-card-o:before{content:"\f2c3"}.fa-quora:before{content:"\f2c4"}.fa-free-code-camp:before{content:"\f2c5"}.fa-telegram:before{content:"\f2c6"}.fa-thermometer-4:before,.fa-thermometer:before,.fa-thermometer-full:before{content:"\f2c7"}.fa-thermometer-3:before,.fa-thermometer-three-quarters:before{content:"\f2c8"}.fa-thermometer-2:before,.fa-thermometer-half:before{content:"\f2c9"}.fa-thermometer-1:before,.fa-thermometer-quarter:before{content:"\f2ca"}.fa-thermometer-0:before,.fa-thermometer-empty:before{content:"\f2cb"}.fa-shower:before{content:"\f2cc"}.fa-bathtub:before,.fa-s15:before,.fa-bath:before{content:"\f2cd"}.fa-podcast:before{content:"\f2ce"}.fa-window-maximize:before{content:"\f2d0"}.fa-window-minimize:before{content:"\f2d1"}.fa-window-restore:before{content:"\f2d2"}.fa-times-rectangle:before,.fa-window-close:before{content:"\f2d3"}.fa-times-rectangle-o:before,.fa-window-close-o:before{content:"\f2d4"}.fa-bandcamp:before{content:"\f2d5"}.fa-grav:before{content:"\f2d6"}.fa-etsy:before{content:"\f2d7"}.fa-imdb:before{content:"\f2d8"}.fa-ravelry:before{content:"\f2d9"}.fa-eercast:before{content:"\f2da"}.fa-microchip:before{content:"\f2db"}.fa-snowflake-o:before{content:"\f2dc"}.fa-superpowers:before{content:"\f2dd"}.fa-wpexplorer:before{content:"\f2de"}.fa-meetup:before{content:"\f2e0"}.sr-only{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0, 0, 0, 0);border:0}.sr-only-focusable:active,.sr-only-focusable:focus{position:static;width:auto;height:auto;margin:0;overflow:visible;clip:auto} diff --git a/src/app/debugger/remix-debugger/assets/fonts/FontAwesome.otf b/src/app/debugger/remix-debugger/assets/fonts/FontAwesome.otf new file mode 100644 index 0000000000..681bdd4d4c Binary files /dev/null and b/src/app/debugger/remix-debugger/assets/fonts/FontAwesome.otf differ diff --git a/src/app/debugger/remix-debugger/assets/fonts/fontawesome-webfont.eot b/src/app/debugger/remix-debugger/assets/fonts/fontawesome-webfont.eot new file mode 100644 index 0000000000..a30335d748 Binary files /dev/null and b/src/app/debugger/remix-debugger/assets/fonts/fontawesome-webfont.eot differ diff --git a/src/app/debugger/remix-debugger/assets/fonts/fontawesome-webfont.svg b/src/app/debugger/remix-debugger/assets/fonts/fontawesome-webfont.svg new file mode 100644 index 0000000000..6fd19abcb9 --- /dev/null +++ b/src/app/debugger/remix-debugger/assets/fonts/fontawesome-webfont.svg @@ -0,0 +1,640 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/app/debugger/remix-debugger/assets/fonts/fontawesome-webfont.ttf b/src/app/debugger/remix-debugger/assets/fonts/fontawesome-webfont.ttf new file mode 100644 index 0000000000..d7994e1308 Binary files /dev/null and b/src/app/debugger/remix-debugger/assets/fonts/fontawesome-webfont.ttf differ diff --git a/src/app/debugger/remix-debugger/assets/fonts/fontawesome-webfont.woff b/src/app/debugger/remix-debugger/assets/fonts/fontawesome-webfont.woff new file mode 100644 index 0000000000..6fd4ede0f3 Binary files /dev/null and b/src/app/debugger/remix-debugger/assets/fonts/fontawesome-webfont.woff differ diff --git a/src/app/debugger/remix-debugger/assets/fonts/fontawesome-webfont.woff2 b/src/app/debugger/remix-debugger/assets/fonts/fontawesome-webfont.woff2 new file mode 100644 index 0000000000..5560193ccc Binary files /dev/null and b/src/app/debugger/remix-debugger/assets/fonts/fontawesome-webfont.woff2 differ diff --git a/src/app/debugger/remix-debugger/ci/browser_tests.sh b/src/app/debugger/remix-debugger/ci/browser_tests.sh new file mode 100755 index 0000000000..b395c03481 --- /dev/null +++ b/src/app/debugger/remix-debugger/ci/browser_tests.sh @@ -0,0 +1,40 @@ +#!/usr/bin/env bash + +set -e + +if test $(uname -s) = "Darwin" +then + OS="osx" + FILEFORMAT="zip" +else + OS="linux" + FILEFORMAT="tar.gz" +fi +SC_VERSION="4.4.11" +SAUCECONNECT_URL="https://saucelabs.com/downloads/sc-$SC_VERSION-$OS.$FILEFORMAT" +SAUCECONNECT_USERNAME="yanneth" +SAUCECONNECT_ACCESSKEY="1f5a4560-b02b-41aa-b52b-f033aad30870" +BUILD_ID=${CIRCLE_BUILD_NUM:-${TRAVIS_JOB_NUMBER}} +SAUCECONNECT_JOBIDENTIFIER="remix_tests_${BUILD_ID}" +SAUCECONNECT_READYFILE="sc.ready" +TEST_EXITCODE=0 + +npm run build +npm run serve & + +wget $SAUCECONNECT_URL +tar -zxvf sc-"$SC_VERSION"-"$OS"."$FILEFORMAT" +./sc-"$SC_VERSION"-$OS/bin/sc -u $SAUCECONNECT_USERNAME -k $SAUCECONNECT_ACCESSKEY -i $SAUCECONNECT_JOBIDENTIFIER --readyfile $SAUCECONNECT_READYFILE & +while [ ! -f $SAUCECONNECT_READYFILE ]; do + sleep .5 +done + +npm run nightwatch_remote_parallel || TEST_EXITCODE=1 + +node ci/sauceDisconnect.js $SAUCECONNECT_USERNAME $SAUCECONNECT_ACCESSKEY $SAUCECONNECT_JOBIDENTIFIER + +echo $TEST_EXITCODE +if [ $TEST_EXITCODE -eq 1 ] +then + exit 1 +fi diff --git a/src/app/debugger/remix-debugger/ci/deploy_from_travis.sh b/src/app/debugger/remix-debugger/ci/deploy_from_travis.sh new file mode 100755 index 0000000000..e80aba8e47 --- /dev/null +++ b/src/app/debugger/remix-debugger/ci/deploy_from_travis.sh @@ -0,0 +1,35 @@ +#!/bin/bash + +set -e + +SHA=`git rev-parse --verify HEAD` + +git config user.name "Travis CI" +git config user.email "builds@ethereum.org" +git checkout --orphan gh-pages +git rm --cached -r . +echo "# Automatic build" > README.md +echo "Built website from {$SHA}. See https://github.com/ethereum/remix/ for details." >> README.md +# -f is needed because "build" is part of .gitignore + +# copying file to the root folder +cp remix-debugger/index.html index.html +mkdir build +cp remix-debugger/build/app.js build/app.js +mkdir assets +cp -R remix-debugger/assets/. assets/ + +git add -f README.md index.html build/app.js assets +git commit -m "Built website from {$SHA}." + +ENCRYPTION_LABEL=fade88419824 +ENCRYPTED_KEY_VAR="encrypted_${ENCRYPTION_LABEL}_key" +ENCRYPTED_IV_VAR="encrypted_${ENCRYPTION_LABEL}_iv" +ENCRYPTED_KEY=${!ENCRYPTED_KEY_VAR} +ENCRYPTED_IV=${!ENCRYPTED_IV_VAR} +openssl aes-256-cbc -K $ENCRYPTED_KEY -iv $ENCRYPTED_IV -in ci/deploy_key.enc -out deploy_key -d +chmod 600 deploy_key +eval `ssh-agent -s` +ssh-add deploy_key + +git push -f git@github.com:ethereum/remix.git gh-pages diff --git a/src/app/debugger/remix-debugger/ci/deploy_key.enc b/src/app/debugger/remix-debugger/ci/deploy_key.enc new file mode 100644 index 0000000000..99ddf96eee Binary files /dev/null and b/src/app/debugger/remix-debugger/ci/deploy_key.enc differ diff --git a/src/app/debugger/remix-debugger/ci/sauceDisconnect.js b/src/app/debugger/remix-debugger/ci/sauceDisconnect.js new file mode 100644 index 0000000000..b29328db5c --- /dev/null +++ b/src/app/debugger/remix-debugger/ci/sauceDisconnect.js @@ -0,0 +1,72 @@ +const https = require('https') + +var userName = process.argv[2] +var accessKey = process.argv[3] +var tunnelName = process.argv[4] + +function removeTunnel () { + const requestPath = `/rest/v1/${userName}/tunnels` + console.log(requestPath) + callSauce(requestPath, 'GET', function (error, result) { + if (error) { + console.log(error) + } else { + var data = JSON.parse(result) + for (var k in data) { + retrieveTunnel(data[k], function (error, result) { + if (error) { + console.log(error) + } else if (result.identtifier === tunnelName) { + deleteTunnel(result.id, function () { + console.log('tunnel deleted ' + data[k] + ' ' + tunnelName) + }) + } + }) + } + } + }) +} + +function retrieveTunnel (tunnelid, callback) { + const requestPath = `/rest/v1/${userName}/tunnels/${tunnelid}` + callSauce(requestPath, 'GET', function (error, result) { + if (error) { + callback(error) + } else { + callback(null, {'identtifier': JSON.parse(result).tunnel_identifier, 'id': tunnelid}) + } + }) +} + +function deleteTunnel (tunnelid, callback) { + const requestPath = `/rest/v1/${userName}/tunnels/${tunnelid}` + callSauce(requestPath, 'DELETE', callback) +} + +function callSauce (requestPath, type, callback) { + function responseCallback (res) { + res.setEncoding('utf8') + console.log('Response: ', res.statusCode, JSON.stringify(res.headers)) + res.on('data', function onData (chunk) { + console.log('BODY: ' + chunk) + callback(null, chunk) + }) + res.on('end', function onEnd () {}) + } + + var req = https.request({ + hostname: 'saucelabs.com', + path: requestPath, + method: type, + auth: userName + ':' + accessKey + }, responseCallback) + + req.on('error', function onError (e) { + console.log('problem with request: ' + e.message) + callback(e.message) + }) + req.write('') + req.end() +} + +removeTunnel() diff --git a/src/app/debugger/remix-debugger/findClient.js b/src/app/debugger/remix-debugger/findClient.js new file mode 100644 index 0000000000..8ceb9bce00 --- /dev/null +++ b/src/app/debugger/remix-debugger/findClient.js @@ -0,0 +1,43 @@ +var which = require('which') + +var geth = null +var eth = null + +try { + geth = which.sync('geth') +} catch (e) { +} + +try { + eth = which.sync('eth') +} catch (e) { +} +if (process.argv.length > 2) { + if (geth && process.argv[2] === 'geth') { + runGeth() + } else if (eth && process.argv[2] === 'eth') { + runEth() + } +} else if (geth && eth) { + console.log('both eth and geth has been found in your system') + console.log('restart the command with the desired client:') + console.log('npm run start_eth') + console.log('or') + console.log('npm run start_geth') +} else if (geth) { + runGeth() +} else if (eth) { + runEth() +} else { + console.log('neither eth or geth has been found in your system') +} + +function runEth () { + console.log('starting eth...') + process.exit(20) +} + +function runGeth () { + console.log('starting geth...') + process.exit(21) +} diff --git a/src/app/debugger/remix-debugger/index.html b/src/app/debugger/remix-debugger/index.html new file mode 100644 index 0000000000..4c6c33cf4c --- /dev/null +++ b/src/app/debugger/remix-debugger/index.html @@ -0,0 +1,19 @@ + + + + + + + +
+ + + diff --git a/src/app/debugger/remix-debugger/index.js b/src/app/debugger/remix-debugger/index.js new file mode 100644 index 0000000000..a113f9390d --- /dev/null +++ b/src/app/debugger/remix-debugger/index.js @@ -0,0 +1,24 @@ +'use strict' +var VMDebugger = require('./src/ui/VmDebugger') +var Debugger = require('./src/ui/Ethdebugger') +var BasicPanel = require('./src/ui/BasicPanel') +var TreeView = require('./src/ui/TreeView') + +if (typeof (module) !== 'undefined' && typeof (module.exports) !== 'undefined') { + module.exports = modules() +} + +if (typeof (window) !== 'undefined') { + window.remix = modules() +} + +function modules () { + return { + ui: { + Debugger: Debugger, + VMdebugger: VMDebugger, + BasicPanel: BasicPanel, + TreeView: TreeView + } + } +} diff --git a/src/app/debugger/remix-debugger/nightwatch.js b/src/app/debugger/remix-debugger/nightwatch.js new file mode 100644 index 0000000000..b81c8176b5 --- /dev/null +++ b/src/app/debugger/remix-debugger/nightwatch.js @@ -0,0 +1,101 @@ +'use strict' +var buildId = process.env.CIRCLE_BUILD_NUM || process.env.TRAVIS_JOB_NUMBER + +module.exports = { + 'src_folders': ['./test-browser/test'], + 'output_folder': './test-browser/test/reports', + 'custom_commands_path': '', + 'custom_assertions_path': '', + 'globals_path': '', + 'page_objects_path': '', + + 'selenium': { + 'start_process': false, + 'server_path': '', + 'log_path': '', + 'host': '127.0.0.1', + 'port': 4444, + 'cli_args': { + 'webdriver.chrome.driver': '', + 'webdriver.ie.driver': '', + 'webdriver.firefox.profile': '' + } + }, + + 'test_settings': { + 'default': { + 'launch_url': 'http://ondemand.saucelabs.com:80', + 'selenium_host': 'ondemand.saucelabs.com', + 'selenium_port': 80, + 'silent': true, + 'username': 'yanneth', + 'access_key': '1f5a4560-b02b-41aa-b52b-f033aad30870', + 'use_ssl': false, + 'globals': { + 'waitForConditionTimeout': 10000, + 'asyncHookTimeout': 100000 + }, + 'screenshots': { + 'enabled': false, + 'path': '' + }, + 'desiredCapabilities': { + 'browserName': 'firefox', + 'javascriptEnabled': true, + 'acceptSslCerts': true, + 'build': 'build-' + buildId, + 'tunnel-identifier': 'remix_tests_' + buildId + } + }, + + 'chrome': { + 'desiredCapabilities': { + 'browserName': 'chrome', + 'javascriptEnabled': true, + 'acceptSslCerts': true, + 'build': 'build-' + buildId, + 'tunnel-identifier': 'remix_tests_' + buildId + } + }, + + 'safari': { + 'desiredCapabilities': { + 'browserName': 'safari', + 'javascriptEnabled': true, + 'platform': 'OS X 10.11', + 'version': '10.0', + 'acceptSslCerts': true, + 'build': 'build-' + buildId, + 'tunnel-identifier': 'remix_tests_' + buildId + } + }, + + 'ie': { + 'desiredCapabilities': { + 'browserName': 'internet explorer', + 'javascriptEnabled': true, + 'acceptSslCerts': true, + 'platform': 'WIN8.1', + 'version': '11', + 'build': 'build-' + buildId, + 'tunnel-identifier': 'remix_tests_' + buildId + } + }, + + 'local': { + 'launch_url': 'http://localhost', + 'selenium_host': '127.0.0.1', + 'selenium_port': 4444, + 'silent': true, + 'screenshots': { + 'enabled': false, + 'path': '' + }, + 'desiredCapabilities': { + 'browserName': 'firefox', + 'javascriptEnabled': true, + 'acceptSslCerts': true + } + } + } +} diff --git a/src/app/debugger/remix-debugger/package.json b/src/app/debugger/remix-debugger/package.json new file mode 100644 index 0000000000..00fbfc8bcb --- /dev/null +++ b/src/app/debugger/remix-debugger/package.json @@ -0,0 +1,150 @@ +{ + "name": "remix-debugger", + "version": "0.1.2", + "description": "Ethereum IDE and tools for the web", + "contributors": [ + { + "name": "Yann Levreau", + "email": "yann@ethdev.com" + }, + { + "name": "Liana Husikyan", + "email": "liana@ethdev.com" + } + ], + "main": "./index.js", + "devDependencies": { + "babel-eslint": "^7.1.1", + "babel-plugin-transform-object-assign": "^6.22.0", + "babel-plugin-yo-yoify": "^0.3.3", + "babel-polyfill": "^6.22.0", + "babel-preset-env": "^1.6.1", + "babel-preset-es2015": "^6.24.0", + "babel-preset-stage-0": "^6.24.1", + "babelify": "^7.3.0", + "browserify": "^13.0.1", + "browserify-livereload": "^1.0.10", + "clipboard-copy": "^1.2.0", + "csjs-inject": "^1.0.1", + "ethereum-common": "0.0.18", + "ethereumjs-block": "^1.2.2", + "ethereumjs-tx": "^1.1.1", + "ethereumjs-util": "^4.5.0", + "ethereumjs-vm": "2.3.1", + "fast-async": "^6.1.2", + "http-server": "^0.9.0", + "nightwatch": "^0.9.5", + "notify-error": "^1.2.0", + "npm-run-all": "^4.1.2", + "onchange": "^3.3.0", + "remix-core": "latest", + "remix-lib": "latest", + "remix-solidity": "latest", + "selenium-standalone": "^6.0.1", + "solc": "^0.4.13", + "standard": "^7.0.1", + "standard-reporter": "^1.0.5", + "tape": "^4.6.0", + "watchify": "^3.9.0", + "web3": "^0.15.3", + "yo-yo": "^1.2.1", + "yo-yoify": "^3.1.0" + }, + "scripts": { + "build": "mkdirp build; browserify index.js > build/app.js", + "lint": "standard | notify-error", + "nightwatch_local": "nightwatch --config nightwatch.js --env local", + "nightwatch_remote_chrome": "nightwatch --config nightwatch.js --env chrome", + "nightwatch_remote_firefox": "nightwatch --config nightwatch.js --env default", + "nightwatch_remote_ie": "nightwatch --config nightwatch.js --env ie", + "nightwatch_remote_parallel": "nightwatch --config nightwatch.js --env safari,chrome,default", + "nightwatch_remote_safari": "nightwatch --config nightwatch.js --env safari", + "onchange": "onchange build/app.js -- npm run lint", + "selenium": "selenium-standalone start", + "selenium-install": "selenium-standalone install", + "serve": "http-server .", + "start": "./runNode.sh", + "start_dev": "npm-run-all -lpr serve watch onchange", + "start_eth": "npm run warning_message; eth -j --rpccorsdomain '*'", + "start_geth": "npm run warning_message; geth --rpc --rpcapi 'web3,eth,debug' --rpcport 8545 --rpccorsdomain '*'", + "test": "standard && tape ./test/tests.js && ./ci/browser_tests.sh", + "test-browser": "npm-run-all -lpr selenium serve waittest", + "waittest": "sleep 5 && npm run nightwatch_local", + "warning_message": "echo 'DO NOT DO THIS IF eth/geth STORES PRIVATE KEYS!! External system might be able to access your node through the RPC server.\n\n';", + "watch": "mkdirp build; watchify index.js -p [ browserify-livereload --host 127.0.0.1 --port 1337 ] -dv -o build/app.js" + }, + "repository": { + "type": "git", + "url": "git+https://github.com/ethereum/remix.git" + }, + "author": "cpp-ethereum team", + "license": "MIT", + "bugs": { + "url": "https://github.com/ethereum/remix/issues" + }, + "homepage": "https://github.com/ethereum/remix#readme", + "standard": { + "ignore": [ + "node_modules/*", + "build/*", + "test/resources/*" + ] + }, + "babel": { + "plugins": [ + "transform-es2015-template-literals", + "transform-es2015-literals", + "transform-es2015-function-name", + "transform-es2015-arrow-functions", + "transform-es2015-block-scoped-functions", + "transform-es2015-classes", + "transform-es2015-object-super", + "transform-es2015-shorthand-properties", + "transform-es2015-duplicate-keys", + "transform-es2015-computed-properties", + "transform-es2015-for-of", + "transform-es2015-sticky-regex", + "transform-es2015-unicode-regex", + "check-es2015-constants", + "transform-es2015-spread", + "transform-es2015-parameters", + "transform-es2015-destructuring", + "transform-es2015-block-scoping", + "transform-object-assign" + ] + }, + "browserify": { + "transform": [ + [ + "babelify", + { + "sourceMapsAbsolute": false, + "sourceMaps": true, + "plugins": [ + [ + "fast-async", + { + "runtimePattern": null, + "compiler": { + "es7": true, + "noRuntime": true, + "promises": true, + "wrapAwait": true + } + } + ], + [ + "yo-yoify" + ], + [ + "transform-object-assign" + ] + ], + "presets": [ + "es2015" + ] + } + ] + ] + } +} diff --git a/src/app/debugger/remix-debugger/runNode.sh b/src/app/debugger/remix-debugger/runNode.sh new file mode 100755 index 0000000000..79575a46f2 --- /dev/null +++ b/src/app/debugger/remix-debugger/runNode.sh @@ -0,0 +1,14 @@ +#!/usr/bin/env bash + +node findClient.js $1 +RUNCLIENT=$? +if [ $RUNCLIENT -eq '20' ] +then + npm run start_eth +fi + +if [ $RUNCLIENT -eq '21' ] +then + echo $? + npm run start_geth +fi \ No newline at end of file diff --git a/src/app/debugger/remix-debugger/src/ui/BasicPanel.js b/src/app/debugger/remix-debugger/src/ui/BasicPanel.js new file mode 100644 index 0000000000..6efed6231c --- /dev/null +++ b/src/app/debugger/remix-debugger/src/ui/BasicPanel.js @@ -0,0 +1,51 @@ +'use strict' +var style = require('./styles/basicStyles') +var yo = require('yo-yo') +var remixLib = require('remix-lib') +var ui = remixLib.helpers.ui + +var csjs = require('csjs-inject') + +var css = csjs` + .container { + width: 70%; + } +` + +function BasicPanel (_name, _width, _height) { + this.data + this.name = _name + this.width = _width + this.height = _height + this.view +} + +BasicPanel.prototype.update = function () { + yo.update(this.view, this.render()) +} + +BasicPanel.prototype.hide = function () { + this.view.style.display = 'none' +} + +BasicPanel.prototype.show = function () { + this.view.style.display = 'block' +} + +BasicPanel.prototype.render = function () { + var view = yo` +
+
+ ${this.name} +
+
+
${this.data}
+
+
` + if (!this.view) { + this.view = view + } + return view +} + +module.exports = BasicPanel diff --git a/src/app/debugger/remix-debugger/src/ui/ButtonNavigator.js b/src/app/debugger/remix-debugger/src/ui/ButtonNavigator.js new file mode 100644 index 0000000000..f438d69236 --- /dev/null +++ b/src/app/debugger/remix-debugger/src/ui/ButtonNavigator.js @@ -0,0 +1,183 @@ +'use strict' +var remixLib = require('remix-lib') +var EventManager = remixLib.EventManager +var yo = require('yo-yo') + +var csjs = require('csjs-inject') +var styleGuide = remixLib.ui.themeChooser +var styles = styleGuide.chooser() + +var css = csjs` + .buttons { + display: flex; + flex-wrap: wrap; + } + .stepButtons { + width: 100%; + display: flex; + justify-content: center; + } + .stepButton { + ${styles.rightPanel.debuggerTab.button_Debugger} + } + .jumpButtons { + width: 100%; + display: flex; + justify-content: center; + } + .jumpButton { + ${styles.rightPanel.debuggerTab.button_Debugger} + } + .navigator { + color: ${styles.rightPanel.debuggerTab.text_Primary}; + } + .navigator:hover { + color: ${styles.rightPanel.debuggerTab.button_Debugger_icon_HoverColor}; + } +` + +function ButtonNavigator (_parent, _traceManager) { + this.event = new EventManager() + this.intoBackDisabled = true + this.overBackDisabled = true + this.intoForwardDisabled = true + this.overForwardDisabled = true + this.jumpOutDisabled = true + this.jumpNextBreakpointDisabled = true + this.jumpPreviousBreakpointDisabled = true + + this.traceManager = _traceManager + this.currentCall = null + this.revertionPoint = null + + _parent.event.register('indexChanged', this, (index) => { + if (index < 0) return + if (_parent.currentStepIndex !== index) return + + this.traceManager.buildCallPath(index, (error, callsPath) => { + if (error) { + console.log(error) + resetWarning(this) + } else { + this.currentCall = callsPath[callsPath.length - 1] + if (this.currentCall.reverted) { + this.revertionPoint = this.currentCall.return + this.view.querySelector('#reverted').style.display = 'block' + this.view.querySelector('#reverted #outofgas').style.display = this.currentCall.outOfGas ? 'inline' : 'none' + this.view.querySelector('#reverted #parenthasthrown').style.display = 'none' + } else { + var k = callsPath.length - 2 + while (k >= 0) { + var parent = callsPath[k] + if (parent.reverted) { + this.revertionPoint = parent.return + this.view.querySelector('#reverted').style.display = 'block' + this.view.querySelector('#reverted #parenthasthrown').style.display = parent ? 'inline' : 'none' + this.view.querySelector('#reverted #outofgas').style.display = 'none' + return + } + k-- + } + resetWarning(this) + } + } + }) + }) + + this.view +} + +module.exports = ButtonNavigator + +ButtonNavigator.prototype.render = function () { + var self = this + var view = yo`
+
+ + + + +
+ +
+ + + +
+ +
` + if (!this.view) { + this.view = view + } + return view +} + +ButtonNavigator.prototype.reset = function () { + this.intoBackDisabled = true + this.overBackDisabled = true + this.intoForwardDisabled = true + this.overForwardDisabled = true + this.jumpOutDisabled = true + this.jumpNextBreakpointDisabled = true + this.jumpPreviousBreakpointDisabled = true + resetWarning(this) +} + +ButtonNavigator.prototype.stepChanged = function (step) { + this.intoBackDisabled = step <= 0 + this.overBackDisabled = step <= 0 + if (!this.traceManager) { + this.intoForwardDisabled = true + this.overForwardDisabled = true + } else { + var self = this + this.traceManager.getLength(function (error, length) { + if (error) { + self.reset() + console.log(error) + } else { + self.jumpNextBreakpointDisabled = step >= length - 1 + self.jumpPreviousBreakpointDisabled = step <= 0 + self.intoForwardDisabled = step >= length - 1 + self.overForwardDisabled = step >= length - 1 + var stepOut = self.traceManager.findStepOut(step) + self.jumpOutDisabled = stepOut === step + } + self.updateAll() + }) + } + this.updateAll() +} + +ButtonNavigator.prototype.updateAll = function () { + this.updateDisabled('intoback', this.intoBackDisabled) + this.updateDisabled('overback', this.overBackDisabled) + this.updateDisabled('overforward', this.overForwardDisabled) + this.updateDisabled('intoforward', this.intoForwardDisabled) + this.updateDisabled('jumpout', this.jumpOutDisabled) + this.updateDisabled('jumptoexception', this.jumpOutDisabled) + this.updateDisabled('jumpnextbreakpoint', this.jumpNextBreakpointDisabled) + this.updateDisabled('jumppreviousbreakpoint', this.jumpPreviousBreakpointDisabled) +} + +ButtonNavigator.prototype.updateDisabled = function (id, disabled) { + if (disabled) { + document.getElementById(id).setAttribute('disabled', true) + } else { + document.getElementById(id).removeAttribute('disabled') + } +} + +function resetWarning (self) { + self.view.querySelector('#reverted #outofgas').style.display = 'none' + self.view.querySelector('#reverted #parenthasthrown').style.display = 'none' + self.view.querySelector('#reverted').style.display = 'none' +} + +module.exports = ButtonNavigator diff --git a/src/app/debugger/remix-debugger/src/ui/CalldataPanel.js b/src/app/debugger/remix-debugger/src/ui/CalldataPanel.js new file mode 100644 index 0000000000..e2fef57b74 --- /dev/null +++ b/src/app/debugger/remix-debugger/src/ui/CalldataPanel.js @@ -0,0 +1,33 @@ +'use strict' +var DropdownPanel = require('./DropdownPanel') +var yo = require('yo-yo') + +function CalldataPanel (_parent, _traceManager) { + this.parent = _parent + this.traceManager = _traceManager + this.basicPanel = new DropdownPanel('Call Data', {json: true}) + this.init() +} + +CalldataPanel.prototype.render = function () { + return yo`
${this.basicPanel.render()}
` +} + +CalldataPanel.prototype.init = function () { + var self = this + this.parent.event.register('indexChanged', this, function (index) { + if (index < 0) return + if (self.parent.currentStepIndex !== index) return + + self.traceManager.getCallDataAt(index, function (error, calldata) { + if (error) { + self.basicPanel.update({}) + console.log(error) + } else if (self.parent.currentStepIndex === index) { + self.basicPanel.update(calldata) + } + }) + }) +} + +module.exports = CalldataPanel diff --git a/src/app/debugger/remix-debugger/src/ui/CallstackPanel.js b/src/app/debugger/remix-debugger/src/ui/CallstackPanel.js new file mode 100644 index 0000000000..a9ecdbac5e --- /dev/null +++ b/src/app/debugger/remix-debugger/src/ui/CallstackPanel.js @@ -0,0 +1,33 @@ +'use strict' +var DropdownPanel = require('./DropdownPanel') +var yo = require('yo-yo') + +function CallstackPanel (_parent, _traceManager) { + this.parent = _parent + this.traceManager = _traceManager + this.basicPanel = new DropdownPanel('Call Stack', {json: true}) + this.init() +} + +CallstackPanel.prototype.render = function () { + return yo`
${this.basicPanel.render()}
` +} + +CallstackPanel.prototype.init = function () { + var self = this + this.parent.event.register('indexChanged', this, function (index) { + if (index < 0) return + if (self.parent.currentStepIndex !== index) return + + self.traceManager.getCallStackAt(index, function (error, callstack) { + if (error) { + console.log(error) + self.basicPanel.update({}) + } else if (self.parent.currentStepIndex === index) { + self.basicPanel.update(callstack) + } + }) + }) +} + +module.exports = CallstackPanel diff --git a/src/app/debugger/remix-debugger/src/ui/CodeListView.js b/src/app/debugger/remix-debugger/src/ui/CodeListView.js new file mode 100644 index 0000000000..784d6cbfe4 --- /dev/null +++ b/src/app/debugger/remix-debugger/src/ui/CodeListView.js @@ -0,0 +1,89 @@ +'use strict' +var style = require('./styles/basicStyles') +var yo = require('yo-yo') +var remixLib = require('remix-lib') +var DropdownPanel = require('./DropdownPanel') +var EventManager = remixLib.EventManager +var csjs = require('csjs-inject') +var styleGuide = remixLib.ui.themeChooser +var styles = styleGuide.chooser() + +var css = csjs` + .instructions { + ${styles.rightPanel.debuggerTab.box_Debugger} + width: 75%; + overflow-y: scroll; + max-height: 250px; + } +` +function CodeListView (_parent, _codeManager) { + this.event = new EventManager() + this.parent = _parent + this.codeManager = _codeManager + this.code + this.address + this.codeView + this.itemSelected + this.basicPanel = new DropdownPanel('Instructions', {json: false}) + this.basicPanel.event.register('hide', () => { + this.event.trigger('hide', []) + }) + this.basicPanel.event.register('show', () => { + this.event.trigger('show', []) + }) + this.init() +} + +CodeListView.prototype.render = function () { + return yo`
${this.basicPanel.render({height: style.instructionsList.height})}
` +} + +CodeListView.prototype.init = function () { + var self = this + this.codeManager.event.register('changed', this, this.changed) + this.parent.event.register('traceUnloaded', this, function () { + self.changed([], '', -1) + }) +} + +CodeListView.prototype.indexChanged = function (index) { + if (index >= 0) { + if (this.itemSelected) { + this.itemSelected.removeAttribute('selected') + this.itemSelected.removeAttribute('style') + if (this.itemSelected.firstChild) { + this.itemSelected.firstChild.removeAttribute('style') + } + } + this.itemSelected = this.codeView.children[index] + this.itemSelected.setAttribute('style', 'background-color: ' + styles.rightPanel.debuggerTab.text_BgHighlight) + this.itemSelected.setAttribute('selected', 'selected') + if (this.itemSelected.firstChild) { + this.itemSelected.firstChild.setAttribute('style', 'margin-left: 2px') + } + this.codeView.scrollTop = this.itemSelected.offsetTop - parseInt(this.codeView.offsetTop) + } +} + +CodeListView.prototype.changed = function (code, address, index) { + if (this.address !== address) { + this.code = code + this.address = address + this.codeView = this.renderAssemblyItems() + this.basicPanel.setContent(this.codeView) + } + this.indexChanged(index) +} + +CodeListView.prototype.renderAssemblyItems = function () { + if (this.code) { + var codeView = this.code.map(function (item, i) { + return yo`
${item}
` + }) + return yo`
+ ${codeView} +
` + } +} + +module.exports = CodeListView diff --git a/src/app/debugger/remix-debugger/src/ui/DropdownPanel.js b/src/app/debugger/remix-debugger/src/ui/DropdownPanel.js new file mode 100644 index 0000000000..17337d49e9 --- /dev/null +++ b/src/app/debugger/remix-debugger/src/ui/DropdownPanel.js @@ -0,0 +1,201 @@ +'use strict' +var yo = require('yo-yo') +const copy = require('clipboard-copy') +var remixLib = require('remix-lib') +var TreeView = require('./TreeView') +var EventManager = remixLib.EventManager + +var csjs = require('csjs-inject') +var styleGuide = remixLib.ui.themeChooser +var styles = styleGuide.chooser() + +var css = csjs` + .title { + margin-top: 10px; + ${styles.rightPanel.debuggerTab.dropdown_Debugger}; + display: flex; + align-items: center; + } + .name { + font-weight: bold; + } + .nameDetail { + font-weight: bold; + margin-left: 3px; + } + .icon { + color: ${styles.rightPanel.debuggerTab.button_Debugger_icon_Color}; + margin-right: 5%; + } + .eyeButton { + margin: 3px; + } + .eyeButton:hover { + color: ${styles.rightPanel.debuggerTab.button_Debugger_icon_HoverColor}; + } + .dropdownpanel { + ${styles.rightPanel.debuggerTab.dropdown_Debugger}; + width: 100%; + } + .dropdownrawcontent { + padding: 2px; + word-break: break-all; + } + .message { + padding: 2px; + word-break: break-all; + } + .refresh { + display: none; + margin-left: 4px; + margin-top: 4px; + animation: spin 2s linear infinite; + } +` + +function DropdownPanel (_name, _opts) { + this.event = new EventManager() + if (!_opts) { + _opts = {} + } + this.name = _name + this.header = '' + this.json = _opts.json + if (this.json) { + this.treeView = new TreeView(_opts) + } + this.view +} + +DropdownPanel.prototype.setMessage = function (message) { + if (this.view) { + this.view.querySelector('.dropdownpanel .dropdownrawcontent').style.display = 'none' + this.view.querySelector('.dropdownpanel .dropdowncontent').style.display = 'none' + this.view.querySelector('.dropdownpanel .fa-refresh').style.display = 'none' + this.message(message) + } +} + +DropdownPanel.prototype.setLoading = function () { + if (this.view) { + this.view.querySelector('.dropdownpanel .dropdownrawcontent').style.display = 'none' + this.view.querySelector('.dropdownpanel .dropdowncontent').style.display = 'none' + this.view.querySelector('.dropdownpanel .fa-refresh').style.display = 'inline-block' + this.message('') + } +} + +DropdownPanel.prototype.setUpdating = function () { + if (this.view) { + this.view.querySelector('.dropdownpanel .dropdowncontent').style.color = styles.appProperties.greyedText_color + } +} + +DropdownPanel.prototype.update = function (_data, _header) { + if (this.view) { + this.view.querySelector('.dropdownpanel .fa-refresh').style.display = 'none' + this.view.querySelector('.dropdownpanel .dropdowncontent').style.display = 'block' + this.view.querySelector('.dropdownpanel .dropdowncontent').style.color = styles.appProperties.mainText_Color + this.view.querySelector('.dropdownpanel .dropdownrawcontent').innerText = JSON.stringify(_data, null, '\t') + this.view.querySelector('.title div.btn').style.display = 'block' + this.view.querySelector('.title span').innerText = _header || ' ' + this.message('') + if (this.json) { + this.treeView.update(_data) + } + } +} + +DropdownPanel.prototype.setContent = function (node) { + if (this.view) { + var parent = this.view.querySelector('.dropdownpanel div.dropdowncontent') + parent.replaceChild(node, parent.firstElementChild) + } +} + +DropdownPanel.prototype.render = function (overridestyle) { + var content = yo`
Empty
` + if (this.json) { + content = this.treeView.render({}) + } + overridestyle === undefined ? {} : overridestyle + var self = this + var view = yo` +
+ +
+
+
${this.name}
+
+
+ +
` + if (!this.view) { + this.view = view + } + return view +} + +DropdownPanel.prototype.copyClipboard = function () { + var content = this.view.querySelector('.dropdownpanel .dropdownrawcontent') + if (content) copy(content.innerText ? content.innerText : content.textContent) +} + +DropdownPanel.prototype.toggle = function () { + var el = this.view.querySelector('.dropdownpanel') + var caret = this.view.querySelector('.title').firstElementChild + if (el.style.display === '') { + el.style.display = 'none' + caret.className = `${css.icon} fa fa-caret-right` + this.event.trigger('hide', []) + } else { + el.style.display = '' + caret.className = `${css.icon} fa fa-caret-down` + this.event.trigger('show', []) + } +} + +DropdownPanel.prototype.hide = function () { + if (this.view) { + var caret = this.view.querySelector('.title').firstElementChild + var el = this.view.querySelector('.dropdownpanel') + el.style.display = 'none' + caret.className = `${css.icon} fa fa-caret-right` + this.event.trigger('hide', []) + } +} + +DropdownPanel.prototype.show = function () { + if (this.view) { + var caret = this.view.querySelector('.title').firstElementChild + var el = this.view.querySelector('.dropdownpanel') + el.style.display = '' + caret.className = `${css.icon} fa fa-caret-down` + this.event.trigger('show', []) + } +} + +DropdownPanel.prototype.message = function (message) { + if (this.view) { + var mes = this.view.querySelector('.dropdownpanel .message') + mes.innerText = message + mes.style.display = (message === '') ? 'none' : 'block' + } +} + +module.exports = DropdownPanel diff --git a/src/app/debugger/remix-debugger/src/ui/Ethdebugger.js b/src/app/debugger/remix-debugger/src/ui/Ethdebugger.js new file mode 100644 index 0000000000..b799c0cc59 --- /dev/null +++ b/src/app/debugger/remix-debugger/src/ui/Ethdebugger.js @@ -0,0 +1,185 @@ +'use strict' +var TxBrowser = require('./TxBrowser') +var StepManager = require('./StepManager') +var remixCore = require('remix-core') +var TraceManager = remixCore.trace.TraceManager +var VmDebugger = require('./VmDebugger') +var remixLib = require('remix-lib') +var global = remixLib.global +var init = remixLib.init +var executionContext = remixLib.execution.executionContext +var EventManager = remixLib.EventManager +var yo = require('yo-yo') +var csjs = require('csjs-inject') +var Web3Providers = remixLib.vm.Web3Providers +var DummyProvider = remixLib.vm.DummyProvider +var CodeManager = remixCore.code.CodeManager +var remixSolidity = require('remix-solidity') +var SolidityProxy = remixSolidity.SolidityProxy +var InternalCallTree = remixSolidity.InternalCallTree + +var css = csjs` + .statusMessage { + margin-left: 15px; + } + .innerShift { + padding: 2px; + margin-left: 10px; + } +` + +function Ethdebugger (opts) { + this.opts = opts || {} + if (!this.opts.compilationResult) this.opts.compilationResult = () => { return null } + + var self = this + this.event = new EventManager() + + this.currentStepIndex = -1 + this.tx + this.statusMessage = '' + + this.view + this.web3Providers = new Web3Providers() + this.addProvider('DUMMYWEB3', new DummyProvider()) + this.switchProvider('DUMMYWEB3') + this.traceManager = new TraceManager() + this.codeManager = new CodeManager(this.traceManager) + this.solidityProxy = new SolidityProxy(this.traceManager, this.codeManager) + + var callTree = new InternalCallTree(this.event, this.traceManager, this.solidityProxy, this.codeManager, { includeLocalVariables: true }) + this.callTree = callTree // TODO: currently used by browser solidity, we should improve the API + + this.event.register('indexChanged', this, function (index) { + self.codeManager.resolveStep(index, self.tx) + }) + + this.txBrowser = new TxBrowser(this) + this.txBrowser.event.register('newTxLoading', this, function () { + self.unLoad() + }) + this.txBrowser.event.register('newTraceRequested', this, function (blockNumber, txIndex, tx) { + self.startDebugging(blockNumber, txIndex, tx) + }) + this.txBrowser.event.register('unloadRequested', this, function (blockNumber, txIndex, tx) { + self.unLoad() + }) + this.stepManager = new StepManager(this, this.traceManager) + this.stepManager.event.register('stepChanged', this, function (stepIndex) { + self.stepChanged(stepIndex) + }) + this.vmDebugger = new VmDebugger(this, this.traceManager, this.codeManager, this.solidityProxy, callTree) + + this.codeManager.event.register('changed', this, (code, address, instIndex) => { + this.callTree.sourceLocationTracker.getSourceLocationFromVMTraceIndex(address, this.currentStepIndex, this.solidityProxy.contracts, (error, sourceLocation) => { + if (!error) { + this.event.trigger('sourceLocationChanged', [sourceLocation]) + } + }) + }) +} + +Ethdebugger.prototype.setBreakpointManager = function (breakpointManager) { + this.breakpointManager = breakpointManager +} + +Ethdebugger.prototype.web3 = function () { + return global.web3 +} + +Ethdebugger.prototype.addProvider = function (type, obj) { + this.web3Providers.addProvider(type, obj) + this.event.trigger('providerAdded', [type]) +} + +Ethdebugger.prototype.switchProvider = function (type) { + var self = this + this.web3Providers.get(type, function (error, obj) { + if (error) { + console.log('provider ' + type + ' not defined') + } else { + global.web3 = obj + executionContext.detectNetwork((error, network) => { + if (error || !network) { + global.web3Debug = obj + } else { + var webDebugNode = init.web3DebugNode(network.name) + global.web3Debug = !webDebugNode ? obj : webDebugNode + } + }) + self.event.trigger('providerChanged', [type]) + } + }) +} + +Ethdebugger.prototype.setCompilationResult = function (compilationResult) { + if (compilationResult && compilationResult.sources && compilationResult.contracts) { + this.solidityProxy.reset(compilationResult) + } else { + this.solidityProxy.reset({}) + } +} + +Ethdebugger.prototype.debug = function (tx) { + this.setCompilationResult(this.opts.compilationResult()) + if (tx instanceof Object) { + this.txBrowser.load(tx.hash) + } else if (tx instanceof String) { + this.txBrowser.load(tx) + } +} + +Ethdebugger.prototype.render = function () { + var view = yo`
+
+ ${this.txBrowser.render()} + ${this.stepManager.render()} +
+
${this.statusMessage}
+ ${this.vmDebugger.render()} +
` + if (!this.view) { + this.view = view + } + return view +} + +Ethdebugger.prototype.unLoad = function () { + this.traceManager.init() + this.codeManager.clear() + this.stepManager.reset() + this.event.trigger('traceUnloaded') +} + +Ethdebugger.prototype.stepChanged = function (stepIndex) { + this.currentStepIndex = stepIndex + this.event.trigger('indexChanged', [stepIndex]) +} + +Ethdebugger.prototype.startDebugging = function (blockNumber, txIndex, tx) { + if (this.traceManager.isLoading) { + return + } + this.setCompilationResult(this.opts.compilationResult()) + this.statusMessage = 'Loading trace...' + yo.update(this.view, this.render()) + console.log('loading trace...') + this.tx = tx + var self = this + this.traceManager.resolveTrace(tx, function (error, result) { + console.log('trace loaded ' + result) + if (result) { + self.statusMessage = '' + yo.update(self.view, self.render()) + self.event.trigger('newTraceLoaded', [self.traceManager.trace]) + if (self.breakpointManager && self.breakpointManager.hasBreakpoint()) { + self.breakpointManager.jumpNextBreakpoint(false) + } + } else { + self.statusMessage = error ? error.message : 'Trace not loaded' + yo.update(self.view, self.render()) + } + }) +} + +module.exports = Ethdebugger diff --git a/src/app/debugger/remix-debugger/src/ui/FullStoragesChanges.js b/src/app/debugger/remix-debugger/src/ui/FullStoragesChanges.js new file mode 100644 index 0000000000..ef454851c2 --- /dev/null +++ b/src/app/debugger/remix-debugger/src/ui/FullStoragesChanges.js @@ -0,0 +1,71 @@ +'use strict' +var DropdownPanel = require('./DropdownPanel') +var remixCore = require('remix-core') +var StorageViewer = remixCore.storage.StorageViewer +var yo = require('yo-yo') + +function FullStoragesChanges (_parent, _traceManager) { + this.storageResolver = null + this.parent = _parent + this.traceManager = _traceManager + this.addresses = [] + this.view + this.traceLength + this.basicPanel = new DropdownPanel('Full Storages Changes', {json: true}) + this.init() +} + +FullStoragesChanges.prototype.render = function () { + var view = yo`
${this.basicPanel.render()}
` + if (!this.view) { + this.view = view + } + return view +} + +FullStoragesChanges.prototype.init = function () { + var self = this + this.parent.event.register('newTraceLoaded', this, function (length) { + self.panels = [] + self.traceManager.getAddresses(function (error, addresses) { + if (!error) { + self.addresses = addresses + self.basicPanel.update({}) + } + }) + + self.traceManager.getLength(function (error, length) { + if (!error) { + self.traceLength = length + } + }) + }) + + this.parent.event.register('indexChanged', this, function (index) { + if (index < 0) return + if (self.parent.currentStepIndex !== index) return + if (!self.storageResolver) return + + if (index === self.traceLength - 1) { + var storageJSON = {} + for (var k in self.addresses) { + var address = self.addresses[k] + var storageViewer = new StorageViewer({ + stepIndex: self.parent.currentStepIndex, + tx: self.parent.tx, + address: address + }, self.storageResolver, self.traceManager) + storageViewer.storageRange(function (error, result) { + if (!error) { + storageJSON[address] = result + self.basicPanel.update(storageJSON) + } + }) + } + } else { + self.basicPanel.update({}) + } + }) +} + +module.exports = FullStoragesChanges diff --git a/src/app/debugger/remix-debugger/src/ui/MemoryPanel.js b/src/app/debugger/remix-debugger/src/ui/MemoryPanel.js new file mode 100644 index 0000000000..c37cf48b2c --- /dev/null +++ b/src/app/debugger/remix-debugger/src/ui/MemoryPanel.js @@ -0,0 +1,39 @@ +'use strict' +var DropdownPanel = require('./DropdownPanel') +var remixLib = require('remix-lib') +var ui = remixLib.helpers.ui +var yo = require('yo-yo') + +function MemoryPanel (_parent, _traceManager) { + this.parent = _parent + this.traceManager = _traceManager + this.basicPanel = new DropdownPanel('Memory', { + json: true, + css: { + 'font-family': 'monospace' + }}) + this.init() +} + +MemoryPanel.prototype.render = function () { + return yo`
${this.basicPanel.render()}
` +} + +MemoryPanel.prototype.init = function () { + var self = this + this.parent.event.register('indexChanged', this, function (index) { + if (index < 0) return + if (self.parent.currentStepIndex !== index) return + + self.traceManager.getMemoryAt(index, function (error, memory) { + if (error) { + console.log(error) + self.basicPanel.update({}) + } else if (self.parent.currentStepIndex === index) { + self.basicPanel.update(ui.formatMemory(memory, 16)) + } + }) + }) +} + +module.exports = MemoryPanel diff --git a/src/app/debugger/remix-debugger/src/ui/Slider.js b/src/app/debugger/remix-debugger/src/ui/Slider.js new file mode 100644 index 0000000000..10ca255d81 --- /dev/null +++ b/src/app/debugger/remix-debugger/src/ui/Slider.js @@ -0,0 +1,75 @@ +'use strict' +var remixLib = require('remix-lib') +var EventManager = remixLib.EventManager +var yo = require('yo-yo') + +class Slider { + constructor (_traceManager, _stepOverride) { + this.event = new EventManager() + this.traceManager = _traceManager + this.max + this.disabled = true + this.view + this.solidityMode = false + this.stepOverride = _stepOverride + + this.previousValue = null + } + + render () { + var self = this + var view = yo`
+ +
` + if (!this.view) { + this.view = view + } + return view + } + + init (length) { + var slider = this.view.querySelector('#slider') + slider.setAttribute('max', length - 1) + this.max = length - 1 + this.updateDisabled(length === 0) + this.disabled = length === 0 + this.setValue(0) + } + + onChange (event) { + var value = parseInt(this.view.querySelector('#slider').value) + if (this.stepOverride) { + var correctedValue = this.stepOverride(value) + if (correctedValue !== value) { + this.setValue(correctedValue) + value = correctedValue + } + } + if (value === this.previousValue) return + this.previousValue = value + this.event.trigger('moved', [value]) + } + + setValue (value) { + this.view.querySelector('#slider').value = value + } + + updateDisabled (disabled) { + if (disabled) { + this.view.querySelector('#slider').setAttribute('disabled', true) + } else { + this.view.querySelector('#slider').removeAttribute('disabled') + } + } +} + +module.exports = Slider diff --git a/src/app/debugger/remix-debugger/src/ui/SolidityLocals.js b/src/app/debugger/remix-debugger/src/ui/SolidityLocals.js new file mode 100644 index 0000000000..3b8f6d91e0 --- /dev/null +++ b/src/app/debugger/remix-debugger/src/ui/SolidityLocals.js @@ -0,0 +1,80 @@ +'use strict' +var DropdownPanel = require('./DropdownPanel') +var remixSolidity = require('remix-solidity') +var localDecoder = remixSolidity.localDecoder +var solidityTypeFormatter = require('./SolidityTypeFormatter') +var remixCore = require('remix-core') +var StorageViewer = remixCore.storage.StorageViewer +var yo = require('yo-yo') + +class SolidityLocals { + + constructor (_parent, _traceManager, _internalTreeCall) { + this.parent = _parent + this.internalTreeCall = _internalTreeCall + this.storageResolver = null + this.traceManager = _traceManager + this.basicPanel = new DropdownPanel('Solidity Locals', { + json: true, + formatSelf: solidityTypeFormatter.formatSelf, + extractData: solidityTypeFormatter.extractData + }) + this.init() + this.view + } + + render () { + this.view = yo`
+ ${this.basicPanel.render()} +
` + return this.view + } + + init () { + var decodeTimeout = null + this.parent.event.register('sourceLocationChanged', this, (sourceLocation) => { + if (!this.storageResolver) { + this.basicPanel.setMessage('storage not ready') + return + } + if (decodeTimeout) { + window.clearTimeout(decodeTimeout) + } + this.basicPanel.setUpdating() + decodeTimeout = setTimeout(() => { + decode(this, sourceLocation) + }, 500) + }) + } +} + +function decode (self, sourceLocation) { + self.traceManager.waterfall([ + self.traceManager.getStackAt, + self.traceManager.getMemoryAt, + self.traceManager.getCurrentCalledAddressAt], + self.parent.currentStepIndex, + (error, result) => { + if (!error) { + var stack = result[0].value + var memory = result[1].value + try { + var storageViewer = new StorageViewer({ + stepIndex: self.parent.currentStepIndex, + tx: self.parent.tx, + address: result[2].value + }, self.storageResolver, self.traceManager) + localDecoder.solidityLocals(self.parent.currentStepIndex, self.internalTreeCall, stack, memory, storageViewer, sourceLocation).then((locals) => { + if (!locals.error) { + self.basicPanel.update(locals) + } + }) + } catch (e) { + self.basicPanel.setMessage(e.message) + } + } else { + console.log(error) + } + }) +} +module.exports = SolidityLocals diff --git a/src/app/debugger/remix-debugger/src/ui/SolidityState.js b/src/app/debugger/remix-debugger/src/ui/SolidityState.js new file mode 100644 index 0000000000..f69fc045f4 --- /dev/null +++ b/src/app/debugger/remix-debugger/src/ui/SolidityState.js @@ -0,0 +1,104 @@ +'use strict' +var DropdownPanel = require('./DropdownPanel') +var remixSolidity = require('remix-solidity') +var stateDecoder = remixSolidity.stateDecoder +var solidityTypeFormatter = require('./SolidityTypeFormatter') +var remixCore = require('remix-core') +var StorageViewer = remixCore.storage.StorageViewer +var yo = require('yo-yo') + +function SolidityState (_parent, _traceManager, _codeManager, _solidityProxy) { + this.storageResolver = null + this.parent = _parent + this.traceManager = _traceManager + this.codeManager = _codeManager + this.solidityProxy = _solidityProxy + this.basicPanel = new DropdownPanel('Solidity State', { + json: true, + formatSelf: solidityTypeFormatter.formatSelf, + extractData: solidityTypeFormatter.extractData + }) + this.init() + this.view + this.stateVariablesByAddresses = {} + _parent.event.register('traceUnloaded', () => { this.stateVariablesByAddresses = {} }) + _parent.event.register('newTraceLoaded', () => { this.stateVariablesByAddresses = {} }) +} + +SolidityState.prototype.render = function () { + if (!this.view) { + this.view = yo`
+ ${this.basicPanel.render()} +
` + } + return this.view +} + +SolidityState.prototype.init = function () { + var self = this + var decodeTimeout = null + this.parent.event.register('indexChanged', this, function (index) { + if (index < 0) { + self.basicPanel.setMessage('invalid step index') + return + } + + if (self.parent.currentStepIndex !== index) return + if (!self.solidityProxy.loaded()) { + self.basicPanel.setMessage('no source has been specified') + return + } + + if (!self.storageResolver) { + return + } + if (decodeTimeout) { + window.clearTimeout(decodeTimeout) + } + self.basicPanel.setUpdating() + decodeTimeout = setTimeout(() => { + decode(self, index) + }, 500) + }) +} + +function decode (self, index) { + self.traceManager.getCurrentCalledAddressAt(self.parent.currentStepIndex, (error, address) => { + if (error) { + self.basicPanel.update({}) + console.log(error) + } else { + if (self.stateVariablesByAddresses[address]) { + extractStateVariables(self, self.stateVariablesByAddresses[address], address) + } else { + self.solidityProxy.extractStateVariablesAt(index, function (error, stateVars) { + if (error) { + self.basicPanel.update({}) + console.log(error) + } else { + self.stateVariablesByAddresses[address] = stateVars + extractStateVariables(self, stateVars, address) + } + }) + } + } + }) +} + +function extractStateVariables (self, stateVars, address) { + var storageViewer = new StorageViewer({ + stepIndex: self.parent.currentStepIndex, + tx: self.parent.tx, + address: address + }, self.storageResolver, self.traceManager) + stateDecoder.decodeState(stateVars, storageViewer).then((result) => { + self.basicPanel.setMessage('') + if (!result.error) { + self.basicPanel.update(result) + } else { + self.basicPanel.setMessage(result.error) + } + }) +} + +module.exports = SolidityState diff --git a/src/app/debugger/remix-debugger/src/ui/SolidityTypeFormatter.js b/src/app/debugger/remix-debugger/src/ui/SolidityTypeFormatter.js new file mode 100644 index 0000000000..48c422418c --- /dev/null +++ b/src/app/debugger/remix-debugger/src/ui/SolidityTypeFormatter.js @@ -0,0 +1,71 @@ +'use strict' +var yo = require('yo-yo') +var BN = require('ethereumjs-util').BN + +module.exports = { + formatSelf: formatSelf, + extractData: extractData +} + +function formatSelf (key, data) { + var style = fontColor(data) + var keyStyle = data.isProperty ? 'color:#847979' : '' + if (data.type === 'string') { + data.self = JSON.stringify(data.self) + } + return yo`` +} + +function extractData (item, parent, key) { + var ret = {} + if (item.isProperty) { + return item + } + if (item.type.lastIndexOf(']') === item.type.length - 1) { + ret.children = (item.value || []).map(function (item, index) { + return {key: index, value: item} + }) + ret.children.unshift({ + key: 'length', + value: { + self: (new BN(item.length.replace('0x', ''), 16)).toString(10), + type: 'uint', + isProperty: true + } + }) + ret.isArray = true + ret.self = parent.isArray ? '' : item.type + } else if (item.type.indexOf('struct') === 0) { + ret.children = Object.keys((item.value || {})).map(function (key) { + return {key: key, value: item.value[key]} + }) + ret.self = item.type + ret.isStruct = true + } else if (item.type.indexOf('mapping') === 0) { + ret.children = Object.keys((item.value || {})).map(function (key) { + return {key: key, value: item.value[key]} + }) + ret.isMapping = true + ret.self = item.type + } else { + ret.children = null + ret.self = item.value + ret.type = item.type + } + return ret +} + +function fontColor (data) { + var color = '#124B46' + if (data.isArray || data.isStruct || data.isMapping) { + color = '#847979' + } else if (data.type.indexOf('uint') === 0 || + data.type.indexOf('int') === 0 || + data.type.indexOf('bool') === 0 || + data.type.indexOf('enum') === 0) { + color = '#0F0CE9' + } else if (data.type === 'string') { + color = '#E91E0C' + } + return 'color:' + color +} diff --git a/src/app/debugger/remix-debugger/src/ui/StackPanel.js b/src/app/debugger/remix-debugger/src/ui/StackPanel.js new file mode 100644 index 0000000000..c07aa01094 --- /dev/null +++ b/src/app/debugger/remix-debugger/src/ui/StackPanel.js @@ -0,0 +1,44 @@ +'use strict' +var DropdownPanel = require('./DropdownPanel') +var remixLib = require('remix-lib') +var ui = remixLib.helpers.ui +var yo = require('yo-yo') + +function StackPanel (_parent, _traceManager) { + this.parent = _parent + this.traceManager = _traceManager + this.basicPanel = new DropdownPanel('Stack', {json: true}) + this.init() +} + +StackPanel.prototype.render = function () { + return yo`
${this.basicPanel.render()}
` +} + +StackPanel.prototype.init = function () { + var self = this + this.parent.event.register('indexChanged', this, function (index) { + if (index < 0) return + if (self.parent.currentStepIndex !== index) return + + self.traceManager.getStackAt(index, function (error, stack) { + if (error) { + self.basicPanel.update({}) + console.log(error) + } else if (self.parent.currentStepIndex === index) { + self.basicPanel.update(self.format(stack)) + } + }) + }) +} + +StackPanel.prototype.format = function (stack) { + var ret = [] + stack.map(function (item, i) { + var hex = ui.normalizeHex(item) + ret.push(hex) + }) + return ret +} + +module.exports = StackPanel diff --git a/src/app/debugger/remix-debugger/src/ui/StepDetail.js b/src/app/debugger/remix-debugger/src/ui/StepDetail.js new file mode 100644 index 0000000000..88950e4e93 --- /dev/null +++ b/src/app/debugger/remix-debugger/src/ui/StepDetail.js @@ -0,0 +1,100 @@ +'use strict' +var yo = require('yo-yo') +var DropdownPanel = require('./DropdownPanel') + +function StepDetail (_parent, _traceManager) { + this.parent = _parent + this.traceManager = _traceManager + + this.basicPanel = new DropdownPanel('Step detail', {json: true}) + + this.detail = initDetail() + this.view + this.init() +} + +StepDetail.prototype.render = function () { + return yo`
${this.basicPanel.render()}
` +} + +StepDetail.prototype.init = function () { + var self = this + this.parent.event.register('traceUnloaded', this, function () { + self.detail = initDetail() + self.basicPanel.update(self.detail) + }) + + this.parent.event.register('newTraceLoaded', this, function () { + self.detail = initDetail() + self.basicPanel.update(self.detail) + }) + + this.parent.event.register('indexChanged', this, function (index) { + if (index < 0) return + + self.detail['vm trace step'] = index + + self.traceManager.getCurrentStep(index, function (error, step) { + if (error) { + console.log(error) + self.detail['execution step'] = '-' + } else { + self.detail['execution step'] = step + } + self.basicPanel.update(self.detail) + }) + + self.traceManager.getMemExpand(index, function (error, addmem) { + if (error) { + console.log(error) + self.detail['add memory'] = '-' + } else { + self.detail['add memory'] = addmem + } + self.basicPanel.update(self.detail) + }) + + self.traceManager.getStepCost(index, function (error, gas) { + if (error) { + console.log(error) + self.detail.gas = '-' + } else { + self.detail.gas = gas + } + self.basicPanel.update(self.detail) + }) + + self.traceManager.getCurrentCalledAddressAt(index, function (error, address) { + if (error) { + console.log(error) + self.detail['loaded address'] = '-' + } else { + self.detail['loaded address'] = address + } + self.basicPanel.update(self.detail) + }) + + self.traceManager.getRemainingGas(index, function (error, remaingas) { + if (error) { + console.log(error) + self.detail['remaining gas'] = '-' + } else { + self.detail['remaining gas'] = remaingas + } + self.basicPanel.update(self.detail) + }) + }) +} + +module.exports = StepDetail + +function initDetail () { + return { + 'vm trace step': '-', + 'execution step': '-', + 'add memory': '', + 'gas': '', + 'remaining gas': '-', + 'loaded address': '-' + } +} diff --git a/src/app/debugger/remix-debugger/src/ui/StepManager.js b/src/app/debugger/remix-debugger/src/ui/StepManager.js new file mode 100644 index 0000000000..eaa2ec0d85 --- /dev/null +++ b/src/app/debugger/remix-debugger/src/ui/StepManager.js @@ -0,0 +1,204 @@ +'use strict' +var ButtonNavigator = require('./ButtonNavigator') +var Slider = require('./Slider') +var remixLib = require('remix-lib') +var EventManager = remixLib.EventManager +var yo = require('yo-yo') +var util = remixLib.util + +function StepManager (_parent, _traceManager) { + this.event = new EventManager() + this.parent = _parent + this.traceManager = _traceManager + this.sourceMapByAddress = {} + this.solidityMode = false + + var self = this + this.parent.event.register('newTraceLoaded', this, function () { + self.traceManager.getLength(function (error, length) { + if (error) { + console.log(error) + } else { + self.slider.init(length) + self.init() + } + }) + }) + + this.slider = new Slider(this.traceManager, (step) => { + return this.solidityMode ? this.resolveToReducedTrace(step, 0) : step + }) + this.slider.event.register('moved', this, function (step) { + self.sliderMoved(step) + }) + + this.parent.callTree.event.register('callTreeReady', () => { + this.solidityMode = true + this.parent.vmDebugger.asmCode.event.register('hide', () => { + this.solidityMode = this.parent.callTree.reducedTrace.length !== 0 + }) + this.parent.vmDebugger.asmCode.event.register('show', () => { + this.solidityMode = false + }) + if (this.parent.callTree.functionCallStack.length) { + this.jumpTo(this.parent.callTree.functionCallStack[0]) + } + }) + + this.buttonNavigator = new ButtonNavigator(_parent, this.traceManager) + this.buttonNavigator.event.register('stepIntoBack', this, function () { + self.stepIntoBack() + }) + this.buttonNavigator.event.register('stepIntoForward', this, function () { + self.stepIntoForward() + }) + this.buttonNavigator.event.register('stepOverBack', this, function () { + self.stepOverBack() + }) + this.buttonNavigator.event.register('stepOverForward', this, function () { + self.stepOverForward() + }) + this.buttonNavigator.event.register('jumpOut', this, function () { + self.jumpOut() + }) + this.buttonNavigator.event.register('jumpToException', this, function (exceptionIndex) { + self.jumpTo(exceptionIndex) + }) + this.buttonNavigator.event.register('jumpNextBreakpoint', (exceptionIndex) => { + self.parent.breakpointManager.jumpNextBreakpoint(true) + }) + this.buttonNavigator.event.register('jumpPreviousBreakpoint', (exceptionIndex) => { + self.parent.breakpointManager.jumpPreviousBreakpoint(true) + }) +} + +StepManager.prototype.resolveToReducedTrace = function (value, incr) { + if (this.parent.callTree.reducedTrace.length) { + var nextSource = util.findClosestIndex(value, this.parent.callTree.reducedTrace) + nextSource = nextSource + incr + if (nextSource <= 0) { + nextSource = 0 + } else if (nextSource > this.parent.callTree.reducedTrace.length) { + nextSource = this.parent.callTree.reducedTrace.length - 1 + } + return this.parent.callTree.reducedTrace[nextSource] + } + return value +} + +StepManager.prototype.render = function () { + return ( + yo`
+ ${this.slider.render()} + ${this.buttonNavigator.render()} +
` + ) +} + +StepManager.prototype.reset = function () { + this.slider.setValue(0) + this.currentStepIndex = 0 + this.buttonNavigator.reset() +} + +StepManager.prototype.init = function () { + this.slider.setValue(0) + this.changeState(0) +} + +StepManager.prototype.newTraceAvailable = function () { + this.init() +} + +StepManager.prototype.jumpTo = function (step) { + if (!this.traceManager.inRange(step)) { + return + } + this.slider.setValue(step) + this.changeState(step) +} + +StepManager.prototype.sliderMoved = function (step) { + if (!this.traceManager.inRange(step)) { + return + } + this.changeState(step) +} + +StepManager.prototype.stepIntoForward = function () { + if (!this.traceManager.isLoaded()) { + return + } + var step = this.currentStepIndex + if (this.solidityMode) { + step = this.resolveToReducedTrace(step, 1) + } else { + step += 1 + } + if (!this.traceManager.inRange(step)) { + return + } + this.slider.setValue(step) + this.changeState(step) +} + +StepManager.prototype.stepIntoBack = function () { + if (!this.traceManager.isLoaded()) { + return + } + var step = this.currentStepIndex + if (this.solidityMode) { + step = this.resolveToReducedTrace(step, -1) + } else { + step -= 1 + } + if (!this.traceManager.inRange(step)) { + return + } + this.slider.setValue(step) + this.changeState(step) +} + +StepManager.prototype.stepOverForward = function () { + if (!this.traceManager.isLoaded()) { + return + } + var step = this.traceManager.findStepOverForward(this.currentStepIndex) + if (this.solidityMode) { + step = this.resolveToReducedTrace(step, 1) + } + this.slider.setValue(step) + this.changeState(step) +} + +StepManager.prototype.stepOverBack = function () { + if (!this.traceManager.isLoaded()) { + return + } + var step = this.traceManager.findStepOverBack(this.currentStepIndex) + if (this.solidityMode) { + step = this.resolveToReducedTrace(step, -1) + } + this.slider.setValue(step) + this.changeState(step) +} + +StepManager.prototype.jumpOut = function () { + if (!this.traceManager.isLoaded()) { + return + } + var step = this.traceManager.findStepOut(this.currentStepIndex) + if (this.solidityMode) { + step = this.resolveToReducedTrace(step, 0) + } + this.slider.setValue(step) + this.changeState(step) +} + +StepManager.prototype.changeState = function (step) { + this.currentStepIndex = step + this.buttonNavigator.stepChanged(step) + this.event.trigger('stepChanged', [step]) +} + +module.exports = StepManager diff --git a/src/app/debugger/remix-debugger/src/ui/StoragePanel.js b/src/app/debugger/remix-debugger/src/ui/StoragePanel.js new file mode 100644 index 0000000000..cd4573e4f7 --- /dev/null +++ b/src/app/debugger/remix-debugger/src/ui/StoragePanel.js @@ -0,0 +1,50 @@ +'use strict' +var DropdownPanel = require('./DropdownPanel') +var remixCore = require('remix-core') +var StorageViewer = remixCore.storage.StorageViewer +var yo = require('yo-yo') + +function StoragePanel (_parent, _traceManager) { + this.parent = _parent + this.storageResolver = null + this.traceManager = _traceManager + this.basicPanel = new DropdownPanel('Storage', {json: true}) + this.init() + this.disabled = false +} + +StoragePanel.prototype.render = function () { + return yo`
${this.basicPanel.render()}
` +} + +StoragePanel.prototype.init = function () { + var self = this + this.parent.event.register('indexChanged', this, function (index) { + if (self.disabled) return + if (index < 0) return + if (self.parent.currentStepIndex !== index) return + if (!self.storageResolver) return + + this.traceManager.getCurrentCalledAddressAt(index, (error, address) => { + if (!error) { + var storageViewer = new StorageViewer({ + stepIndex: self.parent.currentStepIndex, + tx: self.parent.tx, + address: address + }, self.storageResolver, self.traceManager) + + storageViewer.storageRange((error, storage) => { + if (error) { + console.log(error) + self.basicPanel.update({}) + } else if (self.parent.currentStepIndex === index) { + var header = storageViewer.isComplete(address) ? 'completely loaded' : 'partially loaded...' + self.basicPanel.update(storage, header) + } + }) + } + }) + }) +} + +module.exports = StoragePanel diff --git a/src/app/debugger/remix-debugger/src/ui/TreeView.js b/src/app/debugger/remix-debugger/src/ui/TreeView.js new file mode 100644 index 0000000000..7601a707f3 --- /dev/null +++ b/src/app/debugger/remix-debugger/src/ui/TreeView.js @@ -0,0 +1,186 @@ +'use strict' +var yo = require('yo-yo') +var csjs = require('csjs-inject') +var css = csjs` + .li_tv { + list-style-type: none; + -webkit-margin-before: 0px; + -webkit-margin-after: 0px; + -webkit-margin-start: 0px; + -webkit-margin-end: 0px; + -webkit-padding-start: 0px; + margin-left: 10px; + } + .ul_tv { + list-style-type: none; + -webkit-margin-before: 0px; + -webkit-margin-after: 0px; + -webkit-margin-start: 0px; + -webkit-margin-end: 0px; + -webkit-padding-start: 0px; + } + .caret_tv { + width: 10px; + } + .label_tv { + display: flex; + align-items: center; + } +` + +var remixLib = require('remix-lib') +var EventManager = remixLib.EventManager + +/** + * TreeView + * - extendable by specifying custom `extractData` and `formatSelf` function + * - trigger `nodeClick` and `leafClick` + */ +class TreeView { + + constructor (opts) { + this.event = new EventManager() + this.extractData = opts.extractData || this.extractDataDefault + this.formatSelf = opts.formatSelf || this.formatSelfDefault + this.view = null + } + + render (json, expand) { + var view = this.renderProperties(json, expand) + if (!this.view) { + this.view = view + } + return view + } + + update (json) { + if (this.view) { + yo.update(this.view, this.render(json)) + } + } + + renderObject (item, parent, key, expand, keyPath) { + var data = this.extractData(item, parent, key) + var children = (data.children || []).map((child, index) => { + return this.renderObject(child.value, data, child.key, expand, keyPath + '/' + child.key) + }) + return this.formatData(key, data, children, expand, keyPath) + } + + renderProperties (json, expand, key) { + key = key || '' + var children = Object.keys(json).map((innerkey) => { + return this.renderObject(json[innerkey], json, innerkey, expand, innerkey) + }) + return yo`` + } + + formatData (key, data, children, expand, keyPath) { + var self = this + var li = yo`
  • ` + var caret = yo`
    ` + var label = yo` +
    + ${caret} + ${self.formatSelf(key, data, li)} +
    ` + li.appendChild(label) + if (data.children) { + var list = yo`` + list.style.display = 'none' + caret.className = list.style.display === 'none' ? `fa fa-caret-right caret ${css.caret_tv}` : `fa fa-caret-down caret ${css.caret_tv}` + label.onclick = function () { + self.expand(keyPath) + } + label.oncontextmenu = function (event) { + self.event.trigger('nodeRightClick', [keyPath, data, label, event]) + } + li.appendChild(list) + } else { + caret.style.visibility = 'hidden' + label.oncontextmenu = function (event) { + self.event.trigger('leafRightClick', [keyPath, data, label, event]) + } + label.onclick = function (event) { + self.event.trigger('leafClick', [keyPath, data, label, event]) + } + } + return li + } + + isExpanded (path) { + var current = this.nodeAt(path) + if (current) { + return current.style.display !== 'none' + } + return false + } + + expand (path) { + var caret = this.caretAt(path) + var node = this.nodeAt(path) + if (node) { + node.style.display = node.style.display === 'none' ? 'block' : 'none' + caret.className = node.style.display === 'none' ? `fa fa-caret-right caret ${css.caret_tv}` : `fa fa-caret-down caret ${css.caret_tv}` + this.event.trigger('nodeClick', [path, node]) + } + } + + caretAt (path) { + var label = this.labelAt(path) + if (label) { + return label.querySelector('.caret') + } + } + + itemAt (path) { + return this.view.querySelector(`li[key="${path}"]`) + } + + labelAt (path) { + return this.view.querySelector(`div[key="${path}"]`) + } + + nodeAt (path) { + return this.view.querySelector(`ul[key="${path}"]`) + } + + updateNodeFromJSON (path, jsonTree, expand) { + var newTree = this.renderProperties(jsonTree, expand, path) + var current = this.nodeAt(path) + if (current && current.parentElement) { + current.parentElement.replaceChild(newTree, current) + } + } + + formatSelfDefault (key, data) { + return yo`` + } + + extractDataDefault (item, parent, key) { + var ret = {} + if (item instanceof Array) { + ret.children = item.map((item, index) => { + return {key: index, value: item} + }) + ret.self = 'Array' + ret.isNode = true + ret.isLeaf = false + } else if (item instanceof Object) { + ret.children = Object.keys(item).map((key) => { + return {key: key, value: item[key]} + }) + ret.self = 'Object' + ret.isNode = true + ret.isLeaf = false + } else { + ret.self = item + ret.children = null + ret.isNode = false + ret.isLeaf = true + } + return ret + } +} + +module.exports = TreeView diff --git a/src/app/debugger/remix-debugger/src/ui/TxBrowser.js b/src/app/debugger/remix-debugger/src/ui/TxBrowser.js new file mode 100644 index 0000000000..2d90132d26 --- /dev/null +++ b/src/app/debugger/remix-debugger/src/ui/TxBrowser.js @@ -0,0 +1,211 @@ +var remixLib = require('remix-lib') +var global = remixLib.global +var EventManager = remixLib.EventManager +var traceHelper = remixLib.helpers.trace +var yo = require('yo-yo') +var init = remixLib.init +var DropdownPanel = require('./DropdownPanel') +var csjs = require('csjs-inject') +var styleGuide = remixLib.ui.themeChooser +var styles = styleGuide.chooser() + +var css = csjs` + .container { + display: flex; + flex-direction: column; + } + .txContainer { + display: flex; + flex-direction: column; + } + .txinputs { + width: 100%; + display: flex; + justify-content: center; + } + .txinput { + ${styles.rightPanel.debuggerTab.input_Debugger} + min-width: 30px; + margin: 3px; + } + .txbuttons { + width: 100%; + display: flex; + justify-content: center; + } + .txbutton { + ${styles.rightPanel.debuggerTab.button_Debugger} + } + .txbutton:hover { + color: ${styles.rightPanel.debuggerTab.button_Debugger_icon_HoverColor}; + } + .txinfo { + margin-top: 5px; + } + .vmargin { + margin-top: 10px; + margin-bottom: 10px; + } +` +function TxBrowser (_parent) { + this.event = new EventManager() + + this.blockNumber + this.txNumber + this.view + this.displayConnectionSetting = true + this.basicPanel = new DropdownPanel('Transaction', {json: true}) + this.basicPanel.data = {} + var self = this + _parent.event.register('providerChanged', this, function (provider) { + self.displayConnectionSetting = provider === 'INTERNAL' + self.setDefaultValues() + if (self.view) { + yo.update(self.view, self.render()) + } + }) +} + +// creation 0xa9619e1d0a35b2c1d686f5b661b3abd87f998d2844e8e9cc905edb57fc9ce349 +// invokation 0x71a6d583d16d142c5c3e8903060e8a4ee5a5016348a9448df6c3e63b68076ec4 0xcda2b2835add61af54cf83bd076664d98d7908c6cd98d86423b3b48d8b8e51ff +// test: +// creation: 0x72908de76f99fca476f9e3a3b5d352f350a98cd77d09cebfc59ffe32a6ecaa0b +// invokation: 0x20ef65b8b186ca942fcccd634f37074dde49b541c27994fc7596740ef44cfd51 + +TxBrowser.prototype.setDefaultValues = function () { + this.connectInfo = '' + this.basicPanel.update({}) + this.basicPanel.hide() + if (this.view) { + yo.update(this.view, this.render()) + } +} + +TxBrowser.prototype.submit = function () { + if (!this.txNumber) { + return + } + this.event.trigger('newTxLoading', [this.blockNumber, this.txNumber]) + try { + var self = this + if (this.txNumber.indexOf('0x') !== -1) { + global.web3.eth.getTransaction(this.txNumber, function (error, result) { + self.update(error, result) + }) + } else { + global.web3.eth.getTransactionFromBlock(this.blockNumber, this.txNumber, function (error, result) { + self.update(error, result) + }) + } + } catch (e) { + self.update(e.message) + } +} + +TxBrowser.prototype.update = function (error, tx) { + var info = {} + if (error) { + this.view.querySelector('#error').innerHTML = error + } else { + if (tx) { + this.view.querySelector('#error').innerHTML = '' + if (!tx.to) { + tx.to = traceHelper.contractCreationToken('0') + } + info.from = tx.from + info.to = tx.to + info.hash = tx.hash + this.event.trigger('newTraceRequested', [this.blockNumber, this.txNumber, tx]) + } else { + var mes = '' + info.from = mes + info.to = mes + info.hash = mes + this.view.querySelector('#error').innerHTML = 'Cannot find transaction with reference. Block number: ' + this.blockNumber + '. Transaction index/hash: ' + this.txNumber + } + } + this.basicPanel.update(info) +} + +TxBrowser.prototype.updateWeb3Url = function (newhost) { + init.setProvider(global.web3, newhost) + var self = this + this.checkWeb3(function (error, block) { + if (!error) { + self.connectInfo = 'Connected to ' + global.web3.currentProvider.host + '. Current block number: ' + block + } else { + self.connectInfo = 'Unable to connect to ' + global.web3.currentProvider.host + '. ' + error.message + } + yo.update(self.view, self.render()) + }) +} + +TxBrowser.prototype.checkWeb3 = function (callback) { + try { + global.web3.eth.getBlockNumber(function (error, block) { + callback(error, block) + }) + } catch (e) { + console.log(e) + callback(e.message, null) + } +} + +TxBrowser.prototype.updateBlockN = function (ev) { + this.blockNumber = ev.target.value +} + +TxBrowser.prototype.updateTxN = function (ev) { + this.txNumber = ev.target.value +} + +TxBrowser.prototype.load = function (txHash) { + this.txNumber = txHash + this.submit() +} + +TxBrowser.prototype.unload = function (txHash) { + this.event.trigger('unloadRequested') + this.init() +} + +TxBrowser.prototype.init = function (ev) { + this.setDefaultValues() +} + +TxBrowser.prototype.connectionSetting = function () { + if (this.displayConnectionSetting) { + var self = this + return yo`
    Node URL: + ${this.connectInfo}
    ` + } else { + return '' + } +} + +TxBrowser.prototype.render = function () { + var self = this + var view = yo`
    + ${this.connectionSetting()} +
    +
    + + +
    +
    + + +
    +
    + +
    + ${this.basicPanel.render()} +
    +
    ` + if (!this.view) { + this.view = view + } + return view +} + +module.exports = TxBrowser diff --git a/src/app/debugger/remix-debugger/src/ui/VmDebugger.js b/src/app/debugger/remix-debugger/src/ui/VmDebugger.js new file mode 100644 index 0000000000..c372c7b9f5 --- /dev/null +++ b/src/app/debugger/remix-debugger/src/ui/VmDebugger.js @@ -0,0 +1,90 @@ +'use strict' +var CodeListView = require('./CodeListView') +var CalldataPanel = require('./CalldataPanel') +var MemoryPanel = require('./MemoryPanel') +var CallstackPanel = require('./CallstackPanel') +var StackPanel = require('./StackPanel') +var StoragePanel = require('./StoragePanel') +var FullStoragesChangesPanel = require('./FullStoragesChanges') +var StepDetail = require('./StepDetail') +var DropdownPanel = require('./DropdownPanel') +var SolidityState = require('./SolidityState') +var SolidityLocals = require('./SolidityLocals') +var remixCore = require('remix-core') +var StorageResolver = remixCore.storage.StorageResolver +var yo = require('yo-yo') + +function VmDebugger (_parent, _traceManager, _codeManager, _solidityProxy, _callTree) { + this.asmCode = new CodeListView(_parent, _codeManager) + this.stackPanel = new StackPanel(_parent, _traceManager) + this.storagePanel = new StoragePanel(_parent, _traceManager) + this.memoryPanel = new MemoryPanel(_parent, _traceManager) + this.calldataPanel = new CalldataPanel(_parent, _traceManager) + this.callstackPanel = new CallstackPanel(_parent, _traceManager) + this.stepDetail = new StepDetail(_parent, _traceManager) + this.solidityState = new SolidityState(_parent, _traceManager, _codeManager, _solidityProxy) + this.solidityLocals = new SolidityLocals(_parent, _traceManager, _callTree) + + /* Return values - */ + this.returnValuesPanel = new DropdownPanel('Return Value', {json: true}) + this.returnValuesPanel.data = {} + _parent.event.register('indexChanged', this.returnValuesPanel, function (index) { + var self = this + _traceManager.getReturnValue(index, function (error, returnValue) { + if (error) { + self.update([error]) + } else if (_parent.currentStepIndex === index) { + self.update([returnValue]) + } + }) + }) + /* Return values - */ + + this.fullStoragesChangesPanel = new FullStoragesChangesPanel(_parent, _traceManager) + + this.view + var self = this + _parent.event.register('newTraceLoaded', this, function () { + var storageResolver = new StorageResolver() + self.storagePanel.storageResolver = storageResolver + self.solidityState.storageResolver = storageResolver + self.solidityLocals.storageResolver = storageResolver + self.fullStoragesChangesPanel.storageResolver = storageResolver + self.view.style.display = 'block' + }) + _parent.event.register('traceUnloaded', this, function () { + self.view.style.display = 'none' + }) + _parent.callTree.event.register('callTreeReady', () => { + if (_parent.callTree.reducedTrace.length) { + self.solidityLocals.basicPanel.show() + self.solidityState.basicPanel.show() + } else { + self.asmCode.basicPanel.show() + } + }) +} + +VmDebugger.prototype.render = function () { + var view = yo`` + if (!this.view) { + this.view = view + } + return view +} + +module.exports = VmDebugger diff --git a/src/app/debugger/remix-debugger/src/ui/styles/basicStyles.js b/src/app/debugger/remix-debugger/src/ui/styles/basicStyles.js new file mode 100644 index 0000000000..c824703d3f --- /dev/null +++ b/src/app/debugger/remix-debugger/src/ui/styles/basicStyles.js @@ -0,0 +1,84 @@ +'use strict' +module.exports = { + truncate: { + 'white-space': 'nowrap', + 'overflow': 'hidden', + 'text-overflow': 'ellipsis', + 'margin-right': '5px' + }, + font: { + 'font-family': 'arial,sans-serif' + }, + innerShift: { + 'padding': '2px', + 'margin-left': '10px' + }, + container: { + 'margin': '10px', + 'padding': '5px' + }, + statusMessage: { + 'margin-left': '15px' + }, + address: { + 'font-style': 'italic' + }, + instructionsList: { + 'width': '52%', + 'overflow-y': 'scroll', + 'max-height': '250px', + 'margin': '0', + 'margin-left': '10px', + 'padding': '2px' + }, + transactionInfo: { + 'margin-top': '5px' + }, + panel: { + container: { + 'border': '1px solid', + 'width': '70%' + }, + tableContainer: { + 'height': '50%', + 'overflow-y': 'auto' + }, + table: { + 'padding': '5px' + }, + title: { + 'padding': '5px', + 'font-style': 'italic' + } + }, + hidden: { + 'display': 'none' + }, + display: { + 'display': 'block' + }, + inline: { + 'display': 'inline-block' + }, + vmargin: { + 'margin-top': '10px', + 'margin-bottom': '10px' + }, + button: { + 'border-color': 'transparent', + 'border-radius': '3px', + 'border': '.3px solid ${colors.veryLightGrey}', + 'cursor': 'pointer', + 'min-height': '25px', + 'max-height': '25px', + 'padding': '3px', + 'min-width': '100px', + 'font-size': '12px', + 'overflow': 'hidden', + 'word-break': 'normal', + 'background-color': 'hsla(0, 0%, 40%, .2)', + 'color': 'hsla(0, 0%, 40%, 1)', + 'margin': '3px', + 'text-decoration': 'none' + } +} diff --git a/src/app/debugger/remix-debugger/src/ui/styles/dropdownPanel.js b/src/app/debugger/remix-debugger/src/ui/styles/dropdownPanel.js new file mode 100644 index 0000000000..bb9a1e7f2b --- /dev/null +++ b/src/app/debugger/remix-debugger/src/ui/styles/dropdownPanel.js @@ -0,0 +1,31 @@ +'use strict' +module.exports = { + title: { + 'border': '1px solid #dadada', + 'background-color': 'white', + 'width': '100%', + 'color': '#363f47', + 'margin-top': '5px', + 'cursor': 'pointer' + }, + titleInner: { + 'display': 'inline-block' + }, + content: { + 'color': '#111111', + 'width': '100%', + 'min-height': '20px' + }, + inner: { + 'padding': '2px', + 'word-break': 'break-all' + }, + copyBtn: { + 'float': 'right', + 'margin-top': '3px' + }, + caret: { + 'margin-left': '10px', + 'margin-right': '10px' + } +} diff --git a/src/app/debugger/remix-debugger/src/ui/styles/sliderStyles.js b/src/app/debugger/remix-debugger/src/ui/styles/sliderStyles.js new file mode 100644 index 0000000000..45912b6380 --- /dev/null +++ b/src/app/debugger/remix-debugger/src/ui/styles/sliderStyles.js @@ -0,0 +1,6 @@ +'use strict' +module.exports = { + rule: { + 'width': '100%' + } +} diff --git a/src/app/debugger/remix-debugger/src/ui/styles/treeView.js b/src/app/debugger/remix-debugger/src/ui/styles/treeView.js new file mode 100644 index 0000000000..b50bacd24f --- /dev/null +++ b/src/app/debugger/remix-debugger/src/ui/styles/treeView.js @@ -0,0 +1,30 @@ +'use strict' +module.exports = { + cssUl: { + 'list-style-type': 'none', + '-webkit-margin-before': '0px', + '-webkit-margin-after': '0px', + '-webkit-margin-start': '0px', + '-webkit-margin-end': '0px', + '-webkit-padding-start': '0px' + }, + cssLi: { + 'list-style-type': 'none', + '-webkit-margin-before': '0px', + '-webkit-margin-after': '0px', + '-webkit-margin-start': '0px', + '-webkit-margin-end': '0px', + '-webkit-padding-start': '0px', + 'margin-left': '10px' + }, + label: { + 'vertical-align': 'top', + 'font-family': 'arial,sans-serif' + }, + caret: { + 'margin-top': '3px', + 'width': '10px' + }, + data: { + } +} diff --git a/src/app/debugger/remix-debugger/test-browser/resources/insertTestWeb3.js b/src/app/debugger/remix-debugger/test-browser/resources/insertTestWeb3.js new file mode 100644 index 0000000000..833c7198e1 --- /dev/null +++ b/src/app/debugger/remix-debugger/test-browser/resources/insertTestWeb3.js @@ -0,0 +1,77 @@ +/* global XMLHttpRequest */ +function loadJSON (url, callback) { + var xobj = new XMLHttpRequest() + xobj.overrideMimeType('application/json') + xobj.open('GET', url, true) + xobj.onreadystatechange = function () { + if (xobj.readyState === 4 && xobj.status === 200) { + callback(xobj.responseText) + } + } + xobj.send(null) +} + +function loadTestWeb3 (data) { + var container = document.getElementById('app') + var vmdebugger = container.debugger + var uiTestweb3 = {} + uiTestweb3.eth = {} + uiTestweb3.debug = {} + uiTestweb3.eth.getCode = function (address, callback) { + if (callback) { + callback(null, data.testCodes[address]) + } else { + return data.testCodes[address] + } + } + + uiTestweb3.debug.traceTransaction = function (txHash, options, callback) { + callback(null, data.testTraces[txHash]) + } + + uiTestweb3.debug.storageRangeAt = function (blockNumber, txIndex, address, start, size, callback) { + callback(null, { storage: {}, complete: true }) + } + + uiTestweb3.eth.getTransaction = function (txHash, callback) { + if (callback) { + callback(null, data.testTxs[txHash]) + } else { + return data.testTxs[txHash] + } + } + + uiTestweb3.eth.getTransactionFromBlock = function (blockNumber, txIndex, callback) { + if (callback) { + callback(null, data.testTxsByBlock[blockNumber + '-' + txIndex]) + } else { + return data.testTxsByBlock[blockNumber + '-' + txIndex] + } + } + + uiTestweb3.eth.getBlockNumber = function (callback) { callback(null, 'web3 modified for testing purposes :)') } + + uiTestweb3.providers = { 'HttpProvider': function (url) {} } + + uiTestweb3.setProvider = function (provider) {} + + uiTestweb3.currentProvider = {host: 'web3 modified for testing purposes :)'} + vmdebugger.addProvider('TEST', uiTestweb3) + vmdebugger.switchProvider('TEST') +} + +function waitForRemix (data) { + setTimeout(function () { + if (!document.getElementById('app').debugger) { + waitForRemix(data) + } else { + loadTestWeb3(data) + } + }, 500) +} + +loadJSON('/test-browser/resources/testWeb3.json', function (result) { + var data = JSON.parse(result) + waitForRemix(data) +}) + diff --git a/src/app/debugger/remix-debugger/test-browser/resources/testWeb3.json b/src/app/debugger/remix-debugger/test-browser/resources/testWeb3.json new file mode 100644 index 0000000000..ba5f88cb0f --- /dev/null +++ b/src/app/debugger/remix-debugger/test-browser/resources/testWeb3.json @@ -0,0 +1,17 @@ +{ + "testTxs": { + "0x20ef65b8b186ca942fcccd634f37074dde49b541c27994fc7596740ef44cfd51": {"blockHash":"0xd1d34932f8733e0485b7d9bf8500c4046d650f20ed7792508c304304fa7bbfac","blockNumber":89,"from":"0x00101c5bfa3fc8bad02c9f5fd65b069306251915","gas":105967,"gasPrice":"20000000000","hash":"0x20ef65b8b186ca942fcccd634f37074dde49b541c27994fc7596740ef44cfd51","input":"0x60fe47b10000000000000000000000000000000000000000000000000000000000000038","nonce":3,"to":"0x0d3a18d64dfe4f927832ab58d6451cecc4e517c5","transactionIndex":0,"value":"0"} + }, + + "testTxsByBlock": { + "105967-0": {"blockHash":"0xd1d34932f8733e0485b7d9bf8500c4046d650f20ed7792508c304304fa7bbfac","blockNumber":89,"from":"0x00101c5bfa3fc8bad02c9f5fd65b069306251915","gas":105967,"gasPrice":"20000000000","hash":"0x20ef65b8b186ca942fcccd634f37074dde49b541c27994fc7596740ef44cfd51","input":"0x60fe47b10000000000000000000000000000000000000000000000000000000000000038","nonce":3,"to":"0x0d3a18d64dfe4f927832ab58d6451cecc4e517c5","transactionIndex":0,"value":"0"} + }, + + "testCodes": { + "0x0d3a18d64dfe4f927832ab58d6451cecc4e517c5": "0x60606040526000357c01000000000000000000000000000000000000000000000000000000009004806360fe47b11460415780636d4ce63c14605757603f565b005b605560048080359060200190919050506089565b005b606260048050506078565b6040518082815260200191505060405180910390f35b600060006000505490506086565b90565b80600060005081905550602d6040516045806100f083390180828152602001915050604051809103906000f0600160006101000a81548173ffffffffffffffffffffffffffffffffffffffff02191690830217905550602281016000600050819055505b505660606040526040516020806045833981016040528080519060200190919050505b806001016000600050819055505b50600a80603b6000396000f360606040526008565b00" + }, + + "testTraces": { + "0x20ef65b8b186ca942fcccd634f37074dde49b541c27994fc7596740ef44cfd51": {"gas":"0x0000000000000000000000000000000000000000000000000000000000019def","return":"0x","structLogs":[{"gas":"84503","gasCost":"3","memory":[],"op":"PUSH1","pc":"0","stack":[]},{"gas":"84500","gasCost":"3","op":"PUSH1","pc":"2","stack":["0x60"]},{"gas":"84497","gasCost":"12","memexpand":"3","op":"MSTORE","pc":"4","stack":["0x60","0x40"]},{"gas":"84485","gasCost":"3","memory":["0000000000000000000000000000000000000000000000000000000000000000","0000000000000000000000000000000000000000000000000000000000000000","0000000000000000000000000000000000000000000000000000000000000060"],"op":"PUSH1","pc":"5","stack":[]},{"gas":"84482","gasCost":"3","op":"CALLDATALOAD","pc":"7","stack":["0x00"]},{"gas":"84479","gasCost":"3","op":"PUSH29","pc":"8","stack":["0x60fe47b100000000000000000000000000000000000000000000000000000000"]},{"gas":"84476","gasCost":"3","op":"SWAP1","pc":"38","stack":["0x60fe47b100000000000000000000000000000000000000000000000000000000","0x0100000000000000000000000000000000000000000000000000000000"]},{"gas":"84473","gasCost":"5","op":"DIV","pc":"39","stack":["0x0100000000000000000000000000000000000000000000000000000000","0x60fe47b100000000000000000000000000000000000000000000000000000000"]},{"gas":"84468","gasCost":"3","op":"DUP1","pc":"40","stack":["0x60fe47b1"]},{"gas":"84465","gasCost":"3","op":"PUSH4","pc":"41","stack":["0x60fe47b1","0x60fe47b1"]},{"gas":"84462","gasCost":"3","op":"EQ","pc":"46","stack":["0x60fe47b1","0x60fe47b1","0x60fe47b1"]},{"gas":"84459","gasCost":"3","op":"PUSH1","pc":"47","stack":["0x60fe47b1","0x01"]},{"gas":"84456","gasCost":"10","op":"JUMPI","pc":"49","stack":["0x60fe47b1","0x01","0x41"]},{"gas":"84446","gasCost":"1","op":"JUMPDEST","pc":"65","stack":["0x60fe47b1"]},{"gas":"84445","gasCost":"3","op":"PUSH1","pc":"66","stack":["0x60fe47b1"]},{"gas":"84442","gasCost":"3","op":"PUSH1","pc":"68","stack":["0x60fe47b1","0x55"]},{"gas":"84439","gasCost":"3","op":"DUP1","pc":"70","stack":["0x60fe47b1","0x55","0x04"]},{"gas":"84436","gasCost":"3","op":"DUP1","pc":"71","stack":["0x60fe47b1","0x55","0x04","0x04"]},{"gas":"84433","gasCost":"3","op":"CALLDATALOAD","pc":"72","stack":["0x60fe47b1","0x55","0x04","0x04","0x04"]},{"gas":"84430","gasCost":"3","op":"SWAP1","pc":"73","stack":["0x60fe47b1","0x55","0x04","0x04","0x38"]},{"gas":"84427","gasCost":"3","op":"PUSH1","pc":"74","stack":["0x60fe47b1","0x55","0x04","0x38","0x04"]},{"gas":"84424","gasCost":"3","op":"ADD","pc":"76","stack":["0x60fe47b1","0x55","0x04","0x38","0x04","0x20"]},{"gas":"84421","gasCost":"3","op":"SWAP1","pc":"77","stack":["0x60fe47b1","0x55","0x04","0x38","0x24"]},{"gas":"84418","gasCost":"3","op":"SWAP2","pc":"78","stack":["0x60fe47b1","0x55","0x04","0x24","0x38"]},{"gas":"84415","gasCost":"3","op":"SWAP1","pc":"79","stack":["0x60fe47b1","0x55","0x38","0x24","0x04"]},{"gas":"84412","gasCost":"2","op":"POP","pc":"80","stack":["0x60fe47b1","0x55","0x38","0x04","0x24"]},{"gas":"84410","gasCost":"2","op":"POP","pc":"81","stack":["0x60fe47b1","0x55","0x38","0x04"]},{"gas":"84408","gasCost":"3","op":"PUSH1","pc":"82","stack":["0x60fe47b1","0x55","0x38"]},{"gas":"84405","gasCost":"8","op":"JUMP","pc":"84","stack":["0x60fe47b1","0x55","0x38","0x89"]},{"gas":"84397","gasCost":"1","op":"JUMPDEST","pc":"137","stack":["0x60fe47b1","0x55","0x38"]},{"gas":"84396","gasCost":"3","op":"DUP1","pc":"138","stack":["0x60fe47b1","0x55","0x38"]},{"gas":"84393","gasCost":"3","op":"PUSH1","pc":"139","stack":["0x60fe47b1","0x55","0x38","0x38"]},{"gas":"84390","gasCost":"3","op":"PUSH1","pc":"141","stack":["0x60fe47b1","0x55","0x38","0x38","0x00"]},{"gas":"84387","gasCost":"2","op":"POP","pc":"143","stack":["0x60fe47b1","0x55","0x38","0x38","0x00","0x00"]},{"gas":"84385","gasCost":"3","op":"DUP2","pc":"144","stack":["0x60fe47b1","0x55","0x38","0x38","0x00"]},{"gas":"84382","gasCost":"3","op":"SWAP1","pc":"145","stack":["0x60fe47b1","0x55","0x38","0x38","0x00","0x38"]},{"gas":"84379","gasCost":"5000","op":"SSTORE","pc":"146","stack":["0x60fe47b1","0x55","0x38","0x38","0x38","0x00"]},{"gas":"79379","gasCost":"2","op":"POP","pc":"147","stack":["0x60fe47b1","0x55","0x38","0x38"]},{"gas":"79377","gasCost":"3","op":"PUSH1","pc":"148","stack":["0x60fe47b1","0x55","0x38"]},{"gas":"79374","gasCost":"3","op":"PUSH1","pc":"150","stack":["0x60fe47b1","0x55","0x38","0x2d"]},{"gas":"79371","gasCost":"3","op":"MLOAD","pc":"152","stack":["0x60fe47b1","0x55","0x38","0x2d","0x40"]},{"gas":"79368","gasCost":"3","memory":["0000000000000000000000000000000000000000000000000000000000000000","0000000000000000000000000000000000000000000000000000000000000000","0000000000000000000000000000000000000000000000000000000000000060"],"op":"PUSH1","pc":"153","stack":["0x60fe47b1","0x55","0x38","0x2d","0x60"]},{"gas":"79365","gasCost":"3","op":"DUP1","pc":"155","stack":["0x60fe47b1","0x55","0x38","0x2d","0x60","0x45"]},{"gas":"79362","gasCost":"3","op":"PUSH2","pc":"156","stack":["0x60fe47b1","0x55","0x38","0x2d","0x60","0x45","0x45"]},{"gas":"79359","gasCost":"3","op":"DUP4","pc":"159","stack":["0x60fe47b1","0x55","0x38","0x2d","0x60","0x45","0x45","0xf0"]},{"gas":"79356","gasCost":"21","memexpand":"3","op":"CODECOPY","pc":"160","stack":["0x60fe47b1","0x55","0x38","0x2d","0x60","0x45","0x45","0xf0","0x60"]},{"gas":"79335","gasCost":"3","memory":["0000000000000000000000000000000000000000000000000000000000000000","0000000000000000000000000000000000000000000000000000000000000000","0000000000000000000000000000000000000000000000000000000000000060","6060604052604051602080604583398101604052808051906020019091905050","5b806001016000600050819055505b50600a80603b6000396000f36060604052","6008565b00000000000000000000000000000000000000000000000000000000"],"op":"ADD","pc":"161","stack":["0x60fe47b1","0x55","0x38","0x2d","0x60","0x45"]},{"gas":"79332","gasCost":"3","op":"DUP1","pc":"162","stack":["0x60fe47b1","0x55","0x38","0x2d","0xa5"]},{"gas":"79329","gasCost":"3","op":"DUP3","pc":"163","stack":["0x60fe47b1","0x55","0x38","0x2d","0xa5","0xa5"]},{"gas":"79326","gasCost":"3","op":"DUP2","pc":"164","stack":["0x60fe47b1","0x55","0x38","0x2d","0xa5","0xa5","0x2d"]},{"gas":"79323","gasCost":"6","memexpand":"1","op":"MSTORE","pc":"165","stack":["0x60fe47b1","0x55","0x38","0x2d","0xa5","0xa5","0x2d","0xa5"]},{"gas":"79317","gasCost":"3","memory":["0000000000000000000000000000000000000000000000000000000000000000","0000000000000000000000000000000000000000000000000000000000000000","0000000000000000000000000000000000000000000000000000000000000060","6060604052604051602080604583398101604052808051906020019091905050","5b806001016000600050819055505b50600a80603b6000396000f36060604052","6008565b00000000000000000000000000000000000000000000000000000000","000000002d000000000000000000000000000000000000000000000000000000"],"op":"PUSH1","pc":"166","stack":["0x60fe47b1","0x55","0x38","0x2d","0xa5","0xa5"]},{"gas":"79314","gasCost":"3","op":"ADD","pc":"168","stack":["0x60fe47b1","0x55","0x38","0x2d","0xa5","0xa5","0x20"]},{"gas":"79311","gasCost":"3","op":"SWAP2","pc":"169","stack":["0x60fe47b1","0x55","0x38","0x2d","0xa5","0xc5"]},{"gas":"79308","gasCost":"2","op":"POP","pc":"170","stack":["0x60fe47b1","0x55","0x38","0xc5","0xa5","0x2d"]},{"gas":"79306","gasCost":"2","op":"POP","pc":"171","stack":["0x60fe47b1","0x55","0x38","0xc5","0xa5"]},{"gas":"79304","gasCost":"3","op":"PUSH1","pc":"172","stack":["0x60fe47b1","0x55","0x38","0xc5"]},{"gas":"79301","gasCost":"3","op":"MLOAD","pc":"174","stack":["0x60fe47b1","0x55","0x38","0xc5","0x40"]},{"gas":"79298","gasCost":"3","memory":["0000000000000000000000000000000000000000000000000000000000000000","0000000000000000000000000000000000000000000000000000000000000000","0000000000000000000000000000000000000000000000000000000000000060","6060604052604051602080604583398101604052808051906020019091905050","5b806001016000600050819055505b50600a80603b6000396000f36060604052","6008565b00000000000000000000000000000000000000000000000000000000","000000002d000000000000000000000000000000000000000000000000000000"],"op":"DUP1","pc":"175","stack":["0x60fe47b1","0x55","0x38","0xc5","0x60"]},{"gas":"79295","gasCost":"3","op":"SWAP2","pc":"176","stack":["0x60fe47b1","0x55","0x38","0xc5","0x60","0x60"]},{"gas":"79292","gasCost":"3","op":"SUB","pc":"177","stack":["0x60fe47b1","0x55","0x38","0x60","0x60","0xc5"]},{"gas":"79289","gasCost":"3","op":"SWAP1","pc":"178","stack":["0x60fe47b1","0x55","0x38","0x60","0x65"]},{"gas":"79286","gasCost":"3","op":"PUSH1","pc":"179","stack":["0x60fe47b1","0x55","0x38","0x65","0x60"]},{"gas":"79283","gasCost":"32000","op":"CREATE","pc":"181","stack":["0x60fe47b1","0x55","0x38","0x65","0x60","0x00"]},{"gas":"47283","gasCost":"3","memory":[],"op":"PUSH1","pc":"0","stack":[]},{"gas":"47280","gasCost":"3","op":"PUSH1","pc":"2","stack":["0x60"]},{"gas":"47277","gasCost":"12","memexpand":"3","op":"MSTORE","pc":"4","stack":["0x60","0x40"]},{"gas":"47265","gasCost":"3","memory":["0000000000000000000000000000000000000000000000000000000000000000","0000000000000000000000000000000000000000000000000000000000000000","0000000000000000000000000000000000000000000000000000000000000060"],"op":"PUSH1","pc":"5","stack":[]},{"gas":"47262","gasCost":"3","op":"MLOAD","pc":"7","stack":["0x40"]},{"gas":"47259","gasCost":"3","memory":["0000000000000000000000000000000000000000000000000000000000000000","0000000000000000000000000000000000000000000000000000000000000000","0000000000000000000000000000000000000000000000000000000000000060"],"op":"PUSH1","pc":"8","stack":["0x60"]},{"gas":"47256","gasCost":"3","op":"DUP1","pc":"10","stack":["0x60","0x20"]},{"gas":"47253","gasCost":"3","op":"PUSH1","pc":"11","stack":["0x60","0x20","0x20"]},{"gas":"47250","gasCost":"3","op":"DUP4","pc":"13","stack":["0x60","0x20","0x20","0x45"]},{"gas":"47247","gasCost":"9","memexpand":"1","op":"CODECOPY","pc":"14","stack":["0x60","0x20","0x20","0x45","0x60"]},{"gas":"47238","gasCost":"3","memory":["0000000000000000000000000000000000000000000000000000000000000000","0000000000000000000000000000000000000000000000000000000000000000","0000000000000000000000000000000000000000000000000000000000000060","000000000000000000000000000000000000000000000000000000000000002d"],"op":"DUP2","pc":"15","stack":["0x60","0x20"]},{"gas":"47235","gasCost":"3","op":"ADD","pc":"16","stack":["0x60","0x20","0x60"]},{"gas":"47232","gasCost":"3","op":"PUSH1","pc":"17","stack":["0x60","0x80"]},{"gas":"47229","gasCost":"3","op":"MSTORE","pc":"19","stack":["0x60","0x80","0x40"]},{"gas":"47226","gasCost":"3","memory":["0000000000000000000000000000000000000000000000000000000000000000","0000000000000000000000000000000000000000000000000000000000000000","0000000000000000000000000000000000000000000000000000000000000080","000000000000000000000000000000000000000000000000000000000000002d"],"op":"DUP1","pc":"20","stack":["0x60"]},{"gas":"47223","gasCost":"3","op":"DUP1","pc":"21","stack":["0x60","0x60"]},{"gas":"47220","gasCost":"3","op":"MLOAD","pc":"22","stack":["0x60","0x60","0x60"]},{"gas":"47217","gasCost":"3","memory":["0000000000000000000000000000000000000000000000000000000000000000","0000000000000000000000000000000000000000000000000000000000000000","0000000000000000000000000000000000000000000000000000000000000080","000000000000000000000000000000000000000000000000000000000000002d"],"op":"SWAP1","pc":"23","stack":["0x60","0x60","0x2d"]},{"gas":"47214","gasCost":"3","op":"PUSH1","pc":"24","stack":["0x60","0x2d","0x60"]},{"gas":"47211","gasCost":"3","op":"ADD","pc":"26","stack":["0x60","0x2d","0x60","0x20"]},{"gas":"47208","gasCost":"3","op":"SWAP1","pc":"27","stack":["0x60","0x2d","0x80"]},{"gas":"47205","gasCost":"3","op":"SWAP2","pc":"28","stack":["0x60","0x80","0x2d"]},{"gas":"47202","gasCost":"3","op":"SWAP1","pc":"29","stack":["0x2d","0x80","0x60"]},{"gas":"47199","gasCost":"2","op":"POP","pc":"30","stack":["0x2d","0x60","0x80"]},{"gas":"47197","gasCost":"2","op":"POP","pc":"31","stack":["0x2d","0x60"]},{"gas":"47195","gasCost":"1","op":"JUMPDEST","pc":"32","stack":["0x2d"]},{"gas":"47194","gasCost":"3","op":"DUP1","pc":"33","stack":["0x2d"]},{"gas":"47191","gasCost":"3","op":"PUSH1","pc":"34","stack":["0x2d","0x2d"]},{"gas":"47188","gasCost":"3","op":"ADD","pc":"36","stack":["0x2d","0x2d","0x01"]},{"gas":"47185","gasCost":"3","op":"PUSH1","pc":"37","stack":["0x2d","0x2e"]},{"gas":"47182","gasCost":"3","op":"PUSH1","pc":"39","stack":["0x2d","0x2e","0x00"]},{"gas":"47179","gasCost":"2","op":"POP","pc":"41","stack":["0x2d","0x2e","0x00","0x00"]},{"gas":"47177","gasCost":"3","op":"DUP2","pc":"42","stack":["0x2d","0x2e","0x00"]},{"gas":"47174","gasCost":"3","op":"SWAP1","pc":"43","stack":["0x2d","0x2e","0x00","0x2e"]},{"gas":"47171","gasCost":"20000","op":"SSTORE","pc":"44","stack":["0x2d","0x2e","0x2e","0x00"]},{"gas":"27171","gasCost":"2","op":"POP","pc":"45","stack":["0x2d","0x2e"]},{"gas":"27169","gasCost":"1","op":"JUMPDEST","pc":"46","stack":["0x2d"]},{"gas":"27168","gasCost":"2","op":"POP","pc":"47","stack":["0x2d"]},{"gas":"27166","gasCost":"3","op":"PUSH1","pc":"48","stack":[]},{"gas":"27163","gasCost":"3","op":"DUP1","pc":"50","stack":["0x0a"]},{"gas":"27160","gasCost":"3","op":"PUSH1","pc":"51","stack":["0x0a","0x0a"]},{"gas":"27157","gasCost":"3","op":"PUSH1","pc":"53","stack":["0x0a","0x0a","0x3b"]},{"gas":"27154","gasCost":"6","op":"CODECOPY","pc":"55","stack":["0x0a","0x0a","0x3b","0x00"]},{"gas":"27148","gasCost":"3","memory":["60606040526008565b0000000000000000000000000000000000000000000000","0000000000000000000000000000000000000000000000000000000000000000","0000000000000000000000000000000000000000000000000000000000000080","000000000000000000000000000000000000000000000000000000000000002d"],"op":"PUSH1","pc":"56","stack":["0x0a"]},{"gas":"27145","gasCost":"0","op":"RETURN","pc":"58","stack":["0x0a","0x00"]},{"gas":"25145","gasCost":"3","memory":["0000000000000000000000000000000000000000000000000000000000000000","0000000000000000000000000000000000000000000000000000000000000000","0000000000000000000000000000000000000000000000000000000000000060","6060604052604051602080604583398101604052808051906020019091905050","5b806001016000600050819055505b50600a80603b6000396000f36060604052","6008565b00000000000000000000000000000000000000000000000000000000","000000002d000000000000000000000000000000000000000000000000000000"],"op":"PUSH1","pc":"182","stack":["0x60fe47b1","0x55","0x38","0xd01f65e3472f24faf45f08f8698ec4da1bf32a95"]},{"gas":"25142","gasCost":"3","memory":["0000000000000000000000000000000000000000000000000000000000000000","0000000000000000000000000000000000000000000000000000000000000000","0000000000000000000000000000000000000000000000000000000000000060","6060604052604051602080604583398101604052808051906020019091905050","5b806001016000600050819055505b50600a80603b6000396000f36060604052","6008565b00000000000000000000000000000000000000000000000000000000","000000002d000000000000000000000000000000000000000000000000000000"],"op":"PUSH1","pc":"184","stack":["0x60fe47b1","0x55","0x38","0xd01f65e3472f24faf45f08f8698ec4da1bf32a95","0x01"]},{"gas":"25139","gasCost":"3","op":"PUSH2","pc":"186","stack":["0x60fe47b1","0x55","0x38","0xd01f65e3472f24faf45f08f8698ec4da1bf32a95","0x01","0x00"]},{"gas":"25136","gasCost":"10","op":"EXP","pc":"189","stack":["0x60fe47b1","0x55","0x38","0xd01f65e3472f24faf45f08f8698ec4da1bf32a95","0x01","0x00","0x0100"]},{"gas":"25126","gasCost":"3","op":"DUP2","pc":"190","stack":["0x60fe47b1","0x55","0x38","0xd01f65e3472f24faf45f08f8698ec4da1bf32a95","0x01","0x01"]},{"gas":"25123","gasCost":"50","op":"SLOAD","pc":"191","stack":["0x60fe47b1","0x55","0x38","0xd01f65e3472f24faf45f08f8698ec4da1bf32a95","0x01","0x01","0x01"]},{"gas":"25073","gasCost":"3","op":"DUP2","pc":"192","stack":["0x60fe47b1","0x55","0x38","0xd01f65e3472f24faf45f08f8698ec4da1bf32a95","0x01","0x01","0x00"]},{"gas":"25070","gasCost":"3","op":"PUSH20","pc":"193","stack":["0x60fe47b1","0x55","0x38","0xd01f65e3472f24faf45f08f8698ec4da1bf32a95","0x01","0x01","0x00","0x01"]},{"gas":"25067","gasCost":"5","op":"MUL","pc":"214","stack":["0x60fe47b1","0x55","0x38","0xd01f65e3472f24faf45f08f8698ec4da1bf32a95","0x01","0x01","0x00","0x01","0xffffffffffffffffffffffffffffffffffffffff"]},{"gas":"25062","gasCost":"3","op":"NOT","pc":"215","stack":["0x60fe47b1","0x55","0x38","0xd01f65e3472f24faf45f08f8698ec4da1bf32a95","0x01","0x01","0x00","0xffffffffffffffffffffffffffffffffffffffff"]},{"gas":"25059","gasCost":"3","op":"AND","pc":"216","stack":["0x60fe47b1","0x55","0x38","0xd01f65e3472f24faf45f08f8698ec4da1bf32a95","0x01","0x01","0x00","0xffffffffffffffffffffffff0000000000000000000000000000000000000000"]},{"gas":"25056","gasCost":"3","op":"SWAP1","pc":"217","stack":["0x60fe47b1","0x55","0x38","0xd01f65e3472f24faf45f08f8698ec4da1bf32a95","0x01","0x01","0x00"]},{"gas":"25053","gasCost":"3","op":"DUP4","pc":"218","stack":["0x60fe47b1","0x55","0x38","0xd01f65e3472f24faf45f08f8698ec4da1bf32a95","0x01","0x00","0x01"]},{"gas":"25050","gasCost":"5","op":"MUL","pc":"219","stack":["0x60fe47b1","0x55","0x38","0xd01f65e3472f24faf45f08f8698ec4da1bf32a95","0x01","0x00","0x01","0xd01f65e3472f24faf45f08f8698ec4da1bf32a95"]},{"gas":"25045","gasCost":"3","op":"OR","pc":"220","stack":["0x60fe47b1","0x55","0x38","0xd01f65e3472f24faf45f08f8698ec4da1bf32a95","0x01","0x00","0xd01f65e3472f24faf45f08f8698ec4da1bf32a95"]},{"gas":"25042","gasCost":"3","op":"SWAP1","pc":"221","stack":["0x60fe47b1","0x55","0x38","0xd01f65e3472f24faf45f08f8698ec4da1bf32a95","0x01","0xd01f65e3472f24faf45f08f8698ec4da1bf32a95"]},{"gas":"25039","gasCost":"20000","op":"SSTORE","pc":"222","stack":["0x60fe47b1","0x55","0x38","0xd01f65e3472f24faf45f08f8698ec4da1bf32a95","0xd01f65e3472f24faf45f08f8698ec4da1bf32a95","0x01"]},{"gas":"5039","gasCost":"2","op":"POP","pc":"223","stack":["0x60fe47b1","0x55","0x38","0xd01f65e3472f24faf45f08f8698ec4da1bf32a95"]},{"gas":"5037","gasCost":"3","op":"PUSH1","pc":"224","stack":["0x60fe47b1","0x55","0x38"]},{"gas":"5034","gasCost":"3","op":"DUP2","pc":"226","stack":["0x60fe47b1","0x55","0x38","0x22"]},{"gas":"5031","gasCost":"3","op":"ADD","pc":"227","stack":["0x60fe47b1","0x55","0x38","0x22","0x38"]},{"gas":"5028","gasCost":"3","op":"PUSH1","pc":"228","stack":["0x60fe47b1","0x55","0x38","0x5a"]},{"gas":"5025","gasCost":"3","op":"PUSH1","pc":"230","stack":["0x60fe47b1","0x55","0x38","0x5a","0x00"]},{"gas":"5022","gasCost":"2","op":"POP","pc":"232","stack":["0x60fe47b1","0x55","0x38","0x5a","0x00","0x00"]},{"gas":"5020","gasCost":"3","op":"DUP2","pc":"233","stack":["0x60fe47b1","0x55","0x38","0x5a","0x00"]},{"gas":"5017","gasCost":"3","op":"SWAP1","pc":"234","stack":["0x60fe47b1","0x55","0x38","0x5a","0x00","0x5a"]},{"gas":"5014","gasCost":"5000","op":"SSTORE","pc":"235","stack":["0x60fe47b1","0x55","0x38","0x5a","0x5a","0x00"]},{"gas":"14","gasCost":"2","op":"POP","pc":"236","stack":["0x60fe47b1","0x55","0x38","0x5a"]},{"gas":"12","gasCost":"1","op":"JUMPDEST","pc":"237","stack":["0x60fe47b1","0x55","0x38"]},{"gas":"11","gasCost":"2","op":"POP","pc":"238","stack":["0x60fe47b1","0x55","0x38"]},{"gas":"9","gasCost":"8","op":"JUMP","pc":"239","stack":["0x60fe47b1","0x55"]},{"gas":"1","gasCost":"1","op":"JUMPDEST","pc":"85","stack":["0x60fe47b1"]},{"gas":"0","gasCost":"0","op":"STOP","pc":"86","stack":["0x60fe47b1"]}]} + } +} diff --git a/src/app/debugger/remix-debugger/test-browser/test/init.js b/src/app/debugger/remix-debugger/test-browser/test/init.js new file mode 100644 index 0000000000..4078b00185 --- /dev/null +++ b/src/app/debugger/remix-debugger/test-browser/test/init.js @@ -0,0 +1,172 @@ +module.exports = function (browser, callback) { + extendBrowser(browser) + browser + .url('http://127.0.0.1:8080') + .injectScript('test-browser/resources/insertTestWeb3.js', function () { + // wait for the script to load test web3... + setTimeout(function () { + callback() + }, 5000) + }) +} + +function extendBrowser (browser) { + browser.multipleClick = function (id, time) { + for (var k = 0; k < time; k++) { + browser.click(id) + } + return browser + } + + browser.assertCurrentSelectedItem = function (expected) { + browser.execute(function (id) { + var node = document.querySelector('#asmcodes div div[selected="selected"] span') + return node.innerText + }, [''], function (returnValue) { + browser.assert.equal(returnValue.value, expected) + }) + return browser + } + + browser.retrieveInnerText = function (selector, callback) { + browser.execute(function (selector) { + var node = document.querySelector(selector) + return node ? node.innerText : '' + }, [selector], function (returnValue) { + callback(returnValue.value) + }) + return browser + } + + browser.assertStepDetail = function (vmtracestepinfo, stepinfo, addmemoryinfo, gasinfo, remaininggasinfo, loadedaddressinfo) { + assertPanel('#stepdetail', browser, ['vmtracestep:' + vmtracestepinfo, 'executionstep:' + stepinfo, 'addmemory:' + addmemoryinfo, 'gas:' + gasinfo, 'remaininggas:' + remaininggasinfo, 'loadedaddress:' + loadedaddressinfo]) + return browser + } + + browser.assertStack = function (value) { + return assertPanel('#stackpanel', browser, value) + } + + browser.assertStorageChanges = function (value) { + return assertPanel('#storagepanel', browser, value) + } + + browser.assertMemory = function (value) { + return assertPanel('#memorypanel', browser, value) + } + + browser.assertCallData = function (value) { + return assertPanel('#calldatapanel', browser, value) + } + + browser.assertCallStack = function (value) { + return assertPanel('#callstackpanel', browser, value) + } + + browser.assertStackValue = function (index, value) { + return assertPanelValue('#stackpanel', browser, index, value) + } + + browser.assertStorageChangesValue = function (index, value) { + return assertPanelValue('#storagepanel', browser, index, value) + } + + browser.assertMemoryValue = function (index, value) { + return assertPanelValue('#memorypanel', browser, index, value) + } + + browser.assertCallStackValue = function (index, value) { + return assertPanelValue('#callstackpanel', browser, index, value) + } + + browser.debugerKeyCode = { + 'Enter': 13, + 'Up': 38, + 'Down': 40, + 'Right': '39', + 'Left': 37, + 'Esc': 27, + 'SpaceBar': 32, + 'Ctrl': 17, + 'Alt': 18, + 'Shift': 16 + } + +/* browser.sendKeys is not working for safari */ +/* still not working properly +browser.fireEvent = function (el, key, times, callback) { + var data = { + 'id': el.substring(1), + 'key': key, + 'times': times + } + browser.execute(function (data) { + data = JSON.parse(data) + var el = document.getElementById(data.id) + var eventObj + console.log(el) + console.log(data) + var k = 0 + if (document.createEventObject) { + eventObj = document.createEventObject() + eventObj.keyCode = data.key + while (k < data.times) { + console.log('firing brfore createEventObject') + el.fireEvent('onkeypress', eventObj) + console.log('firing') + k++ + } + } else if (typeof (KeyboardEvent) === 'function') { + eventObj = new KeyboardEvent('keyup') + eventObj.key = data.key + eventObj.which = data.key + while (k < data.times) { + console.log('firing brfore createEvent') + el.dispatchEvent(eventObj) + console.log('firing') + k++ + } + } + }, [JSON.stringify(data)], function () { + callback() + }) +} +*/ +} + +function assertPanel (id, browser, value) { + var selector = '.dropdownpanel div.dropdowncontent ul' + browser.execute(function (id, selector) { + var el = document.getElementById(id.replace('#', '').replace('.', '')) + var node = el.querySelector(selector) + var ret = [] + for (var k = 0; k < node.children.length; k++) { + if (node.children[k].innerText) { + ret.push(node.children[k].innerText) + } + } + return ret + }, [id, selector], function (returnValues) { + value.map(function (item, index) { + if (returnValues.value.length) { + var testValue = returnValues.value[index].replace(/\r\n/g, '').replace(/\t/g, '').replace(/\s/g, '') + browser.assert.equal(testValue, value[index]) + } else { + browser.assert.equal(item, '') + } + }) + }) + return browser +} + +function assertPanelValue (id, browser, index, value) { + var selector = id + ' .dropdownpanel .dropdowncontent ul' + browser.execute(function (id, index) { + var node = document.querySelector(id) + return node.children[index].innerText + }, [selector, index], function (returnValues) { + var testValue = returnValues.value.replace(/\r\n/g, '').replace(/\t/g, '').replace(/\s/g, '') + browser.assert.equal(testValue, value) + }) + return browser +} diff --git a/src/app/debugger/remix-debugger/test-browser/test/sauce.js b/src/app/debugger/remix-debugger/test-browser/test/sauce.js new file mode 100644 index 0000000000..4a41fe5930 --- /dev/null +++ b/src/app/debugger/remix-debugger/test-browser/test/sauce.js @@ -0,0 +1,61 @@ +const https = require('https') + +module.exports = function sauce (callback) { + const currentTest = this.client.currentTest + const username = this.client.options.username + const sessionId = this.client.capabilities['webdriver.remote.sessionid'] + const accessKey = this.client.options.accessKey + + if (!this.client.launch_url.match(/saucelabs/)) { + console.log('Not saucelabs ...') + return callback() + } + + if (!username || !accessKey || !sessionId) { + console.log(this.client) + console.log('No username, accessKey or sessionId') + return callback() + } + + const passed = currentTest.results.passed === currentTest.results.tests + + const data = JSON.stringify({passed}) + + const requestPath = `/rest/v1/${username}/jobs/${sessionId}` + + function responseCallback (res) { + res.setEncoding('utf8') + console.log('Response: ', res.statusCode, JSON.stringify(res.headers)) + res.on('data', function onData (chunk) { + console.log('BODY: ' + chunk) + }) + res.on('end', function onEnd () { + console.info('Finished updating saucelabs') + callback() + }) + } + + try { + console.log('Updating saucelabs', requestPath) + + const req = https.request({ + hostname: 'saucelabs.com', + path: requestPath, + method: 'PUT', + auth: `${username}:${accessKey}`, + headers: { + 'Content-Type': 'application/json', + 'Content-Length': data.length + } + }, responseCallback) + + req.on('error', function onError (e) { + console.log('problem with request: ' + e.message) + }) + req.write(data) + req.end() + } catch (error) { + console.log('Error', error) + callback() + } +} diff --git a/src/app/debugger/remix-debugger/test-browser/test/vmdebugger.js b/src/app/debugger/remix-debugger/test-browser/test/vmdebugger.js new file mode 100644 index 0000000000..f84555a0f3 --- /dev/null +++ b/src/app/debugger/remix-debugger/test-browser/test/vmdebugger.js @@ -0,0 +1,192 @@ +'use strict' +var init = require('./init') +var sauce = require('./sauce') + +module.exports = { + beforeEach: function (browser, done) { + try { + init(browser, function () { + done() + }) + } catch (e) { + var mes = 'error ' + e.message + console.log(mes) + done(mes) + } + }, + + 'vmdebugger': function (browser) { + loadTraceNotFound(browser) + .click('#unload') + loadTrace(browser) + .click('#unload') + panels(browser) + .click('#unload') + slider(browser) + .click('#unload') + stepping(browser) + .click('#unload') + stepdetail(browser) + .end() + }, + + tearDown: sauce +} + +function loadTraceNotFound (browser) { + browser + .clearValue('#txinput') + .setValue('#txinput', '0x20ef65b8b186ca942zcccd634f37074dde49b541c27994fc7596740ef44cfd51') + .click('#load') + .click('#txinfo .title') + .execute(function () { + return document.querySelector('#txinfo .dropdownpanel .dropdownrawcontent').innerHTML + }, [], function (result) { + console.log(result.value) + if (result.value.indexOf('not found') === -1) { + browser.assert.fail(' txinput panel does not contain ', 'info about error', '') + } + }) + return browser +} + +function loadTrace (browser) { + browser + .clearValue('#txinput') + .setValue('#txinput', '0x20ef65b8b186ca942fcccd634f37074dde49b541c27994fc7596740ef44cfd51') + .click('#load') + .click('#txinfo .title') + .execute(function () { + return document.querySelector('#txinfo .dropdownpanel .dropdownrawcontent').innerHTML + }, [], function (result) { + if (result.value.indexOf('0x20ef65b8b186ca942fcccd634f37074dde49b541c27994fc7596740ef44cfd51') === -1) { + browser.assert.fail(' txinput panel does not contain 0x20ef65b8b186ca942fcccd634f37074dde49b541c27994fc7596740ef44cfd51 ', 'info about error', '') + } + }) + .click('#unload') + .waitForElementNotVisible('#vmdebugger', 1000) + return browser +} + +function panels (browser) { + browser + .clearValue('#txinput') + .setValue('#txinput', '0x20ef65b8b186ca942fcccd634f37074dde49b541c27994fc7596740ef44cfd51') + .click('#load') + .multipleClick('#intoforward', 63) + .assertStack(['0:0x', '1:0x60', '2:0x65', '3:0x38', '4:0x55', '5:0x60fe47b1']) + .assertStorageChanges(['0x290decd9548b62a8d60345a988386fc84ba6bc95484008f6362f93160ef3e563:Objectkey:0x00value:0x38']) + .assertCallData(['0:0x60fe47b10000000000000000000000000000000000000000000000000000000000000038']) + .assertCallStack(['0:0x0d3a18d64dfe4f927832ab58d6451cecc4e517c5']) + .assertStackValue(1, '1:0x60') + .assertMemoryValue(6, '0x60:60606040526040516020806045833981????R??Q????E?9?') + .assertMemoryValue(7, '0x70:01604052808051906020019091905050???R??Q???????PP') + .assertMemoryValue(8, '0x80:5b806001016000600050819055505b50?????????P??UP?P') + .click('#intoforward') // CREATE + .assertStack(['']) + .assertStorageChanges([]) + .assertMemory(['']) + .assertCallData(['0:0x0000000000000000000000000000000000000000000000000000000000000000000000000000006060606040526040516020806045833981016040528080519060200190919050505b806001016000600050819055']) + .assertCallStack(['0:0x0d3a18d64dfe4f927832ab58d6451cecc4e517c5', '1:(ContractCreation-Step63)']) + return browser +} + +function slider (browser) { + browser + .clearValue('#txinput') + .setValue('#txinput', '0x20ef65b8b186ca942fcccd634f37074dde49b541c27994fc7596740ef44cfd51') + .click('#load') + .click('#intoforward') + .click('#intoforward') + .click('#intoforward') + .click('#intoforward') + .click('#intoforward') + .click('#intoforward') + .click('#intoforward') + .click('#intoforward') + .click('#intoforward') + /* + .sendKeys('#slider', browser.Keys.RIGHT_ARROW) + .sendKeys('#slider', browser.Keys.RIGHT_ARROW) + .sendKeys('#slider', browser.Keys.RIGHT_ARROW) + .sendKeys('#slider', browser.Keys.RIGHT_ARROW) + .sendKeys('#slider', browser.Keys.RIGHT_ARROW) + .sendKeys('#slider', browser.Keys.RIGHT_ARROW) + .sendKeys('#slider', browser.Keys.RIGHT_ARROW) + .sendKeys('#slider', browser.Keys.RIGHT_ARROW) + .sendKeys('#slider', browser.Keys.LEFT_ARROW) + */ + .assertCurrentSelectedItem('041 PUSH4 60fe47b1') + return browser +} + +function stepping (browser) { + browser + .clearValue('#txinput') + .setValue('#txinput', '0x20ef65b8b186ca942fcccd634f37074dde49b541c27994fc7596740ef44cfd51') + .click('#load') + .click('#intoforward') + .click('#intoforward') + .assertCurrentSelectedItem('004 MSTORE') + .click('#intoforward') + .click('#intoback') + .click('#intoback') + .assertCurrentSelectedItem('002 PUSH1 40') + .multipleClick('#intoforward', 62) + .assertCurrentSelectedItem('181 CREATE') + .click('#intoforward') + .click('#intoforward') + .click('#intoforward') + .click('#intoforward') + .click('#overforward') + .assertCurrentSelectedItem('007 MLOAD') + .click('#intoback') + .click('#intoback') + .click('#intoback') + .click('#intoback') + .click('#intoback') + .click('#overforward') + .assertCurrentSelectedItem('182 PUSH1 01') + .click('#overforward') + .assertCurrentSelectedItem('184 PUSH1 00') + .click('#intoback') + .click('#intoback') + .click('#overback') + .assertCurrentSelectedItem('181 CREATE') + return browser +} + +function stepdetail (browser) { + browser + .clearValue('#txinput') + .setValue('#txinput', '0x20ef65b8b186ca942fcccd634f37074dde49b541c27994fc7596740ef44cfd51') + .click('#load') + .click('#intoforward') + .click('#intoforward') + .click('#intoforward') + .click('#intoforward') + .click('#intoforward') + .click('#intoforward') + /* + .fireEvent('#slider', browser.debugerKeyCode.Right, 4, function () { + browser.assertSticker('6', '6', '', '3', '84476', '0x0d3a18d64dfe4f927832ab58d6451cecc4e517c5') + .click('#nextcall') + .assertSticker('63', '63', '', '32000', '79283', '0x0d3a18d64dfe4f927832ab58d6451cecc4e517c5') + .click('#intoforward') + .click('#overforward') + .assertSticker('108', '44', '', '0', '27145', '(Contract Creation - Step 63)') + .click('#intoforward') + .assertSticker('109', '64', '', '3', '25145', '0x0d3a18d64dfe4f927832ab58d6451cecc4e517c5') + .end() + }) + */ + .assertStepDetail('6', '6', '', '3', '84476', '0x0d3a18d64dfe4f927832ab58d6451cecc4e517c5') + .multipleClick('#intoforward', 57) + .assertStepDetail('63', '63', '', '32000', '79283', '0x0d3a18d64dfe4f927832ab58d6451cecc4e517c5') + .click('#overforward') + .click('#intoback') + .assertStepDetail('108', '44', '', '0', '27145', '(ContractCreation-Step63)') + .click('#intoforward') + .assertStepDetail('109', '64', '', '3', '25145', '0x0d3a18d64dfe4f927832ab58d6451cecc4e517c5') + return browser +}