├── .circleci ├── config.yml └── fetch-deps.sh ├── .gitignore ├── .solhint.json ├── LICENSE ├── README.md ├── bin ├── commands │ ├── cmd.spec.js │ ├── cmd.spec.ts │ ├── cmd_compile.js │ ├── cmd_compile.ts │ ├── cmd_interactive.js │ ├── cmd_interactive.ts │ ├── cmd_perf.js │ ├── cmd_perf.ts │ ├── cmd_solstl.js │ ├── cmd_solstl.ts │ ├── cmd_tests.js │ ├── cmd_tests.ts │ ├── command.js │ ├── command.ts │ ├── commands.js │ ├── commands.ts │ ├── option.js │ ├── option.spec.js │ ├── option.spec.ts │ └── option.ts ├── main.js ├── main.ts └── prompt │ ├── compile_prompt.js │ ├── compile_prompt.ts │ ├── logs_prompt.js │ ├── logs_prompt.ts │ ├── main_prompt.js │ ├── main_prompt.ts │ ├── perf_prompt.js │ ├── perf_prompt.ts │ ├── tests_prompt.js │ ├── tests_prompt.ts │ ├── utils.js │ └── utils.ts ├── data ├── data.json └── packages │ ├── bits.json │ ├── bytes.json │ ├── math.json │ ├── patricia_tree.json │ ├── strings.json │ ├── tokens.json │ └── unsafe.json ├── docs ├── _img │ ├── key_hash_segments.png │ ├── label_edge_node.png │ └── root_node.png ├── bits │ └── Bits.md ├── bytes │ └── Bytes.md ├── cli.md ├── math │ └── ExactMath.md ├── patricia_tree │ ├── Data.md │ └── PatriciaTree.md ├── perf.md ├── strings │ └── Strings.md ├── testing.md ├── tokens │ └── ERC20TokenFace.md └── unsafe │ └── Memory.md ├── examples ├── bits │ └── BitsExamples.sol ├── bytes │ └── BytesExamples.sol ├── math │ └── ExactMathExamples.sol ├── patricia_tree │ └── PatriciaTreeExamples.sol └── strings │ └── StringsExamples.sol ├── package-lock.json ├── package.json ├── perf ├── STLPerf.sol ├── bits │ └── bits.sol ├── bytes │ └── bytes.sol ├── math │ └── exact_math.sol ├── patricia_tree │ ├── data.sol │ ├── patricia_tree.sol │ └── patricia_tree_extended.sol ├── strings │ └── strings.sol └── unsafe │ └── memory.sol ├── script ├── compile.js ├── compile.ts ├── constants.js ├── constants.ts ├── exec │ ├── evm.js │ ├── evm.ts │ ├── solc.js │ └── solc.ts ├── perf.js ├── perf.ts ├── tests.js ├── tests.ts └── utils │ ├── data_reader.js │ ├── data_reader.ts │ ├── io.js │ ├── io.ts │ ├── logger.js │ ├── logger.ts │ ├── logs.js │ ├── logs.ts │ ├── test_logger.js │ └── test_logger.ts ├── src ├── bits │ └── Bits.sol ├── bytes │ └── Bytes.sol ├── math │ └── ExactMath.sol ├── patricia_tree │ ├── Data.sol │ ├── PatriciaTree.sol │ └── PatriciaTreeFace.sol ├── strings │ └── Strings.sol ├── tokens │ └── ERC20TokenFace.sol └── unsafe │ └── Memory.sol ├── test ├── STLTest.sol ├── bits │ └── bits.sol ├── bytes │ └── bytes.sol ├── math │ ├── exact_math.sol │ └── math_consistency.sol ├── patricia_tree │ ├── data.sol │ └── patricia_tree.sol ├── strings │ └── strings.sol └── unsafe │ └── memory.sol ├── tsconfig.json └── tslint.json /.circleci/config.yml: -------------------------------------------------------------------------------- 1 | version: 2 2 | jobs: 3 | build: 4 | machine: true 5 | working_directory: ~/solidity-examples 6 | steps: 7 | - checkout 8 | - run: 9 | name: fetch-deps 10 | command: 'sudo chmod +x .circleci/fetch-deps.sh && ./.circleci/fetch-deps.sh' 11 | - restore_cache: 12 | key: dependency-cache-{{ checksum "package.json" }} 13 | - run: 14 | name: install-npm-wee 15 | command: npm install 16 | - save_cache: 17 | key: dependency-cache-{{ checksum "package.json" }} 18 | paths: 19 | - ./node_modules 20 | - run: 21 | name: test 22 | command: npm run contracts-test 23 | - store_artifacts: 24 | path: test-results.xml 25 | prefix: tests 26 | - store_test_results: 27 | path: test-results.xml -------------------------------------------------------------------------------- /.circleci/fetch-deps.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | sudo apt-get -y install software-properties-common 4 | sudo add-apt-repository -y ppa:ethereum/ethereum 5 | sudo add-apt-repository -y ppa:ethereum/ethereum-dev 6 | sudo apt-get -y update 7 | sudo apt-get install -y curl 8 | sudo apt-get install -y dpkg 9 | curl -sL https://deb.nodesource.com/setup_8.x | sudo -E bash 10 | sudo apt-get install -y nodejs evm solc 11 | -------------------------------------------------------------------------------- /.solhint.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "default", 3 | "rules": { 4 | "no-complex-fallback": "warn", 5 | "indent": [ 6 | "error", 7 | 4 8 | ], 9 | "quotes": [ 10 | "error", 11 | "double" 12 | ], 13 | "max-line-length": [ 14 | "error", 15 | 140 16 | ], 17 | "no-inline-assembly": false, 18 | "compiler-fixed": false 19 | } 20 | } -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2016 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Standard library (draft) [![CircleCI](https://circleci.com/gh/ethereum/solidity-examples.svg?style=svg)](https://circleci.com/gh/ethereum/solidity-examples) 2 | 3 | Temporary storage for a standard library of Solidity contracts. 4 | 5 | The code in this library will only run in metropolis compliant networks, and it will only compile using Solidity version `0.5.0` and later. 6 | 7 | ## TOC 8 | 9 | - [Purpose](#purpose) 10 | - [Packages](#packages) 11 | - [bits](#bits) 12 | - [bytes](#bytes) 13 | - [math](#math) 14 | - [patricia_tree](#patricia_tree) 15 | - [strings](#strings) 16 | - [token](#token) 17 | - [unsafe](#unsafe) 18 | - [Quality Assurance](#quality-assurance) 19 | - [Commandline tool](#commandline-tool) 20 | - [Q&A](qa) 21 | 22 | ## Purpose 23 | 24 | The standard library should provide Solidity libraries / functions for performing common tasks, such as working with strings, bits, and other common types, and for working with Ethereum and Ethereum related technologies, like for example Patricia Tries. 25 | 26 | ## Packages 27 | 28 | These are the different packages. 29 | 30 | ### bits 31 | 32 | The `bits` package is used to work with bitfields (uints). 33 | 34 | #### contracts 35 | 36 | - [Bits](./docs/bits/Bits.md) 37 | 38 | ### bytes 39 | 40 | The `bytes` package is used to work with `bytes` in memory, and to convert between `bytes` and other types. 41 | 42 | #### contracts 43 | 44 | - [Bytes](./docs/bytes/Bytes.md) 45 | 46 | ### math 47 | 48 | The `math` package is used to do math with signed and unsigned integers. 49 | 50 | #### contracts 51 | 52 | - [ExactMath](./docs/math/ExactMath.md) 53 | 54 | ### patricia_tree 55 | 56 | The `patricia_tree` package is used to work with the standard Solidity Patricia tree implementation by @chriseth. 57 | 58 | #### contracts 59 | 60 | - [PatriciaTree](./docs/patricia_tree/PatriciaTree.md) 61 | - [Data](./docs/patricia_tree/Data.md) 62 | 63 | ### strings 64 | 65 | The `strings` package is used to work Solidity `string`s. It only supports UTF-8 validation. 66 | 67 | #### contracts 68 | 69 | - [Strings](./docs/strings/Strings.md) 70 | 71 | ### tokens 72 | 73 | The `tokens` package is used to work with Token contracts. 74 | 75 | #### contracts 76 | 77 | - [ERC20TokenFace](./docs/tokens/ERC20TokenFace.md) 78 | 79 | ### unsafe 80 | 81 | The `unsafe` package is used to do unsafe operations, like working with memory directly. 82 | 83 | #### contracts 84 | 85 | - [Memory](./docs/unsafe/Memory.md) 86 | 87 | ## Quality Assurance 88 | 89 | The standard library has well-documented routines for making sure that code meets the required standards when it comes to: 90 | 91 | 1. Security 92 | 2. Performance 93 | 3. Style 94 | 4. Documentation 95 | 96 | Additionally, the tools used to guarantee this should be simple and easy to replace when new and better alternatives are made available. 97 | 98 | ### Testing 99 | 100 | `npm test` - runs the contract test-suite, as well as the tests for the typescript code. 101 | 102 | `npm run ts-test` - runs the typescript tests. 103 | 104 | `npm run contracts-test` - runs the contract test-suite. 105 | 106 | The contract tests requires `solc` and `evm` ([go ethereum](https://github.com/ethereum/go-ethereum)) to be installed and added to the $PATH. Test code is written in Solidity, and is executed directly in the evm. 107 | 108 | For more information about the tests, such as the test file format, read the full [test documentation](./docs/testing.md). 109 | 110 | For running tests with the command-line tool, check the [CLI documentation](./docs/cli.md). 111 | 112 | ### Performance 113 | 114 | `npm run contracts-perf` will run the entire perf suite. 115 | 116 | For more information about perf read the full [perf documentation](./docs/perf.md). 117 | 118 | For running perf with the command-line tool, check the [CLI documentation](./docs/cli.md). 119 | 120 | ### Style 121 | 122 | `npm run ts-lint` - will run TS-lint on the entire library. 123 | 124 | `npm run contracts-lint` - will run [solhint](https://github.com/protofire/solhint) on all contracts. 125 | 126 | The standard library should serve as an (or perhaps *the*) example of strict, idiomatic Solidity. This means all code should follow the style guide and the practices and patterns laid out at https://solidity.readthedocs.org. 127 | 128 | ### Documentation 129 | 130 | Briefly: the documentation should specify - in a very clear and concise a way - what the contract of the library/function is, the interfaces / function signatures should reflect that, and the functions should (obviously) behave as described. 131 | 132 | ### Manual review 133 | 134 | Code should be reviewed by at least one person other then the writer. There should also be review of tests, perf, docs, and code examples as well. 135 | 136 | This should be done using the PR system in github. 137 | 138 | ### Issues and feedback 139 | 140 | Github issues and gitter solidity channel. 141 | 142 | ### Ranking / categorization of contracts 143 | 144 | The QA only really applies to code that is meant to be used in production, but the library will also include code that has not reached that level. 145 | 146 | Node.js has a system of categorizing libraries, experimental, stable, deprecated, and so forth. This library should have something similar. 147 | 148 | ## Commandline tool 149 | 150 | The library has a commandline tool which can be used to run tests, view documentation, and other things. The full docs can be found in the [commandline tool documentation](./docs/cli.md). -------------------------------------------------------------------------------- /bin/commands/cmd.spec.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | Object.defineProperty(exports, "__esModule", { value: true }); 3 | var commands_1 = require("./commands"); 4 | var option_1 = require("./option"); 5 | describe('commands, sanity check for command tree', function () { 6 | it('checks that commands are properly linked', function () { 7 | for (var cmdName in commands_1.COMMANDS) { 8 | if (commands_1.COMMANDS.hasOwnProperty(cmdName)) { 9 | var cmd = commands_1.COMMANDS[cmdName]; 10 | expect(cmd.name()).toBe(cmdName); 11 | expect(cmd.description()).not.toBe(''); 12 | if (cmdName === 'solstl') { 13 | expect(cmd.parent()).toBe(''); 14 | } 15 | else { 16 | expect(commands_1.COMMANDS[cmd.parent()]).not.toBeUndefined(); 17 | } 18 | for (var _i = 0, _a = cmd.subcommands(); _i < _a.length; _i++) { 19 | var subCmd = _a[_i]; 20 | expect(commands_1.COMMANDS[subCmd]).not.toBeUndefined(); 21 | } 22 | for (var _b = 0, _c = cmd.validOptions(); _b < _c.length; _b++) { 23 | var optn = _c[_b]; 24 | expect(option_1.OPTIONS[optn]).not.toBeUndefined(); 25 | } 26 | } 27 | } 28 | }); 29 | }); 30 | -------------------------------------------------------------------------------- /bin/commands/cmd.spec.ts: -------------------------------------------------------------------------------- 1 | import {COMMANDS} from "./commands"; 2 | import {OPTIONS} from "./option"; 3 | 4 | describe('commands, sanity check for command tree', () => { 5 | it('checks that commands are properly linked', () => { 6 | for (const cmdName in COMMANDS) { 7 | if (COMMANDS.hasOwnProperty(cmdName)) { 8 | const cmd = COMMANDS[cmdName]; 9 | expect(cmd.name()).toBe(cmdName); 10 | expect(cmd.description()).not.toBe(''); 11 | if (cmdName === 'solstl') { 12 | expect(cmd.parent()).toBe(''); 13 | } else { 14 | expect(COMMANDS[cmd.parent()]).not.toBeUndefined(); 15 | } 16 | for (const subCmd of cmd.subcommands()) { 17 | expect(COMMANDS[subCmd]).not.toBeUndefined(); 18 | } 19 | for (const optn of cmd.validOptions()) { 20 | expect(OPTIONS[optn]).not.toBeUndefined(); 21 | } 22 | } 23 | } 24 | }); 25 | }); 26 | -------------------------------------------------------------------------------- /bin/commands/cmd_compile.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | var __extends = (this && this.__extends) || (function () { 3 | var extendStatics = Object.setPrototypeOf || 4 | ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) || 5 | function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; }; 6 | return function (d, b) { 7 | extendStatics(d, b); 8 | function __() { this.constructor = d; } 9 | d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); 10 | }; 11 | })(); 12 | var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { 13 | return new (P || (P = Promise))(function (resolve, reject) { 14 | function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } 15 | function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } 16 | function step(result) { result.done ? resolve(result.value) : new P(function (resolve) { resolve(result.value); }).then(fulfilled, rejected); } 17 | step((generator = generator.apply(thisArg, _arguments || [])).next()); 18 | }); 19 | }; 20 | var __generator = (this && this.__generator) || function (thisArg, body) { 21 | var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g; 22 | return g = { next: verb(0), "throw": verb(1), "return": verb(2) }, typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g; 23 | function verb(n) { return function (v) { return step([n, v]); }; } 24 | function step(op) { 25 | if (f) throw new TypeError("Generator is already executing."); 26 | while (_) try { 27 | if (f = 1, y && (t = y[op[0] & 2 ? "return" : op[0] ? "throw" : "next"]) && !(t = t.call(y, op[1])).done) return t; 28 | if (y = 0, t) op = [0, t.value]; 29 | switch (op[0]) { 30 | case 0: case 1: t = op; break; 31 | case 4: _.label++; return { value: op[1], done: false }; 32 | case 5: _.label++; y = op[1]; op = [0]; continue; 33 | case 7: op = _.ops.pop(); _.trys.pop(); continue; 34 | default: 35 | if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; } 36 | if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; } 37 | if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; } 38 | if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; } 39 | if (t[2]) _.ops.pop(); 40 | _.trys.pop(); continue; 41 | } 42 | op = body.call(thisArg, _); 43 | } catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; } 44 | if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true }; 45 | } 46 | }; 47 | Object.defineProperty(exports, "__esModule", { value: true }); 48 | var command_1 = require("./command"); 49 | var compile_1 = require("../../script/compile"); 50 | var CompileCommand = /** @class */ (function (_super) { 51 | __extends(CompileCommand, _super); 52 | function CompileCommand() { 53 | return _super !== null && _super.apply(this, arguments) || this; 54 | } 55 | CompileCommand.prototype.execute = function (args, options) { 56 | return __awaiter(this, void 0, void 0, function () { 57 | return __generator(this, function (_a) { 58 | switch (_a.label) { 59 | case 0: 60 | if (!this.checkOptions(options)) { 61 | this.printHelp(); 62 | return [2 /*return*/]; 63 | } 64 | if (args.length !== 0) { 65 | this.printHelp(); 66 | return [2 /*return*/]; 67 | } 68 | return [4 /*yield*/, compile_1.compileAll()]; 69 | case 1: 70 | _a.sent(); 71 | return [2 /*return*/]; 72 | } 73 | }); 74 | }); 75 | }; 76 | CompileCommand.prototype.name = function () { 77 | return 'compile'; 78 | }; 79 | CompileCommand.prototype.description = function () { 80 | return 'Compile all contracts.'; 81 | }; 82 | CompileCommand.prototype.validOptions = function () { 83 | return []; 84 | }; 85 | CompileCommand.prototype.parent = function () { 86 | return 'solstl'; 87 | }; 88 | CompileCommand.prototype.subcommands = function () { 89 | return []; 90 | }; 91 | CompileCommand.prototype.arguments = function () { 92 | return []; 93 | }; 94 | return CompileCommand; 95 | }(command_1.Command)); 96 | exports.CompileCommand = CompileCommand; 97 | -------------------------------------------------------------------------------- /bin/commands/cmd_compile.ts: -------------------------------------------------------------------------------- 1 | import {Command} from "./command"; 2 | import {compileAll} from "../../script/compile"; 3 | 4 | export class CompileCommand extends Command { 5 | 6 | public async execute(args: string[], options: string[]) { 7 | if (!this.checkOptions(options)) { 8 | this.printHelp(); 9 | return; 10 | } 11 | if (args.length !== 0) { 12 | this.printHelp(); 13 | return; 14 | } 15 | await compileAll(); 16 | } 17 | 18 | public name(): string { 19 | return 'compile'; 20 | } 21 | 22 | public description(): string { 23 | return 'Compile all contracts.'; 24 | } 25 | 26 | public validOptions(): string[] { 27 | return []; 28 | } 29 | 30 | public parent(): string { 31 | return 'solstl'; 32 | } 33 | 34 | public subcommands(): string[] { 35 | return []; 36 | } 37 | 38 | public arguments(): string[] { 39 | return []; 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /bin/commands/cmd_interactive.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | var __extends = (this && this.__extends) || (function () { 3 | var extendStatics = Object.setPrototypeOf || 4 | ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) || 5 | function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; }; 6 | return function (d, b) { 7 | extendStatics(d, b); 8 | function __() { this.constructor = d; } 9 | d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); 10 | }; 11 | })(); 12 | var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { 13 | return new (P || (P = Promise))(function (resolve, reject) { 14 | function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } 15 | function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } 16 | function step(result) { result.done ? resolve(result.value) : new P(function (resolve) { resolve(result.value); }).then(fulfilled, rejected); } 17 | step((generator = generator.apply(thisArg, _arguments || [])).next()); 18 | }); 19 | }; 20 | var __generator = (this && this.__generator) || function (thisArg, body) { 21 | var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g; 22 | return g = { next: verb(0), "throw": verb(1), "return": verb(2) }, typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g; 23 | function verb(n) { return function (v) { return step([n, v]); }; } 24 | function step(op) { 25 | if (f) throw new TypeError("Generator is already executing."); 26 | while (_) try { 27 | if (f = 1, y && (t = y[op[0] & 2 ? "return" : op[0] ? "throw" : "next"]) && !(t = t.call(y, op[1])).done) return t; 28 | if (y = 0, t) op = [0, t.value]; 29 | switch (op[0]) { 30 | case 0: case 1: t = op; break; 31 | case 4: _.label++; return { value: op[1], done: false }; 32 | case 5: _.label++; y = op[1]; op = [0]; continue; 33 | case 7: op = _.ops.pop(); _.trys.pop(); continue; 34 | default: 35 | if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; } 36 | if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; } 37 | if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; } 38 | if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; } 39 | if (t[2]) _.ops.pop(); 40 | _.trys.pop(); continue; 41 | } 42 | op = body.call(thisArg, _); 43 | } catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; } 44 | if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true }; 45 | } 46 | }; 47 | Object.defineProperty(exports, "__esModule", { value: true }); 48 | var command_1 = require("./command"); 49 | var main_prompt_1 = require("../prompt/main_prompt"); 50 | var InteractiveCommand = /** @class */ (function (_super) { 51 | __extends(InteractiveCommand, _super); 52 | function InteractiveCommand() { 53 | return _super !== null && _super.apply(this, arguments) || this; 54 | } 55 | InteractiveCommand.prototype.execute = function (args, options) { 56 | return __awaiter(this, void 0, void 0, function () { 57 | var terminate; 58 | return __generator(this, function (_a) { 59 | switch (_a.label) { 60 | case 0: 61 | if (!this.checkOptions(options)) { 62 | this.printHelp(); 63 | return [2 /*return*/]; 64 | } 65 | if (args.length !== 0) { 66 | this.printHelp(); 67 | return [2 /*return*/]; 68 | } 69 | terminate = false; 70 | _a.label = 1; 71 | case 1: 72 | if (!!terminate) return [3 /*break*/, 3]; 73 | return [4 /*yield*/, main_prompt_1.mainMenu()]; 74 | case 2: 75 | terminate = _a.sent(); 76 | return [3 /*break*/, 1]; 77 | case 3: return [2 /*return*/]; 78 | } 79 | }); 80 | }); 81 | }; 82 | InteractiveCommand.prototype.name = function () { 83 | return 'interactive'; 84 | }; 85 | InteractiveCommand.prototype.description = function () { 86 | return 'Start an interactive session.'; 87 | }; 88 | InteractiveCommand.prototype.validOptions = function () { 89 | return []; 90 | }; 91 | InteractiveCommand.prototype.parent = function () { 92 | return 'solstl'; 93 | }; 94 | InteractiveCommand.prototype.subcommands = function () { 95 | return []; 96 | }; 97 | InteractiveCommand.prototype.arguments = function () { 98 | return []; 99 | }; 100 | return InteractiveCommand; 101 | }(command_1.Command)); 102 | exports.InteractiveCommand = InteractiveCommand; 103 | -------------------------------------------------------------------------------- /bin/commands/cmd_interactive.ts: -------------------------------------------------------------------------------- 1 | import {Command} from "./command"; 2 | import {mainMenu} from "../prompt/main_prompt"; 3 | 4 | export class InteractiveCommand extends Command { 5 | 6 | public async execute(args: string[], options: string[]) { 7 | if (!this.checkOptions(options)) { 8 | this.printHelp(); 9 | return; 10 | } 11 | if (args.length !== 0) { 12 | this.printHelp(); 13 | return; 14 | } 15 | // Stops linter from complaining. 16 | let terminate = false; 17 | while (!terminate) { 18 | terminate = await mainMenu(); 19 | } 20 | } 21 | 22 | public name(): string { 23 | return 'interactive'; 24 | } 25 | 26 | public description(): string { 27 | return 'Start an interactive session.'; 28 | } 29 | 30 | public validOptions(): string[] { 31 | return []; 32 | } 33 | 34 | public parent(): string { 35 | return 'solstl'; 36 | } 37 | 38 | public subcommands(): string[] { 39 | return []; 40 | } 41 | 42 | public arguments(): string[] { 43 | return []; 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /bin/commands/cmd_perf.ts: -------------------------------------------------------------------------------- 1 | import {Command} from "./command"; 2 | import {perf} from "../../script/perf"; 3 | import {latestPerfLog} from "../../script/utils/io"; 4 | import {printLatestDiff, printPerfLog} from "../../script/utils/logs"; 5 | import Logger from "../../script/utils/logger"; 6 | import {getAllPerfFiles} from "../../script/utils/data_reader"; 7 | 8 | export class PerfCommand extends Command { 9 | 10 | public async execute(args: string[], options: string[]) { 11 | if (!this.checkOptions(options)) { 12 | this.printHelp(); 13 | return; 14 | } 15 | if (args.length !== 0) { 16 | this.printHelp(); 17 | return; 18 | } 19 | let optAndUnopt = false; 20 | let extended = false; 21 | let silent = false; 22 | let diff = false; 23 | for (const opt of options) { 24 | switch (opt) { 25 | case 'optAndUnopt': 26 | optAndUnopt = true; 27 | break; 28 | case 'extended': 29 | extended = true; 30 | break; 31 | case 'silent': 32 | silent = true; 33 | break; 34 | case 'diff': 35 | diff = true; 36 | break; 37 | } 38 | } 39 | const units = getAllPerfFiles(extended); 40 | await perf(units, optAndUnopt); 41 | if (!silent) { 42 | printPerfLog(latestPerfLog()); 43 | } 44 | if (diff) { 45 | if (!printLatestDiff()) { 46 | Logger.info("No previous perf logs exist."); 47 | } 48 | } 49 | } 50 | 51 | public name(): string { 52 | return 'perf'; 53 | } 54 | 55 | public description(): string { 56 | return 'Run the perf suite.'; 57 | } 58 | 59 | public validOptions(): string[] { 60 | return ['optAndUnopt', 'extended', 'silent', 'diff']; 61 | } 62 | 63 | public parent(): string { 64 | return 'solstl'; 65 | } 66 | 67 | public subcommands(): string[] { 68 | return []; 69 | } 70 | 71 | public arguments(): string[] { 72 | return []; 73 | } 74 | } 75 | -------------------------------------------------------------------------------- /bin/commands/cmd_solstl.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | var __extends = (this && this.__extends) || (function () { 3 | var extendStatics = Object.setPrototypeOf || 4 | ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) || 5 | function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; }; 6 | return function (d, b) { 7 | extendStatics(d, b); 8 | function __() { this.constructor = d; } 9 | d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); 10 | }; 11 | })(); 12 | var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { 13 | return new (P || (P = Promise))(function (resolve, reject) { 14 | function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } 15 | function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } 16 | function step(result) { result.done ? resolve(result.value) : new P(function (resolve) { resolve(result.value); }).then(fulfilled, rejected); } 17 | step((generator = generator.apply(thisArg, _arguments || [])).next()); 18 | }); 19 | }; 20 | var __generator = (this && this.__generator) || function (thisArg, body) { 21 | var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g; 22 | return g = { next: verb(0), "throw": verb(1), "return": verb(2) }, typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g; 23 | function verb(n) { return function (v) { return step([n, v]); }; } 24 | function step(op) { 25 | if (f) throw new TypeError("Generator is already executing."); 26 | while (_) try { 27 | if (f = 1, y && (t = y[op[0] & 2 ? "return" : op[0] ? "throw" : "next"]) && !(t = t.call(y, op[1])).done) return t; 28 | if (y = 0, t) op = [0, t.value]; 29 | switch (op[0]) { 30 | case 0: case 1: t = op; break; 31 | case 4: _.label++; return { value: op[1], done: false }; 32 | case 5: _.label++; y = op[1]; op = [0]; continue; 33 | case 7: op = _.ops.pop(); _.trys.pop(); continue; 34 | default: 35 | if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; } 36 | if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; } 37 | if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; } 38 | if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; } 39 | if (t[2]) _.ops.pop(); 40 | _.trys.pop(); continue; 41 | } 42 | op = body.call(thisArg, _); 43 | } catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; } 44 | if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true }; 45 | } 46 | }; 47 | Object.defineProperty(exports, "__esModule", { value: true }); 48 | var command_1 = require("./command"); 49 | var fs = require("fs"); 50 | var path = require("path"); 51 | var packageFile = fs.readFileSync(path.join(__dirname, '..', '..', 'package.json')).toString(); 52 | var packageObj = JSON.parse(packageFile); 53 | var version = packageObj.version; 54 | var SolStlCommand = /** @class */ (function (_super) { 55 | __extends(SolStlCommand, _super); 56 | function SolStlCommand() { 57 | return _super !== null && _super.apply(this, arguments) || this; 58 | } 59 | SolStlCommand.prototype.execute = function (args, options) { 60 | return __awaiter(this, void 0, void 0, function () { 61 | return __generator(this, function (_a) { 62 | if (!this.checkOptions(options)) { 63 | this.printHelp(); 64 | return [2 /*return*/]; 65 | } 66 | if (args.length !== 0) { 67 | this.printHelp(); 68 | return [2 /*return*/]; 69 | } 70 | if (options[0] === 'version') { 71 | console.log('Solidity Standard Library, version: ' + version); 72 | } 73 | return [2 /*return*/]; 74 | }); 75 | }); 76 | }; 77 | SolStlCommand.prototype.name = function () { 78 | return 'solstl'; 79 | }; 80 | SolStlCommand.prototype.description = function () { 81 | return 'Root command.'; 82 | }; 83 | SolStlCommand.prototype.validOptions = function () { 84 | return ['version']; 85 | }; 86 | SolStlCommand.prototype.parent = function () { 87 | return ''; 88 | }; 89 | SolStlCommand.prototype.subcommands = function () { 90 | return ['perf', 'tests', 'interactive', 'compile']; 91 | }; 92 | SolStlCommand.prototype.arguments = function () { 93 | return []; 94 | }; 95 | return SolStlCommand; 96 | }(command_1.Command)); 97 | exports.SolStlCommand = SolStlCommand; 98 | -------------------------------------------------------------------------------- /bin/commands/cmd_solstl.ts: -------------------------------------------------------------------------------- 1 | import {Command} from "./command"; 2 | import * as fs from 'fs'; 3 | import * as path from 'path'; 4 | 5 | const packageFile = fs.readFileSync(path.join(__dirname, '..', '..', 'package.json')).toString(); 6 | const packageObj = JSON.parse(packageFile); 7 | const version = packageObj.version; 8 | 9 | export class SolStlCommand extends Command { 10 | 11 | public async execute(args: string[], options: string[]) { 12 | if (!this.checkOptions(options)) { 13 | this.printHelp(); 14 | return; 15 | } 16 | 17 | if (args.length !== 0) { 18 | this.printHelp(); 19 | return; 20 | } 21 | if (options[0] === 'version') { 22 | console.log('Solidity Standard Library, version: ' + version); 23 | } 24 | } 25 | 26 | public name(): string { 27 | return 'solstl'; 28 | } 29 | 30 | public description(): string { 31 | return 'Root command.'; 32 | } 33 | 34 | public validOptions(): string[] { 35 | return ['version']; 36 | } 37 | 38 | public parent(): string { 39 | return ''; 40 | } 41 | 42 | public subcommands(): string[] { 43 | return ['perf', 'tests', 'interactive', 'compile']; 44 | } 45 | 46 | public arguments(): string[] { 47 | return []; 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /bin/commands/cmd_tests.ts: -------------------------------------------------------------------------------- 1 | import {Command} from "./command"; 2 | import {test} from "../../script/tests"; 3 | import {printTestLog} from "../../script/utils/logs"; 4 | import {latestTestLog} from "../../script/utils/io"; 5 | import {getAllTestFiles} from "../../script/utils/data_reader"; 6 | 7 | export class TestsCommand extends Command { 8 | 9 | public async execute(args: string[], options: string[]) { 10 | if (!this.checkOptions(options)) { 11 | this.printHelp(); 12 | return; 13 | } 14 | if (args.length !== 0) { 15 | this.printHelp(); 16 | return; 17 | } 18 | let optAndUnopt = false; 19 | let extended = false; 20 | let silent = false; 21 | for (const opt of options) { 22 | switch (opt) { 23 | case 'optAndUnopt': 24 | optAndUnopt = true; 25 | break; 26 | case 'extended': 27 | extended = true; 28 | break; 29 | case 'silent': 30 | silent = true; 31 | } 32 | } 33 | const units = getAllTestFiles(extended); 34 | const result = await test(units, optAndUnopt); 35 | if (!silent) { 36 | printTestLog(latestTestLog()); 37 | } 38 | if (!result) { 39 | throw new Error(`At least one test failed.`); 40 | } 41 | } 42 | 43 | public name(): string { 44 | return 'tests'; 45 | } 46 | 47 | public description(): string { 48 | return 'Run the test suite.'; 49 | } 50 | 51 | public validOptions(): string[] { 52 | return ['optAndUnopt', 'extended', 'silent']; 53 | } 54 | 55 | public parent(): string { 56 | return 'solstl'; 57 | } 58 | 59 | public subcommands(): string[] { 60 | return []; 61 | } 62 | 63 | public arguments(): string[] { 64 | return []; 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /bin/commands/command.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | Object.defineProperty(exports, "__esModule", { value: true }); 3 | var chalk = require("chalk"); 4 | var option_1 = require("./option"); 5 | var commands_1 = require("./commands"); 6 | var io_1 = require("../../script/utils/io"); 7 | var Command = /** @class */ (function () { 8 | function Command() { 9 | } 10 | Command.prototype.checkOptions = function (options) { 11 | // Check for the help flag first. 12 | for (var _i = 0, options_1 = options; _i < options_1.length; _i++) { 13 | var opt = options_1[_i]; 14 | if (opt === 'help') { 15 | return false; 16 | } 17 | } 18 | for (var _a = 0, options_2 = options; _a < options_2.length; _a++) { 19 | var opt = options_2[_a]; 20 | if (this.validOptions().indexOf(opt) < 0) { 21 | return false; 22 | } 23 | } 24 | return true; 25 | }; 26 | Command.prototype.printHelp = function () { 27 | var command = this.name(); 28 | var cmdStr = command; 29 | while (command !== 'solstl') { 30 | command = commands_1.COMMANDS[this.parent()].name(); 31 | cmdStr = this.parent() + ' ' + cmdStr; 32 | } 33 | var subcommands = this.subcommands(); 34 | if (subcommands.length !== 0) { 35 | cmdStr += " [SUBCOMMAND] [ARG]..."; 36 | } 37 | else { 38 | var args = this.arguments().join(' ').trim(); 39 | if (args !== '') { 40 | cmdStr += " " + args; 41 | } 42 | } 43 | var opts = this.validOptions(); 44 | var help = '\n'; 45 | help += chalk['bold']['white']('Command:') + " " + chalk['magentaBright'](this.name()) + "\n\n"; 46 | help += "\t" + this.description() + "\n\n"; 47 | help += chalk['bold']['white']('Usage:') + " " + cmdStr + " [OPTION]...\n\n"; 48 | if (subcommands.length > 0) { 49 | help += chalk['bold']['white']('Subcommands:') + "\n\n"; 50 | for (var _i = 0, subcommands_1 = subcommands; _i < subcommands_1.length; _i++) { 51 | var subcommand = subcommands_1[_i]; 52 | var scObj = commands_1.COMMANDS[subcommand]; 53 | var scStr = (subcommand + " ").substr(0, 30) + scObj.description(); 54 | help += "\t" + scStr + "\n"; 55 | } 56 | help += '\n'; 57 | } 58 | if (opts.length > 0) { 59 | help += chalk['bold']['white']('Options:') + "\n\n"; 60 | for (var _a = 0, opts_1 = opts; _a < opts_1.length; _a++) { 61 | var opt = opts_1[_a]; 62 | var optObj = option_1.OPTIONS[opt]; 63 | var objStr = ("--" + optObj.name() + ", -" + optObj.shortForm() + " ").substr(0, 30) + optObj.info(); 64 | help += "\t" + objStr + "\n"; 65 | } 66 | var helpStr = "--help, -H ".substr(0, 30) + option_1.OPTIONS['help'].info(); 67 | help += "\t" + helpStr + "\n"; 68 | help += '\n'; 69 | } 70 | help += chalk['bold']['white']('Global Options:') + "\n\n"; 71 | for (var opt in option_1.GLOBAL_OPTIONS) { 72 | if (option_1.GLOBAL_OPTIONS.hasOwnProperty(opt)) { 73 | var optObj = option_1.GLOBAL_OPTIONS[opt]; 74 | var objStr = ("--" + optObj.name() + ", -" + optObj.shortForm() + " ").substr(0, 30) + optObj.info(); 75 | help += "\t" + objStr + "\n"; 76 | } 77 | } 78 | io_1.println(help); 79 | }; 80 | return Command; 81 | }()); 82 | exports.Command = Command; 83 | -------------------------------------------------------------------------------- /bin/commands/command.ts: -------------------------------------------------------------------------------- 1 | import * as chalk from 'chalk'; 2 | import {GLOBAL_OPTIONS, OPTIONS} from "./option"; 3 | import {COMMANDS} from "./commands"; 4 | import {println} from "../../script/utils/io"; 5 | 6 | export abstract class Command { 7 | 8 | public abstract async execute(args: string[], options: string[]); 9 | 10 | public abstract name(): string; 11 | 12 | public abstract description(): string; 13 | 14 | public abstract validOptions(): string[]; 15 | 16 | public abstract parent(): string; 17 | 18 | public abstract subcommands(): string[]; 19 | 20 | public abstract arguments(): string[]; 21 | 22 | protected checkOptions(options: string[]) { 23 | // Check for the help flag first. 24 | for (const opt of options) { 25 | if (opt === 'help') { 26 | return false; 27 | } 28 | } 29 | for (const opt of options) { 30 | if (this.validOptions().indexOf(opt) < 0) { 31 | return false; 32 | } 33 | } 34 | return true; 35 | } 36 | 37 | public printHelp() { 38 | let command = this.name(); 39 | let cmdStr = command; 40 | while (command !== 'solstl') { 41 | command = COMMANDS[this.parent()].name(); 42 | cmdStr = this.parent() + ' ' + cmdStr; 43 | } 44 | const subcommands = this.subcommands(); 45 | if (subcommands.length !== 0) { 46 | cmdStr += ` [SUBCOMMAND] [ARG]...`; 47 | } else { 48 | const args = this.arguments().join(' ').trim(); 49 | if (args !== '') { 50 | cmdStr += ` ${args}`; 51 | } 52 | } 53 | const opts = this.validOptions(); 54 | 55 | let help = '\n'; 56 | help += `${chalk['bold']['white']('Command:')} ${chalk['magentaBright'](this.name())}\n\n`; 57 | help += `\t${this.description()}\n\n`; 58 | help += `${chalk['bold']['white']('Usage:')} ${cmdStr} [OPTION]...\n\n`; 59 | if (subcommands.length > 0) { 60 | help += `${chalk['bold']['white']('Subcommands:')}\n\n`; 61 | for (const subcommand of subcommands) { 62 | const scObj = COMMANDS[subcommand]; 63 | const scStr = `${subcommand} `.substr(0, 30) + scObj.description(); 64 | help += `\t${scStr}\n`; 65 | } 66 | help += '\n'; 67 | } 68 | if (opts.length > 0) { 69 | help += `${chalk['bold']['white']('Options:')}\n\n`; 70 | for (const opt of opts) { 71 | const optObj = OPTIONS[opt]; 72 | const objStr = `--${optObj.name()}, -${optObj.shortForm()} `.substr(0, 30) + optObj.info(); 73 | help += `\t${objStr}\n`; 74 | } 75 | const helpStr = `--help, -H `.substr(0, 30) + OPTIONS['help'].info(); 76 | help += `\t${helpStr}\n`; 77 | help += '\n'; 78 | } 79 | help += `${chalk['bold']['white']('Global Options:')}\n\n`; 80 | for (const opt in GLOBAL_OPTIONS) { 81 | if (GLOBAL_OPTIONS.hasOwnProperty(opt)) { 82 | const optObj = GLOBAL_OPTIONS[opt]; 83 | const objStr = `--${optObj.name()}, -${optObj.shortForm()} `.substr(0, 30) + optObj.info(); 84 | help += `\t${objStr}\n`; 85 | } 86 | } 87 | println(help); 88 | } 89 | } 90 | -------------------------------------------------------------------------------- /bin/commands/commands.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | Object.defineProperty(exports, "__esModule", { value: true }); 3 | var cmd_solstl_1 = require("./cmd_solstl"); 4 | var cmd_perf_1 = require("./cmd_perf"); 5 | var cmd_tests_1 = require("./cmd_tests"); 6 | var cmd_interactive_1 = require("./cmd_interactive"); 7 | var cmd_compile_1 = require("./cmd_compile"); 8 | exports.COMMANDS = { 9 | solstl: new cmd_solstl_1.SolStlCommand(), 10 | perf: new cmd_perf_1.PerfCommand(), 11 | tests: new cmd_tests_1.TestsCommand(), 12 | interactive: new cmd_interactive_1.InteractiveCommand(), 13 | compile: new cmd_compile_1.CompileCommand() 14 | }; 15 | -------------------------------------------------------------------------------- /bin/commands/commands.ts: -------------------------------------------------------------------------------- 1 | import {Command} from "./command"; 2 | import {SolStlCommand} from "./cmd_solstl"; 3 | import {PerfCommand} from "./cmd_perf"; 4 | import {TestsCommand} from "./cmd_tests"; 5 | import {InteractiveCommand} from "./cmd_interactive"; 6 | import {CompileCommand} from "./cmd_compile"; 7 | 8 | export const COMMANDS: { [name: string]: Command } = { 9 | solstl: new SolStlCommand(), 10 | perf: new PerfCommand(), 11 | tests: new TestsCommand(), 12 | interactive: new InteractiveCommand(), 13 | compile: new CompileCommand() 14 | }; 15 | -------------------------------------------------------------------------------- /bin/commands/option.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | Object.defineProperty(exports, "__esModule", { value: true }); 3 | var Option = /** @class */ (function () { 4 | function Option(name, shortForm, info) { 5 | this.__name = name; 6 | this.__shortForm = shortForm; 7 | this.__info = info; 8 | } 9 | Option.prototype.name = function () { 10 | return this.__name; 11 | }; 12 | Option.prototype.shortForm = function () { 13 | return this.__shortForm; 14 | }; 15 | Option.prototype.info = function () { 16 | return this.__info; 17 | }; 18 | return Option; 19 | }()); 20 | exports.Option = Option; 21 | exports.OPTIONS = { 22 | help: new Option('help', 'H', 'Display the help-text for the given command.'), 23 | version: new Option('version', 'V', 'Show the current version.'), 24 | optAndUnopt: new Option('optAndUnopt', 'O', 'Run the suit both with optimized and un-opttimized code.'), 25 | extended: new Option('extended', 'E', 'Include the extended tests/performance units.'), 26 | silent: new Option('silent', 'S', 'Do not show a detailed results from tests or perf.'), 27 | diff: new Option('diff', 'I', 'Compare perf results with the results from the previous perf (if any).') 28 | }; 29 | exports.GLOBAL_OPTIONS = { 30 | debug: new Option('debug', 'D', 'Enable debug logging.') 31 | }; 32 | -------------------------------------------------------------------------------- /bin/commands/option.spec.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | Object.defineProperty(exports, "__esModule", { value: true }); 3 | var option_1 = require("./option"); 4 | describe('sanity check for options', function () { 5 | it('checks that options are well formed', function () { 6 | var shortFormsFound = {}; 7 | var check = function (optsObj) { 8 | for (var optName in optsObj) { 9 | if (optsObj.hasOwnProperty(optName)) { 10 | var opt = optsObj[optName]; 11 | expect(opt.name()).toBe(optName); 12 | expect(opt.info()).not.toBe(''); 13 | var shortForm = opt.shortForm(); 14 | expect(shortFormsFound[shortForm]).toBeUndefined(); 15 | shortFormsFound[shortForm] = true; 16 | } 17 | } 18 | }; 19 | check(option_1.OPTIONS); 20 | check(option_1.GLOBAL_OPTIONS); 21 | }); 22 | }); 23 | -------------------------------------------------------------------------------- /bin/commands/option.spec.ts: -------------------------------------------------------------------------------- 1 | import {GLOBAL_OPTIONS, OPTIONS} from "./option"; 2 | 3 | describe('sanity check for options', () => { 4 | it('checks that options are well formed', () => { 5 | const shortFormsFound = {}; 6 | 7 | const check = (optsObj) => { 8 | for (const optName in optsObj) { 9 | if (optsObj.hasOwnProperty(optName)) { 10 | const opt = optsObj[optName]; 11 | expect(opt.name()).toBe(optName); 12 | expect(opt.info()).not.toBe(''); 13 | const shortForm = opt.shortForm(); 14 | expect(shortFormsFound[shortForm]).toBeUndefined(); 15 | shortFormsFound[shortForm] = true; 16 | } 17 | } 18 | }; 19 | 20 | check(OPTIONS); 21 | check(GLOBAL_OPTIONS); 22 | }); 23 | }); 24 | -------------------------------------------------------------------------------- /bin/commands/option.ts: -------------------------------------------------------------------------------- 1 | export class Option { 2 | 3 | private __name: string; 4 | private __shortForm: string; 5 | private __info: string; 6 | 7 | public constructor(name: string, shortForm: string, info: string) { 8 | this.__name = name; 9 | this.__shortForm = shortForm; 10 | this.__info = info; 11 | } 12 | 13 | public name(): string { 14 | return this.__name; 15 | } 16 | 17 | public shortForm(): string { 18 | return this.__shortForm; 19 | } 20 | 21 | public info(): string { 22 | return this.__info; 23 | } 24 | } 25 | 26 | export const OPTIONS: { [name: string]: Option } = { 27 | help: new Option('help', 'H', 'Display the help-text for the given command.'), 28 | version: new Option('version', 'V', 'Show the current version.'), 29 | optAndUnopt: new Option('optAndUnopt', 'O', 'Run the suit both with optimized and un-opttimized code.'), 30 | extended: new Option('extended', 'E', 'Include the extended tests/performance units.'), 31 | silent: new Option('silent', 'S', 'Do not show a detailed results from tests or perf.'), 32 | diff: new Option('diff', 'I', 'Compare perf results with the results from the previous perf (if any).') 33 | }; 34 | 35 | export const GLOBAL_OPTIONS: { [name: string]: Option } = { 36 | debug: new Option('debug', 'D', 'Enable debug logging.') 37 | }; 38 | -------------------------------------------------------------------------------- /bin/main.ts: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | 3 | import {GLOBAL_OPTIONS, OPTIONS} from "./commands/option"; 4 | import {COMMANDS} from "./commands/commands"; 5 | import Logger, {Level} from "../script/utils/logger"; 6 | 7 | const handleGlobalOptions = (opts: string[]) => { 8 | for (const opt of opts) { 9 | switch (opt) { 10 | case 'debug': 11 | Logger.setLevel(Level.Debug); 12 | break; 13 | default: 14 | // This should not happen or the parsing must have failed. 15 | throw new Error(`Internal error: global option not found: ${opt}`); 16 | } 17 | } 18 | }; 19 | 20 | const printHelp = () => { 21 | COMMANDS['solstl'].printHelp(); 22 | }; 23 | 24 | const parseGlobalOption = (opt: string): string => GLOBAL_OPTIONS[opt] ? opt : ''; 25 | 26 | const parseOption = (opt: string): string => OPTIONS[opt] ? opt : ''; 27 | 28 | const parseShortFormGlobalOption = (sfo: string): string => { 29 | let opt = ''; 30 | for (const o in GLOBAL_OPTIONS) { 31 | if (GLOBAL_OPTIONS[o].shortForm() === sfo) { 32 | opt = o; 33 | break; 34 | } 35 | } 36 | return opt; 37 | }; 38 | 39 | const parseShortFormOption = (sfo: string): string => { 40 | let opt = ''; 41 | for (const o in OPTIONS) { 42 | if (OPTIONS[o].shortForm() === sfo) { 43 | opt = o; 44 | break; 45 | } 46 | } 47 | return opt; 48 | }; 49 | 50 | export const run = async (input: string[]) => { 51 | 52 | const optsfnd = {}; 53 | const args = []; 54 | const globalOptions = []; 55 | const options = []; 56 | 57 | if (input.length === 0) { 58 | COMMANDS['solstl'].printHelp(); 59 | return; 60 | } 61 | 62 | // Keep processing the first argument in the array. 63 | for (const ipt of input) { 64 | if (ipt.substr(0, 2) === '--') { 65 | // This is a long form option. 66 | const opt = ipt.substr(2); 67 | if (opt.length === 0) { 68 | throw new Error(`Empty flags not allowed.`); 69 | } 70 | const gOpt = parseGlobalOption(opt); 71 | if (gOpt !== '') { 72 | if (optsfnd[gOpt]) { 73 | throw new Error(`Multiple instances of flag: --${opt}`); 74 | } 75 | globalOptions.push(gOpt); 76 | optsfnd[gOpt] = true; 77 | } else { 78 | const lOpt = parseOption(opt); 79 | if (lOpt === '') { 80 | printHelp(); 81 | throw new Error(`Unknown flag: --${opt}`); 82 | } 83 | if (optsfnd[lOpt]) { 84 | throw new Error(`Multiple instances of flag: --${opt}`); 85 | } 86 | options.push(lOpt); 87 | optsfnd[lOpt] = true; 88 | } 89 | } else if (ipt[0] === '-') { 90 | // This is a short-form option. 91 | if (ipt.length === 1) { 92 | throw new Error(`Empty flags not allowed.`); 93 | } 94 | if (ipt.length > 2) { 95 | throw new Error(`Short form options are single letter only: ${ipt}`); 96 | } 97 | const opt = ipt[1]; 98 | const gOpt = parseShortFormGlobalOption(opt); 99 | if (gOpt !== '') { 100 | if (optsfnd[gOpt]) { 101 | throw new Error(`Option found more then once: ${opt}`); 102 | } 103 | globalOptions.push(gOpt); 104 | optsfnd[gOpt] = true; 105 | } else { 106 | const lOpt = parseShortFormOption(opt); 107 | if (lOpt === '') { 108 | printHelp(); 109 | throw new Error(`Unknown flag: ${ipt}`); 110 | } 111 | if (optsfnd[lOpt]) { 112 | throw new Error(`Option found more then once: ${opt}`); 113 | } 114 | options.push(lOpt); 115 | optsfnd[lOpt] = true; 116 | } 117 | } else { 118 | // This is a command. 119 | args.push(ipt); 120 | } 121 | } 122 | 123 | // Handle all the global options (such as setting the log level). 124 | handleGlobalOptions(globalOptions); 125 | 126 | if (args.length === 0) { 127 | // No arguments 128 | await COMMANDS['solstl'].execute([], options); 129 | } else { 130 | const cmdName = args[0]; 131 | const cmd = COMMANDS[cmdName]; 132 | if (!cmd) { 133 | throw new Error(`Unknown command: ${cmdName}`); 134 | } 135 | await cmd.execute(args.slice(1), options); 136 | } 137 | }; 138 | 139 | (async () => { 140 | try { 141 | await run(process.argv.slice(2)); 142 | } catch (error) { 143 | Logger.error(error.message); 144 | if (Logger.level() === 'debug') { 145 | Logger.debug(error.stack); 146 | } 147 | process.exit(error); 148 | } 149 | })(); 150 | -------------------------------------------------------------------------------- /bin/prompt/compile_prompt.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { 3 | return new (P || (P = Promise))(function (resolve, reject) { 4 | function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } 5 | function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } 6 | function step(result) { result.done ? resolve(result.value) : new P(function (resolve) { resolve(result.value); }).then(fulfilled, rejected); } 7 | step((generator = generator.apply(thisArg, _arguments || [])).next()); 8 | }); 9 | }; 10 | var __generator = (this && this.__generator) || function (thisArg, body) { 11 | var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g; 12 | return g = { next: verb(0), "throw": verb(1), "return": verb(2) }, typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g; 13 | function verb(n) { return function (v) { return step([n, v]); }; } 14 | function step(op) { 15 | if (f) throw new TypeError("Generator is already executing."); 16 | while (_) try { 17 | if (f = 1, y && (t = y[op[0] & 2 ? "return" : op[0] ? "throw" : "next"]) && !(t = t.call(y, op[1])).done) return t; 18 | if (y = 0, t) op = [0, t.value]; 19 | switch (op[0]) { 20 | case 0: case 1: t = op; break; 21 | case 4: _.label++; return { value: op[1], done: false }; 22 | case 5: _.label++; y = op[1]; op = [0]; continue; 23 | case 7: op = _.ops.pop(); _.trys.pop(); continue; 24 | default: 25 | if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; } 26 | if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; } 27 | if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; } 28 | if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; } 29 | if (t[2]) _.ops.pop(); 30 | _.trys.pop(); continue; 31 | } 32 | op = body.call(thisArg, _); 33 | } catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; } 34 | if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true }; 35 | } 36 | }; 37 | var _this = this; 38 | Object.defineProperty(exports, "__esModule", { value: true }); 39 | var utils_1 = require("./utils"); 40 | var compile_1 = require("../../script/compile"); 41 | var data_reader_1 = require("../../script/utils/data_reader"); 42 | exports.contractSelectionData = function () { 43 | var choices = data_reader_1.getAllContractFiles().map(function (file) { 44 | return { 45 | name: file[0] + "/" + file[1], 46 | value: file 47 | }; 48 | }); 49 | return { 50 | type: 'checkbox', 51 | message: 'Select contracts (select none and press to go back)', 52 | name: "compile", 53 | choices: choices 54 | }; 55 | }; 56 | exports.compileMenu = function () { return __awaiter(_this, void 0, void 0, function () { 57 | var selected; 58 | return __generator(this, function (_a) { 59 | switch (_a.label) { 60 | case 0: return [4 /*yield*/, utils_1.prompt(exports.contractSelectionData())]; 61 | case 1: 62 | selected = _a.sent(); 63 | if (selected.compile.length === 0) { 64 | return [2 /*return*/]; 65 | } 66 | return [4 /*yield*/, compile_1.compile(selected.compile)]; 67 | case 2: 68 | _a.sent(); 69 | return [2 /*return*/]; 70 | } 71 | }); 72 | }); }; 73 | -------------------------------------------------------------------------------- /bin/prompt/compile_prompt.ts: -------------------------------------------------------------------------------- 1 | import {prompt} from "./utils"; 2 | import {compile} from "../../script/compile"; 3 | import {getAllContractFiles} from "../../script/utils/data_reader"; 4 | 5 | export const contractSelectionData = () => { 6 | const choices = getAllContractFiles().map((file) => { 7 | return { 8 | name: `${file[0]}/${file[1]}`, 9 | value: file 10 | }; 11 | }); 12 | return { 13 | type: 'checkbox', 14 | message: 'Select contracts (select none and press to go back)', 15 | name: "compile", 16 | choices 17 | }; 18 | }; 19 | 20 | export const compileMenu = async (): Promise => { 21 | const selected = await prompt(contractSelectionData()); 22 | if (selected.compile.length === 0) { 23 | return; 24 | } 25 | await compile(selected.compile); 26 | }; 27 | -------------------------------------------------------------------------------- /bin/prompt/logs_prompt.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { 3 | return new (P || (P = Promise))(function (resolve, reject) { 4 | function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } 5 | function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } 6 | function step(result) { result.done ? resolve(result.value) : new P(function (resolve) { resolve(result.value); }).then(fulfilled, rejected); } 7 | step((generator = generator.apply(thisArg, _arguments || [])).next()); 8 | }); 9 | }; 10 | var __generator = (this && this.__generator) || function (thisArg, body) { 11 | var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g; 12 | return g = { next: verb(0), "throw": verb(1), "return": verb(2) }, typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g; 13 | function verb(n) { return function (v) { return step([n, v]); }; } 14 | function step(op) { 15 | if (f) throw new TypeError("Generator is already executing."); 16 | while (_) try { 17 | if (f = 1, y && (t = y[op[0] & 2 ? "return" : op[0] ? "throw" : "next"]) && !(t = t.call(y, op[1])).done) return t; 18 | if (y = 0, t) op = [0, t.value]; 19 | switch (op[0]) { 20 | case 0: case 1: t = op; break; 21 | case 4: _.label++; return { value: op[1], done: false }; 22 | case 5: _.label++; y = op[1]; op = [0]; continue; 23 | case 7: op = _.ops.pop(); _.trys.pop(); continue; 24 | default: 25 | if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; } 26 | if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; } 27 | if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; } 28 | if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; } 29 | if (t[2]) _.ops.pop(); 30 | _.trys.pop(); continue; 31 | } 32 | op = body.call(thisArg, _); 33 | } catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; } 34 | if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true }; 35 | } 36 | }; 37 | var _this = this; 38 | Object.defineProperty(exports, "__esModule", { value: true }); 39 | var utils_1 = require("./utils"); 40 | var logs_1 = require("../../script/utils/logs"); 41 | var io_1 = require("../../script/utils/io"); 42 | var logger_1 = require("../../script/utils/logger"); 43 | exports.logsPrompt = { 44 | type: 'list', 45 | name: 'logs', 46 | message: 'Select an action to perform', 47 | choices: [ 48 | { 49 | key: 't', 50 | name: 'View latest test log', 51 | value: 'latest_test' 52 | }, 53 | { 54 | key: 'p', 55 | name: 'View latest perf log', 56 | value: 'latest_perf' 57 | }, 58 | { 59 | key: 'p', 60 | name: 'View latest perf diff', 61 | value: 'latest_perf_diff' 62 | } 63 | ].concat(utils_1.SEPARATOR).concat(utils_1.NAV_CHOICES) 64 | }; 65 | exports.logsMenu = function () { return __awaiter(_this, void 0, void 0, function () { 66 | var selected; 67 | return __generator(this, function (_a) { 68 | switch (_a.label) { 69 | case 0: return [4 /*yield*/, utils_1.prompt(exports.logsPrompt)]; 70 | case 1: 71 | selected = _a.sent(); 72 | switch (selected.logs) { 73 | case "latest_test": 74 | try { 75 | logs_1.printTestLog(io_1.latestTestLog()); 76 | } 77 | catch (err) { 78 | logger_1.default.error("Latest testlog could not be read: " + err.message); 79 | } 80 | break; 81 | case "latest_perf": 82 | try { 83 | logs_1.printPerfLog(io_1.latestPerfLog()); 84 | } 85 | catch (err) { 86 | logger_1.default.error("Latest testlog could not be read: " + err.message); 87 | } 88 | break; 89 | case "latest_perf_diff": 90 | if (!logs_1.printLatestDiff()) { 91 | logger_1.default.error("Missing entries: Perf must have been run at least two times."); 92 | } 93 | break; 94 | case "back":// Navigation 95 | return [2 /*return*/]; 96 | case "exit": 97 | process.exit(0); 98 | break; // Make linter shut up. 99 | default: 100 | process.exit(1); 101 | } 102 | return [4 /*yield*/, exports.logsMenu()]; 103 | case 2: 104 | _a.sent(); 105 | return [2 /*return*/]; 106 | } 107 | }); 108 | }); }; 109 | -------------------------------------------------------------------------------- /bin/prompt/logs_prompt.ts: -------------------------------------------------------------------------------- 1 | import { 2 | NAV_CHOICES, prompt, SEPARATOR} from "./utils"; 3 | import {printLatestDiff, printPerfLog, printTestLog} from "../../script/utils/logs"; 4 | import {latestPerfLog, latestTestLog} from "../../script/utils/io"; 5 | import Logger from "../../script/utils/logger"; 6 | 7 | export const logsPrompt = { 8 | type: 'list', 9 | name: 'logs', 10 | message: 'Select an action to perform', 11 | choices: [ 12 | { 13 | key: 't', 14 | name: 'View latest test log', 15 | value: 'latest_test' 16 | }, 17 | { 18 | key: 'p', 19 | name: 'View latest perf log', 20 | value: 'latest_perf' 21 | }, 22 | { 23 | key: 'p', 24 | name: 'View latest perf diff', 25 | value: 'latest_perf_diff' 26 | }].concat(SEPARATOR).concat(NAV_CHOICES) 27 | }; 28 | 29 | export const logsMenu = async (): Promise => { 30 | const selected = await prompt(logsPrompt); 31 | switch (selected.logs) { 32 | case "latest_test": 33 | try { 34 | printTestLog(latestTestLog()); 35 | } catch (err) { 36 | Logger.error(`Latest testlog could not be read: ${err.message}`); 37 | } 38 | break; 39 | case "latest_perf": 40 | try { 41 | printPerfLog(latestPerfLog()); 42 | } catch (err) { 43 | Logger.error(`Latest testlog could not be read: ${err.message}`); 44 | } 45 | break; 46 | case "latest_perf_diff": 47 | if (!printLatestDiff()) { 48 | Logger.error(`Missing entries: Perf must have been run at least two times.`); 49 | } 50 | break; 51 | case "back": // Navigation 52 | return; 53 | case "exit": 54 | process.exit(0); 55 | break; // Make linter shut up. 56 | default: 57 | process.exit(1); 58 | } 59 | await logsMenu(); 60 | }; 61 | -------------------------------------------------------------------------------- /bin/prompt/main_prompt.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { 3 | return new (P || (P = Promise))(function (resolve, reject) { 4 | function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } 5 | function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } 6 | function step(result) { result.done ? resolve(result.value) : new P(function (resolve) { resolve(result.value); }).then(fulfilled, rejected); } 7 | step((generator = generator.apply(thisArg, _arguments || [])).next()); 8 | }); 9 | }; 10 | var __generator = (this && this.__generator) || function (thisArg, body) { 11 | var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g; 12 | return g = { next: verb(0), "throw": verb(1), "return": verb(2) }, typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g; 13 | function verb(n) { return function (v) { return step([n, v]); }; } 14 | function step(op) { 15 | if (f) throw new TypeError("Generator is already executing."); 16 | while (_) try { 17 | if (f = 1, y && (t = y[op[0] & 2 ? "return" : op[0] ? "throw" : "next"]) && !(t = t.call(y, op[1])).done) return t; 18 | if (y = 0, t) op = [0, t.value]; 19 | switch (op[0]) { 20 | case 0: case 1: t = op; break; 21 | case 4: _.label++; return { value: op[1], done: false }; 22 | case 5: _.label++; y = op[1]; op = [0]; continue; 23 | case 7: op = _.ops.pop(); _.trys.pop(); continue; 24 | default: 25 | if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; } 26 | if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; } 27 | if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; } 28 | if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; } 29 | if (t[2]) _.ops.pop(); 30 | _.trys.pop(); continue; 31 | } 32 | op = body.call(thisArg, _); 33 | } catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; } 34 | if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true }; 35 | } 36 | }; 37 | var _this = this; 38 | Object.defineProperty(exports, "__esModule", { value: true }); 39 | var utils_1 = require("./utils"); 40 | var tests_prompt_1 = require("./tests_prompt"); 41 | var perf_prompt_1 = require("./perf_prompt"); 42 | var logs_prompt_1 = require("./logs_prompt"); 43 | var compile_prompt_1 = require("./compile_prompt"); 44 | exports.mainPrompt = { 45 | type: 'list', 46 | name: 'main', 47 | message: 'Select an action to perform', 48 | choices: [ 49 | { 50 | key: 'c', 51 | name: 'Compile contracts', 52 | value: 'compile' 53 | }, 54 | { 55 | key: 't', 56 | name: 'Run tests', 57 | value: 'tests' 58 | }, 59 | { 60 | key: 'p', 61 | name: 'Run perf', 62 | value: 'perf' 63 | }, 64 | { 65 | key: 'l', 66 | name: 'Check logs', 67 | value: 'logs' 68 | } 69 | ].concat(utils_1.SEPARATOR).concat(utils_1.EXIT_CHOICE) 70 | }; 71 | exports.mainMenu = function () { return __awaiter(_this, void 0, void 0, function () { 72 | var selected, _a; 73 | return __generator(this, function (_b) { 74 | switch (_b.label) { 75 | case 0: return [4 /*yield*/, utils_1.prompt(exports.mainPrompt)]; 76 | case 1: 77 | selected = _b.sent(); 78 | _a = selected.main; 79 | switch (_a) { 80 | case "tests": return [3 /*break*/, 2]; 81 | case "perf": return [3 /*break*/, 4]; 82 | case "logs": return [3 /*break*/, 6]; 83 | case "compile": return [3 /*break*/, 8]; 84 | case "exit": return [3 /*break*/, 10]; 85 | } 86 | return [3 /*break*/, 11]; 87 | case 2: // Options 88 | return [4 /*yield*/, tests_prompt_1.testsMenu()]; 89 | case 3: 90 | _b.sent(); 91 | return [3 /*break*/, 12]; 92 | case 4: // Options 93 | return [4 /*yield*/, perf_prompt_1.perfMenu()]; 94 | case 5: 95 | _b.sent(); 96 | return [3 /*break*/, 12]; 97 | case 6: // Options 98 | return [4 /*yield*/, logs_prompt_1.logsMenu()]; 99 | case 7: 100 | _b.sent(); 101 | return [3 /*break*/, 12]; 102 | case 8: // Options 103 | return [4 /*yield*/, compile_prompt_1.compileMenu()]; 104 | case 9: 105 | _b.sent(); 106 | return [3 /*break*/, 12]; 107 | case 10: // Navigation 108 | return [2 /*return*/, true]; 109 | case 11: return [2 /*return*/, false]; 110 | case 12: return [2 /*return*/, false]; 111 | } 112 | }); 113 | }); }; 114 | -------------------------------------------------------------------------------- /bin/prompt/main_prompt.ts: -------------------------------------------------------------------------------- 1 | import {EXIT_CHOICE, prompt, SEPARATOR} from "./utils"; 2 | import {testsMenu} from "./tests_prompt"; 3 | import {perfMenu} from "./perf_prompt"; 4 | import {logsMenu} from "./logs_prompt"; 5 | import {compileMenu} from "./compile_prompt"; 6 | 7 | export const mainPrompt = { 8 | type: 'list', 9 | name: 'main', 10 | message: 'Select an action to perform', 11 | choices: [ 12 | { 13 | key: 'c', 14 | name: 'Compile contracts', 15 | value: 'compile' 16 | }, 17 | { 18 | key: 't', 19 | name: 'Run tests', 20 | value: 'tests' 21 | }, 22 | { 23 | key: 'p', 24 | name: 'Run perf', 25 | value: 'perf' 26 | }, 27 | { 28 | key: 'l', 29 | name: 'Check logs', 30 | value: 'logs' 31 | } 32 | ].concat(SEPARATOR).concat(EXIT_CHOICE) 33 | }; 34 | 35 | export const mainMenu = async (): Promise => { 36 | const selected = await prompt(mainPrompt); 37 | switch (selected.main) { 38 | case "tests": // Options 39 | await testsMenu(); 40 | break; 41 | case "perf": // Options 42 | await perfMenu(); 43 | break; 44 | case "logs": // Options 45 | await logsMenu(); 46 | break; 47 | case "compile": // Options 48 | await compileMenu(); 49 | break; 50 | case "exit": // Navigation 51 | return true; 52 | default: 53 | return false; 54 | } 55 | return false; 56 | }; 57 | -------------------------------------------------------------------------------- /bin/prompt/perf_prompt.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { 3 | return new (P || (P = Promise))(function (resolve, reject) { 4 | function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } 5 | function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } 6 | function step(result) { result.done ? resolve(result.value) : new P(function (resolve) { resolve(result.value); }).then(fulfilled, rejected); } 7 | step((generator = generator.apply(thisArg, _arguments || [])).next()); 8 | }); 9 | }; 10 | var __generator = (this && this.__generator) || function (thisArg, body) { 11 | var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g; 12 | return g = { next: verb(0), "throw": verb(1), "return": verb(2) }, typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g; 13 | function verb(n) { return function (v) { return step([n, v]); }; } 14 | function step(op) { 15 | if (f) throw new TypeError("Generator is already executing."); 16 | while (_) try { 17 | if (f = 1, y && (t = y[op[0] & 2 ? "return" : op[0] ? "throw" : "next"]) && !(t = t.call(y, op[1])).done) return t; 18 | if (y = 0, t) op = [0, t.value]; 19 | switch (op[0]) { 20 | case 0: case 1: t = op; break; 21 | case 4: _.label++; return { value: op[1], done: false }; 22 | case 5: _.label++; y = op[1]; op = [0]; continue; 23 | case 7: op = _.ops.pop(); _.trys.pop(); continue; 24 | default: 25 | if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; } 26 | if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; } 27 | if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; } 28 | if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; } 29 | if (t[2]) _.ops.pop(); 30 | _.trys.pop(); continue; 31 | } 32 | op = body.call(thisArg, _); 33 | } catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; } 34 | if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true }; 35 | } 36 | }; 37 | var _this = this; 38 | Object.defineProperty(exports, "__esModule", { value: true }); 39 | var utils_1 = require("./utils"); 40 | var perf_1 = require("../../script/perf"); 41 | var io_1 = require("../../script/utils/io"); 42 | var logs_1 = require("../../script/utils/logs"); 43 | var data_reader_1 = require("../../script/utils/data_reader"); 44 | exports.perfSelectionData = function () { 45 | var choices = data_reader_1.getAllPerfFiles().map(function (file) { 46 | return { 47 | name: file[0] + "/" + file[1], 48 | value: file 49 | }; 50 | }); 51 | return { 52 | type: 'checkbox', 53 | message: 'Select contracts (select none and press to go back)', 54 | name: "perf", 55 | choices: choices 56 | }; 57 | }; 58 | exports.perfMenu = function () { return __awaiter(_this, void 0, void 0, function () { 59 | var selected; 60 | return __generator(this, function (_a) { 61 | switch (_a.label) { 62 | case 0: return [4 /*yield*/, utils_1.prompt(exports.perfSelectionData())]; 63 | case 1: 64 | selected = _a.sent(); 65 | if (selected.perf.length === 0) { 66 | return [2 /*return*/]; 67 | } 68 | return [4 /*yield*/, perf_1.perf(selected.perf, false)]; 69 | case 2: 70 | _a.sent(); 71 | logs_1.printPerfLog(io_1.latestPerfLog()); 72 | return [4 /*yield*/, exports.perfMenu()]; 73 | case 3: 74 | _a.sent(); 75 | return [2 /*return*/]; 76 | } 77 | }); 78 | }); }; 79 | -------------------------------------------------------------------------------- /bin/prompt/perf_prompt.ts: -------------------------------------------------------------------------------- 1 | import {prompt} from "./utils"; 2 | import {perf} from "../../script/perf"; 3 | import {latestPerfLog} from "../../script/utils/io"; 4 | import {printPerfLog} from "../../script/utils/logs"; 5 | import {getAllPerfFiles} from "../../script/utils/data_reader"; 6 | 7 | export const perfSelectionData = () => { 8 | const choices = getAllPerfFiles().map((file) => { 9 | return { 10 | name: `${file[0]}/${file[1]}`, 11 | value: file 12 | }; 13 | }); 14 | return { 15 | type: 'checkbox', 16 | message: 'Select contracts (select none and press to go back)', 17 | name: "perf", 18 | choices 19 | }; 20 | }; 21 | 22 | export const perfMenu = async (): Promise => { 23 | const selected = await prompt(perfSelectionData()); 24 | if (selected.perf.length === 0) { 25 | return; 26 | } 27 | await perf(selected.perf, false); 28 | printPerfLog(latestPerfLog()); 29 | await perfMenu(); 30 | }; 31 | -------------------------------------------------------------------------------- /bin/prompt/tests_prompt.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { 3 | return new (P || (P = Promise))(function (resolve, reject) { 4 | function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } 5 | function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } 6 | function step(result) { result.done ? resolve(result.value) : new P(function (resolve) { resolve(result.value); }).then(fulfilled, rejected); } 7 | step((generator = generator.apply(thisArg, _arguments || [])).next()); 8 | }); 9 | }; 10 | var __generator = (this && this.__generator) || function (thisArg, body) { 11 | var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g; 12 | return g = { next: verb(0), "throw": verb(1), "return": verb(2) }, typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g; 13 | function verb(n) { return function (v) { return step([n, v]); }; } 14 | function step(op) { 15 | if (f) throw new TypeError("Generator is already executing."); 16 | while (_) try { 17 | if (f = 1, y && (t = y[op[0] & 2 ? "return" : op[0] ? "throw" : "next"]) && !(t = t.call(y, op[1])).done) return t; 18 | if (y = 0, t) op = [0, t.value]; 19 | switch (op[0]) { 20 | case 0: case 1: t = op; break; 21 | case 4: _.label++; return { value: op[1], done: false }; 22 | case 5: _.label++; y = op[1]; op = [0]; continue; 23 | case 7: op = _.ops.pop(); _.trys.pop(); continue; 24 | default: 25 | if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; } 26 | if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; } 27 | if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; } 28 | if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; } 29 | if (t[2]) _.ops.pop(); 30 | _.trys.pop(); continue; 31 | } 32 | op = body.call(thisArg, _); 33 | } catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; } 34 | if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true }; 35 | } 36 | }; 37 | var _this = this; 38 | Object.defineProperty(exports, "__esModule", { value: true }); 39 | var utils_1 = require("./utils"); 40 | var tests_1 = require("../../script/tests"); 41 | var logs_1 = require("../../script/utils/logs"); 42 | var io_1 = require("../../script/utils/io"); 43 | var data_reader_1 = require("../../script/utils/data_reader"); 44 | exports.testSelectionData = function () { 45 | var choices = data_reader_1.getAllTestFiles().map(function (file) { 46 | return { 47 | name: file[0] + "/" + file[1], 48 | value: file 49 | }; 50 | }); 51 | return { 52 | type: 'checkbox', 53 | message: 'Select contracts (select none and press to go back)', 54 | name: "tests", 55 | choices: choices 56 | }; 57 | }; 58 | exports.testsMenu = function () { return __awaiter(_this, void 0, void 0, function () { 59 | var selected; 60 | return __generator(this, function (_a) { 61 | switch (_a.label) { 62 | case 0: return [4 /*yield*/, utils_1.prompt(exports.testSelectionData())]; 63 | case 1: 64 | selected = _a.sent(); 65 | if (selected.tests.length === 0) { 66 | return [2 /*return*/]; 67 | } 68 | return [4 /*yield*/, tests_1.test(selected.tests, false)]; 69 | case 2: 70 | _a.sent(); 71 | logs_1.printTestLog(io_1.latestTestLog()); 72 | return [4 /*yield*/, exports.testsMenu()]; 73 | case 3: 74 | _a.sent(); 75 | return [2 /*return*/]; 76 | } 77 | }); 78 | }); }; 79 | -------------------------------------------------------------------------------- /bin/prompt/tests_prompt.ts: -------------------------------------------------------------------------------- 1 | import {prompt} from "./utils"; 2 | import {test} from "../../script/tests"; 3 | import {printTestLog} from "../../script/utils/logs"; 4 | import {latestTestLog} from "../../script/utils/io"; 5 | import {getAllTestFiles} from "../../script/utils/data_reader"; 6 | 7 | export const testSelectionData = () => { 8 | const choices = getAllTestFiles().map((file) => { 9 | return { 10 | name: `${file[0]}/${file[1]}`, 11 | value: file 12 | }; 13 | }); 14 | return { 15 | type: 'checkbox', 16 | message: 'Select contracts (select none and press to go back)', 17 | name: "tests", 18 | choices 19 | }; 20 | }; 21 | 22 | export const testsMenu = async (): Promise => { 23 | const selected = await prompt(testSelectionData()); 24 | if (selected.tests.length === 0) { 25 | return; 26 | } 27 | await test(selected.tests, false); 28 | printTestLog(latestTestLog()); 29 | await testsMenu(); 30 | }; 31 | -------------------------------------------------------------------------------- /bin/prompt/utils.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { 3 | return new (P || (P = Promise))(function (resolve, reject) { 4 | function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } 5 | function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } 6 | function step(result) { result.done ? resolve(result.value) : new P(function (resolve) { resolve(result.value); }).then(fulfilled, rejected); } 7 | step((generator = generator.apply(thisArg, _arguments || [])).next()); 8 | }); 9 | }; 10 | var __generator = (this && this.__generator) || function (thisArg, body) { 11 | var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g; 12 | return g = { next: verb(0), "throw": verb(1), "return": verb(2) }, typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g; 13 | function verb(n) { return function (v) { return step([n, v]); }; } 14 | function step(op) { 15 | if (f) throw new TypeError("Generator is already executing."); 16 | while (_) try { 17 | if (f = 1, y && (t = y[op[0] & 2 ? "return" : op[0] ? "throw" : "next"]) && !(t = t.call(y, op[1])).done) return t; 18 | if (y = 0, t) op = [0, t.value]; 19 | switch (op[0]) { 20 | case 0: case 1: t = op; break; 21 | case 4: _.label++; return { value: op[1], done: false }; 22 | case 5: _.label++; y = op[1]; op = [0]; continue; 23 | case 7: op = _.ops.pop(); _.trys.pop(); continue; 24 | default: 25 | if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; } 26 | if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; } 27 | if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; } 28 | if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; } 29 | if (t[2]) _.ops.pop(); 30 | _.trys.pop(); continue; 31 | } 32 | op = body.call(thisArg, _); 33 | } catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; } 34 | if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true }; 35 | } 36 | }; 37 | var _this = this; 38 | Object.defineProperty(exports, "__esModule", { value: true }); 39 | var inquirer = require("inquirer"); 40 | var io_1 = require("../../script/utils/io"); 41 | exports.BACK_CHOICE = { 42 | key: 'b', 43 | name: 'Back', 44 | value: "back" 45 | }; 46 | exports.EXIT_CHOICE = { 47 | key: 'e', 48 | name: 'Exit', 49 | value: 'exit' 50 | }; 51 | exports.NAV_CHOICES = [exports.BACK_CHOICE, exports.EXIT_CHOICE]; 52 | exports.SEPARATOR = [new inquirer.Separator()]; 53 | exports.printDelim = function (text) { 54 | io_1.println('-- DOC START --'); 55 | io_1.println(text); 56 | io_1.println('-- DOC END --'); 57 | }; 58 | exports.printFile = function (filePath, delimited) { 59 | if (delimited === void 0) { delimited = true; } 60 | var text = io_1.readText(filePath); 61 | if (delimited) { 62 | exports.printDelim(text); 63 | } 64 | else { 65 | io_1.println(text); 66 | } 67 | }; 68 | exports.prompt = function (promptData) { return __awaiter(_this, void 0, void 0, function () { 69 | return __generator(this, function (_a) { 70 | return [2 /*return*/, inquirer.prompt([promptData])]; 71 | }); 72 | }); }; 73 | -------------------------------------------------------------------------------- /bin/prompt/utils.ts: -------------------------------------------------------------------------------- 1 | import * as inquirer from 'inquirer'; 2 | import {println, readText} from "../../script/utils/io"; 3 | 4 | export const BACK_CHOICE = { 5 | key: 'b', 6 | name: 'Back', 7 | value: "back" 8 | }; 9 | 10 | export const EXIT_CHOICE = { 11 | key: 'e', 12 | name: 'Exit', 13 | value: 'exit' 14 | }; 15 | 16 | export const NAV_CHOICES = [BACK_CHOICE, EXIT_CHOICE]; 17 | 18 | export const SEPARATOR = [new inquirer.Separator()]; 19 | 20 | export const printDelim = (text: string) => { 21 | println('-- DOC START --'); 22 | println(text); 23 | println('-- DOC END --'); 24 | }; 25 | 26 | export const printFile = (filePath: string, delimited: boolean = true) => { 27 | const text = readText(filePath); 28 | if (delimited) { 29 | printDelim(text); 30 | } else { 31 | println(text); 32 | } 33 | }; 34 | 35 | export const prompt = async (promptData) => { 36 | return inquirer.prompt([promptData]); 37 | }; 38 | -------------------------------------------------------------------------------- /data/data.json: -------------------------------------------------------------------------------- 1 | { 2 | "packages": { 3 | "bits": "packages/bits.json", 4 | "bytes": "packages/bytes.json", 5 | "math": "packages/math.json", 6 | "patricia_tree": "packages/patricia_tree.json", 7 | "strings": "packages/strings.json", 8 | "tokens": "packages/tokens.json", 9 | "unsafe": "packages/unsafe.json" 10 | } 11 | } -------------------------------------------------------------------------------- /data/packages/bits.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "bits", 3 | "contracts": [ 4 | { 5 | "name": "Bits.sol", 6 | "tests": [ 7 | { 8 | "name": "bits.sol" 9 | } 10 | ], 11 | "perf": [ 12 | { 13 | "name": "bits.sol" 14 | } 15 | ] 16 | } 17 | ] 18 | } -------------------------------------------------------------------------------- /data/packages/bytes.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "bytes", 3 | "contracts": [ 4 | { 5 | "name": "Bytes.sol", 6 | "tests": [ 7 | { 8 | "name": "bytes.sol" 9 | } 10 | ], 11 | "perf": [ 12 | { 13 | "name": "bytes.sol" 14 | } 15 | ] 16 | } 17 | ] 18 | } -------------------------------------------------------------------------------- /data/packages/math.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "math", 3 | "contracts": [ 4 | { 5 | "name": "ExactMath.sol", 6 | "tests": [ 7 | { 8 | "name": "exact_math.sol" 9 | } 10 | ], 11 | "perf": [ 12 | { 13 | "name": "exact_math.sol" 14 | } 15 | ] 16 | } 17 | ], 18 | "extra": { 19 | "tests": [ 20 | { 21 | "name": "math_consistency.sol" 22 | } 23 | ] 24 | } 25 | } -------------------------------------------------------------------------------- /data/packages/patricia_tree.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "patricia_tree", 3 | "contracts": [ 4 | { 5 | "name": "PatriciaTree.sol", 6 | "tests": [ 7 | { 8 | "name": "patricia_tree.sol" 9 | } 10 | ], 11 | "perf": [ 12 | { 13 | "name": "patricia_tree.sol" 14 | }, 15 | { 16 | "name": "patricia_tree_extended.sol", 17 | "extended": true 18 | } 19 | ] 20 | }, 21 | { 22 | "name": "Data.sol", 23 | "tests": [ 24 | { 25 | "name": "data.sol" 26 | } 27 | ], 28 | "perf": [ 29 | { 30 | "name": "data.sol" 31 | } 32 | ] 33 | } 34 | ] 35 | } -------------------------------------------------------------------------------- /data/packages/strings.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "strings", 3 | "contracts": [ 4 | { 5 | "name": "Strings.sol", 6 | "tests": [ 7 | { 8 | "name": "strings.sol" 9 | } 10 | ], 11 | "perf": [ 12 | { 13 | "name": "strings.sol" 14 | } 15 | ] 16 | } 17 | ] 18 | } -------------------------------------------------------------------------------- /data/packages/tokens.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "tokens", 3 | "contracts": [ 4 | { 5 | "name": "ERC20TokenFace.sol" 6 | } 7 | ] 8 | } -------------------------------------------------------------------------------- /data/packages/unsafe.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "unsafe", 3 | "contracts": [ 4 | { 5 | "name": "Memory.sol", 6 | "tests": [ 7 | { 8 | "name": "memory.sol" 9 | } 10 | ], 11 | "perf": [ 12 | { 13 | "name": "memory.sol" 14 | } 15 | ] 16 | } 17 | ] 18 | } -------------------------------------------------------------------------------- /docs/_img/key_hash_segments.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ethereum/solidity-examples/ba15d56b62cf384e983006bb139af59a03ce22dc/docs/_img/key_hash_segments.png -------------------------------------------------------------------------------- /docs/_img/label_edge_node.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ethereum/solidity-examples/ba15d56b62cf384e983006bb139af59a03ce22dc/docs/_img/label_edge_node.png -------------------------------------------------------------------------------- /docs/_img/root_node.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ethereum/solidity-examples/ba15d56b62cf384e983006bb139af59a03ce22dc/docs/_img/root_node.png -------------------------------------------------------------------------------- /docs/bits/Bits.md: -------------------------------------------------------------------------------- 1 | # Bits 2 | 3 | **Package:** bits 4 | 5 | **Contract type:** library 6 | 7 | **Source file:** [Bits.sol](../../src/bits/Bits.sol) 8 | 9 | **Example usage:** [BitsExamples.sol](../../examples/bits/BitsExamples.sol) 10 | 11 | **Tests source file:** [bits.sol](../../test/bits/bits.sol) 12 | 13 | **Perf (gas usage) source file:** [bits.sol](../../perf/bits/bits.sol) 14 | 15 | ## description 16 | 17 | The `Bits` library is used for accessing and manipulating bits using numbers of type `uint` to represent bitfields. -------------------------------------------------------------------------------- /docs/bytes/Bytes.md: -------------------------------------------------------------------------------- 1 | # Bytes 2 | 3 | **Package:** bytes 4 | 5 | **Contract type:** library 6 | 7 | **Source file:** [Bytes.sol](../../src/bytes/Bytes.sol) 8 | 9 | **Example usage:** [BytesExamples.sol](../../examples/bytes/BytesExamples.sol) 10 | 11 | **Tests source file:** [bytes.sol](../../test/bytes/bytes.sol) 12 | 13 | **Perf (gas usage) source file:** [bytes.sol](../../perf/bytes/bytes.sol) 14 | 15 | ## description 16 | 17 | The `Bytes` library has functions for working with bytes. 18 | 19 | #### Bytes, strings, numbers and padding in Ethereum 20 | 21 | In Ethereum, strings and bytes are padded on the lower-order (right) side with zero-bytes, while other types (such as numbers and addresses) are padded on the higher-order side. As an example, this is how we would store the string "abcd" in one full word (32 bytes): 22 | 23 | `0x6162636400000000000000000000000000000000000000000000000000000000` 24 | 25 | This is how the number `0x61626364` would be stored: 26 | 27 | `0x0000000000000000000000000000000000000000000000000000000061626364` 28 | 29 | Solidity has built-in support for this, and will automatically use the correct padding rule depending on the type. The `web3.js` javascript API has built-in support for this as well, and padding is normally done automatically when javascript values are being encoded and decoded. 30 | 31 | ##### Using strings and hex-literals with fixed size bytes 32 | 33 | Padding rule is important when working with literals. `bytesN` will use different internal representations for different types of literals. 34 | 35 | 1. Number literals assigned to `bytesN` variables are padded to the left. 36 | 2. String literals assigned to `bytesN` variables are padded to the right. 37 | 38 | ``` 39 | // 0x0000000000000000000000000000000000000000000000000000000001020304 40 | var numLit = bytes32(0x01020304); 41 | 42 | // 0x3031303230333034000000000000000000000000000000000000000000000000 43 | var strLit = bytes32("01020304"); 44 | ``` 45 | 46 | ##### Index access in fixed size bytes 47 | 48 | Accessing individual bytes by index is possible for all `bytesN` types (just as with the dynamic `bytes`). The highest order byte is found at index `0`. 49 | 50 | ``` 51 | var numLit_0 = numLit[0]; // 0 52 | var numLit_31 = numLit[31]; // 0x04 53 | 54 | var strLit_0 = strLit[0]; // 0x30 55 | var strLit_31 = strLit[31]; // 0 56 | ``` 57 | 58 | ##### Using `byte` in inline assembly 59 | 60 | The `byte(index, data)` instruction will read the byte at position `index` from an item `data`, and put it on the stack. The range of `index` is `[0, 31]`. 61 | 62 | ``` 63 | uint numLit_0; 64 | uint numLit_31; 65 | assembly { 66 | numLit_0 := byte(0, numLit) 67 | numLit_31 := byte(31, numLit) 68 | } 69 | // numLit_0 == 0 70 | // numLit_31 == 4 71 | ``` 72 | 73 | ##### The `byte` (`bytes1`) type 74 | 75 | The `byte` type, which is an alias for `bytes1`, stores a single byte. A `byte` is different then a number type of the same bit-size, like for example `uint8`, because their internal representations are different. 76 | 77 | ``` 78 | // 0x0000000000000000000000000000000000000000000000000000000000000001 79 | uint8 u8 = 1; 80 | 81 | // 0x0100000000000000000000000000000000000000000000000000000000000000 82 | byte b = 1; 83 | ``` 84 | 85 | This is important when converting between different types - especially when inline assembly is involved. 86 | 87 | ``` 88 | bytes32 b32 = "Terry A. Davis"; 89 | byte b; 90 | assembly { 91 | b := byte(0, b32) 92 | } 93 | ``` 94 | 95 | What is the value of `b` after this code has been run? 96 | 97 | The value will be `0`, but the internal representation would be `0x0000000000000000000000000000000000000000000000000000000000000054`, which lies outside of the allowed range so it is technically invalid. 98 | 99 | Finally, these sections deal with bytes in memory and on the stack. For bytes packed in storage, things can be different. 100 | -------------------------------------------------------------------------------- /docs/cli.md: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ethereum/solidity-examples/ba15d56b62cf384e983006bb139af59a03ce22dc/docs/cli.md -------------------------------------------------------------------------------- /docs/math/ExactMath.md: -------------------------------------------------------------------------------- 1 | # ExactMath 2 | 3 | **Package:** math 4 | 5 | **Contract type:** library 6 | 7 | **Source file:** [ExactMath.sol](../../src/math/ExactMath.sol) 8 | 9 | **Example usage:** [ExactMathExamples.sol](../../examples/math/ExactMathExamples.sol) 10 | 11 | **Tests source file:** [exact_math.sol](../../test/math/exact_math.sol) 12 | 13 | **Perf (gas usage) source file:** [exact_math.sol](../../perf/math/exact_math.sol) 14 | 15 | ## description 16 | 17 | TODO link to new under/overflow docs when they are added to readthedocs. 18 | 19 | The ExactMath library contains functions for performing exact arithmetic operations. 20 | 21 | Inspiration for adding this library was taken from the SafeMath library in zeppelin-solidity: https://github.com/OpenZeppelin/zeppelin-solidity/blob/353285e5d96477b4abb86f7cde9187e84ed251ac/contracts/math/SafeMath.sol 22 | -------------------------------------------------------------------------------- /docs/patricia_tree/Data.md: -------------------------------------------------------------------------------- 1 | # Data 2 | 3 | **Package:** patricia_tree 4 | 5 | **Contract type:** library 6 | 7 | **Source file:** [Data.sol](../../src/patricia_tree/Data.sol) 8 | 9 | **Tests source file:** [data.sol](../../test/patricia_tree/data.sol) 10 | 11 | **Perf (gas usage) source file:** [data.sol](../../perf/patricia_tree/data.sol) 12 | 13 | ## Description 14 | 15 | `Data.sol` contains a number of data structures and utility functions for those data structures. It is meant to be used by Patricia trees. -------------------------------------------------------------------------------- /docs/perf.md: -------------------------------------------------------------------------------- 1 | ## Perf 2 | 3 | The performance of a contract is measured by looking at its gas-usage. It is mostly used in a relative way, by comparing the gas cost before and after code (or the compiler, or the evm) has been changed. 4 | 5 | Each performance test is a single-method contract which is run in the go-ethereum `evm`, but unlike tests, perf functors implements the `perf`-function directly. 6 | 7 | The `perf` function returns the gas spent during execution of the tested function. This is implemented by storing the remaining gas before and after the function is executed, and then taking the difference. 8 | 9 | The reason that perf metering is done manually in every function is so that the implementor can exclude the staging part of the code (preparing variables & data) from the code that should be metered. 10 | 11 | #### Example 12 | 13 | This is the `STLPerf` contract; its `perf` method is the basis for all perf: 14 | 15 | ``` 16 | contract STLPerf { 17 | function perf() public payable returns (uint); 18 | } 19 | ``` 20 | 21 | This is an example of a perf functor. It measures the gas cost when the `Bytes.equals` function can early out because the lengths of the two `bytes` are not the same: 22 | 23 | ``` 24 | contract PerfBytesEqualsDifferentLengthFail is BytesPerf { 25 | function perf() public payable returns (uint) { 26 | bytes memory bts1 = new bytes(0); 27 | bytes memory bts2 = new bytes(1); 28 | uint gasPre = msg.gas; 29 | Bytes.equals(bts1, bts2); 30 | uint gasPost = msg.gas; 31 | return gasPre - gasPost; 32 | } 33 | } 34 | ``` 35 | 36 | ### Absolute vs. relative results 37 | 38 | Perf is not used primarily to check how much gas is spent when running a function; the main reason is to see how gas cost changes when there has been changes to the code, compiler, and runtime environment. This analysis is made possible by adding perf functions to all library functions, and using a system that computes and displays the difference if gas cost between code updates. 39 | 40 | ### Accuracy 41 | 42 | The goal is to keep the perf result as close to the runtime gas-cost of the tested function as possible, but there will usually be a small difference. One reason is that part of the code may be optimized away unless some redundant code is added. 43 | 44 | The goal is to minimize these deviations, but **there are no guaranteed bounds**. -------------------------------------------------------------------------------- /docs/strings/Strings.md: -------------------------------------------------------------------------------- 1 | # Strings 2 | 3 | **Package:** strings 4 | 5 | **Contract type:** Static library 6 | 7 | **Source file:** [Strings.sol](../../src/strings/Strings.sol) 8 | 9 | **Example usage:** [StringsExamples.sol](../../examples/strings/StringsExamples.sol) 10 | 11 | **Tests source file:** [strings_tests.sol](../../test/strings/strings_tests.sol) 12 | 13 | **Perf (gas usage) source file:** [strings_perfs.sol](../../perf/strings/strings_perfs.sol) 14 | 15 | ## description 16 | 17 | This library can be used to validate that a solidity string is valid UTF-8. 18 | 19 | Solidity strings uses the UTF-8 encoding, as defined in the unicode 10.0 standard: http://www.unicode.org/versions/Unicode10.0.0/, but the only checks that are currently carried out are compile-time checks of string literals. This library makes runtime checks possible. 20 | 21 | The idea to add a UTF string validation library came from Arachnid's (Nick Johnson) string-utils: https://github.com/Arachnid/solidity-stringutils 22 | -------------------------------------------------------------------------------- /docs/testing.md: -------------------------------------------------------------------------------- 1 | ## Tests 2 | 3 | Tests are functor-styled single-method contracts extending the `STLTest` contract and implementing its `testImpl` method. 4 | 5 | Tests are automatically compiled and run using a trivial test-runner written in javascript. It uses native `solc` and `evm` (go ethereum) to compile and execute the actual test code. 6 | 7 | Tests that fail will throw. This is ensured by always using Solidity's `assert` function for test conditions. 8 | 9 | 1. One test function signature. 10 | 2. One contract per test, one function per contract. 11 | 3. Two possible results: throws or does not throw. 12 | 13 | #### Example 14 | 15 | This is the `STLTest` contract; its `test` method is the basis for all tests. 16 | 17 | ``` 18 | contract STLTest { 19 | 20 | function test() public payable returns (bool ret) { 21 | ret = true; 22 | testImpl(); 23 | } 24 | 25 | function testImpl() internal; 26 | 27 | } 28 | ``` 29 | 30 | The test for `Bits.bitXor(uint bitfield, uint8 index)` looks like this: 31 | 32 | ``` 33 | contract TestBitsBitXor is BitsTest { 34 | function testImpl() internal { 35 | for (uint8 i = 0; i < 12; i++) { 36 | assert(ONES.bitXor(ONES, i*20) == 0); 37 | assert(ONES.bitXor(ZERO, i*20) == 1); 38 | assert(ZERO.bitXor(ONES, i*20) == 1); 39 | assert(ZERO.bitXor(ZERO, i*20) == 0); 40 | } 41 | } 42 | } 43 | ``` 44 | 45 | It loops over the tested `uint`s and makes sure XOR works as expected. 46 | 47 | `BitsTest` is a simple (abstract) contract that extends `STLTest` and includes some constants and bindings that are useful for multiple tests throughout the suite (this pattern is used in most suites): 48 | 49 | ``` 50 | contract BitsTest is STLTest { 51 | using Bits for uint; 52 | 53 | uint constant ZERO = uint(0); 54 | uint constant ONE = uint(1); 55 | uint constant ONES = uint(~0); 56 | } 57 | ``` 58 | 59 | ### Naming 60 | 61 | The xor test is named `TestBitsBitXor`: 62 | 63 | 1. `Test` is because it is a test contract, to distinguish it from other artifacts in the output directory. Tests always start with `Test`. 64 | 2. `Bits` is the name of the library contract being tested. 65 | 3. `BitXor` is the name of the function being tested. 66 | 67 | Tests that are expected to throw must have the word `Throws` somewhere in the name. There can be other things in there as well, like further description of the test. 68 | 69 | All test contracts for a given library is normally kept in the same solidity source file. 70 | 71 | ### Success and failure 72 | 73 | In `STLTest.test()`, the test result is set to `true` prior to the execution of the actual test-code. This is done to detect if the function `throws` (although the `evm` also indicates that an illegal jump was made). The real point of this mechanism is to have uniformity over all tests (and a very simple way to interpret the return data in JS), which makes it easy to update. 74 | 75 | 1. If a test functor does not have the word `Throws` in the name, test passes if the return value is `true`. 76 | 2. If a test functor has the word `Throws` in the name, test passes if the return value is not `true`. 77 | 78 | Note that the example test does not have the word `Throws` in the name, and is thus expected to not throw (i.e. none of the assertions is allowed to fail). 79 | -------------------------------------------------------------------------------- /docs/tokens/ERC20TokenFace.md: -------------------------------------------------------------------------------- 1 | # ERC20TokenFace 2 | 3 | **Package:** token 4 | 5 | **Contract type:** Interface 6 | 7 | **Source file:** [ERC20TokenFace.sol](../../src/tokens/ERC20TokenFace.sol) 8 | 9 | ## description 10 | 11 | An [ERC-20](https://github.com/ethereum/EIPs/blob/master/EIPS/eip-20-token-standard.md) compliant Token interface. 12 | 13 | -------------------------------------------------------------------------------- /docs/unsafe/Memory.md: -------------------------------------------------------------------------------- 1 | # Memory 2 | 3 | **Package:** unsafe 4 | 5 | **Contract type:** Static library 6 | 7 | **Source file:** [Memory.sol](../../src/unsafe/Memory.sol) 8 | 9 | **Tests source file:** [memory.sol](../../test/unsafe/memory.sol) 10 | 11 | **Perf (gas usage) source file:** [memory.sol](../../perf/unsafe/memory.sol) 12 | 13 | ## description 14 | 15 | The `unsafe` package contains code that performs potentially unsafe operations, for example reading and writing directly from memory. The memory library is used to work with memory directly; there are methods for copying memory, equals-checks, and converting from and to Solidity types. 16 | -------------------------------------------------------------------------------- /examples/bits/BitsExamples.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.4.16; 2 | pragma experimental "v0.5.0"; 3 | pragma experimental "ABIEncoderV2"; 4 | 5 | import {Bits} from "../src/bits/Bits.sol"; 6 | 7 | 8 | contract BitsExamples { 9 | 10 | using Bits for uint; 11 | 12 | // Set bits 13 | function setBitExample() public pure { 14 | uint n = 0; 15 | n = n.setBit(0); // Set the 0th bit. 16 | assert(n == 1); // 1 17 | n = n.setBit(1); // Set the 1st bit. 18 | assert(n == 3); // 11 19 | n = n.setBit(2); // Set the 2nd bit. 20 | assert(n == 7); // 111 21 | n = n.setBit(3); // Set the 3rd bit. 22 | assert(n == 15); // 1111 23 | 24 | // x.bit(y) == 1 => x.setBit(y) == x 25 | n = 1; 26 | assert(n.setBit(0) == n); 27 | } 28 | 29 | // Clear bits 30 | function clearBitExample() public pure { 31 | uint n = 15; // 1111 32 | n = n.clearBit(0); // Clear the 0th bit. 33 | assert(n == 14); // 1110 34 | n = n.clearBit(1); // Clear the 1st bit. 35 | assert(n == 12); // 1100 36 | n = n.clearBit(2); // Clear the 2nd bit. 37 | assert(n == 8); // 1000 38 | n = n.clearBit(3); // Clear the 3rd bit. 39 | assert(n == 0); // 0 40 | 41 | // x.bit(y) == 0 => x.clearBit(y) == x 42 | n = 0; 43 | assert(n.clearBit(0) == n); 44 | } 45 | 46 | // Toggle bits 47 | function toggleBitExample() public pure { 48 | uint n = 9; // 1001 49 | n = n.toggleBit(0); // Toggle the 0th bit. 50 | assert(n == 8); // 1000 51 | n = n.toggleBit(1); // Toggle the 1st bit. 52 | assert(n == 10); // 1010 53 | n = n.toggleBit(2); // Toggle the 2nd bit. 54 | assert(n == 14); // 1110 55 | n = n.toggleBit(3); // Toggle the 3rd bit. 56 | assert(n == 6); // 0110 57 | 58 | // x.toggleBit(y).toggleBit(y) == x (invertible) 59 | n = 55; 60 | assert(n.toggleBit(5).toggleBit(5) == n); 61 | } 62 | 63 | // Get an individual bit 64 | function bitExample() public pure { 65 | uint n = 9; // 1001 66 | assert(n.bit(0) == 1); 67 | assert(n.bit(1) == 0); 68 | assert(n.bit(2) == 0); 69 | assert(n.bit(3) == 1); 70 | } 71 | 72 | // Is a bit set 73 | function bitSetExample() public pure { 74 | uint n = 9; // 1001 75 | assert(n.bitSet(0) == true); 76 | assert(n.bitSet(1) == false); 77 | assert(n.bitSet(2) == false); 78 | assert(n.bitSet(3) == true); 79 | } 80 | 81 | // Are bits equal 82 | function bitEqualExample() public pure { 83 | uint n = 9; // 1001 84 | uint m = 3; // 0011 85 | assert(n.bitEqual(m, 0) == true); 86 | assert(n.bitEqual(m, 1) == false); 87 | assert(n.bitEqual(m, 2) == true); 88 | assert(n.bitEqual(m, 3) == false); 89 | } 90 | 91 | // Bit 'not' 92 | function bitNotExample() public pure { 93 | uint n = 9; // 1001 94 | assert(n.bitNot(0) == 0); 95 | assert(n.bitNot(1) == 1); 96 | assert(n.bitNot(2) == 1); 97 | assert(n.bitNot(3) == 0); 98 | 99 | // x.bit(y) = 1 - x.bitNot(y); 100 | assert(n.bitNot(0) == 1 - n.bit(0)); 101 | assert(n.bitNot(1) == 1 - n.bit(1)); 102 | assert(n.bitNot(2) == 1 - n.bit(2)); 103 | assert(n.bitNot(3) == 1 - n.bit(3)); 104 | } 105 | 106 | // Bits 'and' 107 | function bitAndExample() public pure { 108 | uint n = 9; // 1001 109 | uint m = 3; // 0011 110 | assert(n.bitAnd(m, 0) == 1); 111 | assert(n.bitAnd(m, 1) == 0); 112 | assert(n.bitAnd(m, 2) == 0); 113 | assert(n.bitAnd(m, 3) == 0); 114 | } 115 | 116 | // Bits 'or' 117 | function bitOrExample() public pure { 118 | uint n = 9; // 1001 119 | uint m = 3; // 0011 120 | assert(n.bitOr(m, 0) == 1); 121 | assert(n.bitOr(m, 1) == 1); 122 | assert(n.bitOr(m, 2) == 0); 123 | assert(n.bitOr(m, 3) == 1); 124 | } 125 | 126 | // Bits 'xor' 127 | function bitXorExample() public pure { 128 | uint n = 9; // 1001 129 | uint m = 3; // 0011 130 | assert(n.bitXor(m, 0) == 0); 131 | assert(n.bitXor(m, 1) == 1); 132 | assert(n.bitXor(m, 2) == 0); 133 | assert(n.bitXor(m, 3) == 1); 134 | } 135 | 136 | // Get bits 137 | function bitsExample() public pure { 138 | uint n = 13; // 0 ... 01101 139 | assert(n.bits(0, 4) == 13); // 1101 140 | assert(n.bits(1, 4) == 6); // 0110 141 | assert(n.bits(2, 4) == 3); // 0011 142 | assert(n.bits(3, 4) == 1); // 0001 143 | 144 | assert(n.bits(0, 4) == 13); // 1101 145 | assert(n.bits(0, 3) == 5); // 101 146 | assert(n.bits(0, 2) == 1); // 01 147 | assert(n.bits(0, 1) == 1); // 1 148 | } 149 | 150 | function bitsExampleThatFails() public pure { 151 | uint n = 13; 152 | n.bits(2, 0); // There is no zero-bit uint! 153 | } 154 | 155 | // Highest bit set 156 | function highestBitSetExample() public pure { 157 | uint n = 13; // 0 ... 01101 158 | assert(n.highestBitSet() == 3); // ^ 159 | 160 | } 161 | 162 | // Highest bit set 163 | function lowestBitSetExample() public pure { 164 | uint n = 12; // 0 ... 01100 165 | assert(n.lowestBitSet() == 2); // ^ 166 | 167 | } 168 | 169 | } -------------------------------------------------------------------------------- /examples/bytes/BytesExamples.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.4.16; 2 | pragma experimental "v0.5.0"; 3 | pragma experimental "ABIEncoderV2"; 4 | 5 | import {Bytes} from "../src/bytes/Bytes.sol"; 6 | 7 | 8 | contract BytesExamples { 9 | 10 | using Bytes for *; 11 | 12 | // Check if bytes are equal. 13 | function bytesEqualsExample() public pure { 14 | bytes memory bts0 = hex"01020304"; 15 | bytes memory bts1 = hex"01020304"; 16 | bytes memory bts2 = hex"05060708"; 17 | 18 | assert(bts0.equals(bts0)); // Check if a byte array equal to itself. 19 | assert(bts0.equals(bts1)); // Should be equal because they have the same byte at each position. 20 | assert(!bts0.equals(bts2)); // Should not be equal. 21 | } 22 | 23 | // Check reference equality 24 | function bytesEqualsRefExample() public pure { 25 | bytes memory bts0 = hex"01020304"; 26 | bytes memory bts1 = bts0; 27 | 28 | // Every 'bytes' will satisfy 'equalsRef' with itself. 29 | assert(bts0.equalsRef(bts0)); 30 | // Different variables, but bts0 was assigned to bts1, so they point to the same area in memory. 31 | assert(bts0.equalsRef(bts1)); 32 | // Changing a byte in bts0 will also affect bts1. 33 | bts0[2] = 0x55; 34 | assert(bts1[2] == 0x55); 35 | 36 | bytes memory bts2 = hex"01020304"; 37 | bytes memory bts3 = hex"01020304"; 38 | 39 | // These bytes has the same byte at each pos (so they would pass 'equals'), but they are referencing different areas in memory. 40 | assert(!bts2.equalsRef(bts3)); 41 | 42 | // Changing a byte in bts2 will not affect bts3. 43 | bts2[2] = 0x55; 44 | assert(bts3[2] != 0x55); 45 | } 46 | 47 | // copying 48 | function bytesCopyExample() public pure { 49 | bytes memory bts0 = hex"01020304"; 50 | 51 | var bts0Copy0 = bts0.copy(); 52 | 53 | // The individual bytes are the same. 54 | assert(bts0.equals(bts0Copy0)); 55 | // bts0Copy is indeed a (n independent) copy. 56 | assert(!bts0.equalsRef(bts0Copy0)); 57 | 58 | bytes memory bts1 = hex"0304"; 59 | 60 | // Copy with start index. 61 | var bts0Copy1 = bts0.copy(2); 62 | 63 | assert(bts0Copy1.equals(bts1)); 64 | 65 | bytes memory bts2 = hex"0203"; 66 | 67 | // Copy with start index and length. 68 | var bts0Copy2 = bts0.copy(1, 2); 69 | 70 | assert(bts0Copy2.equals(bts2)); 71 | } 72 | 73 | // concatenate 74 | function bytesConcatExample() public pure { 75 | bytes memory bts0 = hex"01020304"; 76 | bytes memory bts1 = hex"05060708"; 77 | 78 | bytes memory bts01 = hex"0102030405060708"; 79 | 80 | var cct = bts0.concat(bts1); 81 | 82 | // Should be equal to bts01 83 | assert(cct.equals(bts01)); 84 | } 85 | 86 | // find the highest byte set in a bytes32 87 | function bytes32HighestByteSetExample() public pure { 88 | bytes32 test0 = 0x01; 89 | bytes32 test1 = 0xbb00aa00; 90 | bytes32 test2 = "abc"; 91 | 92 | // with bytesN, the highest byte is the least significant one. 93 | assert(test0.highestByteSet() == 31); 94 | assert(test1.highestByteSet() == 30); // aa 95 | assert(test2.highestByteSet() == 2); 96 | 97 | // Make sure that in the case of test2, the highest byte is equal to "c". 98 | assert(test2[test2.highestByteSet()] == 0x63); 99 | } 100 | 101 | // find the lowest byte set in a bytes32 102 | function bytes32LowestByteSetExample() public pure { 103 | bytes32 test0 = 0x01; 104 | bytes32 test1 = 0xbb00aa00; 105 | bytes32 test2 = "abc"; 106 | 107 | // with bytesN, the lowest byte is the most significant one. 108 | assert(test0.lowestByteSet() == 31); 109 | assert(test1.lowestByteSet() == 28); // bb 110 | assert(test2.lowestByteSet() == 0); 111 | } 112 | 113 | // find the highest byte set in a uint 114 | function uintHighestByteSetExample() public pure { 115 | uint test0 = 0x01; 116 | uint test1 = 0xbb00aa00; 117 | 118 | // with uint, the highest byte is the most significant one. 119 | assert(test0.highestByteSet() == 0); 120 | assert(test1.highestByteSet() == 3); 121 | } 122 | 123 | // find the lowest byte set in a uint 124 | function uintLowestByteSetExample() public pure { 125 | uint test0 = 0x01; 126 | uint test1 = 0xbb00aa00; 127 | 128 | // with uint, the lowest byte is the least significant one. 129 | assert(test0.lowestByteSet() == 0); 130 | assert(test1.lowestByteSet() == 1); 131 | } 132 | 133 | } -------------------------------------------------------------------------------- /examples/math/ExactMathExamples.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.4.16; 2 | pragma experimental "v0.5.0"; 3 | pragma experimental "ABIEncoderV2"; 4 | 5 | import {ExactMath} from "../src/math/ExactMath.sol"; 6 | 7 | 8 | contract MathExamples { 9 | 10 | using ExactMath for int; 11 | using ExactMath for uint; 12 | 13 | // Add exact uints example. 14 | function uintExactAddOverflowExample() public pure { 15 | var n = uint(~0); 16 | n.exactAdd(1); 17 | } 18 | 19 | // Subtract exact uints example. 20 | function uintExactSubUnderflowExample() public pure { 21 | var n = uint(0); 22 | n.exactSub(1); 23 | } 24 | 25 | // Multiply exact uints example. 26 | function uintExactMulOverflowExample() public pure { 27 | var n = uint(~0); 28 | n.exactMul(2); 29 | } 30 | 31 | // Add exact ints example. 32 | function intExactAddOverflowExample() public pure { 33 | int n = 2**255 - 1; 34 | n.exactAdd(1); 35 | } 36 | 37 | // Add exact ints example. 38 | function intExactAddUnderflowExample() public pure { 39 | int n = int(2**255); 40 | n.exactAdd(-1); 41 | } 42 | 43 | // Subtract exact ints example. 44 | function intExactSubOverflowExample() public pure { 45 | var n = int(2**255 - 1); 46 | n.exactSub(-1); 47 | } 48 | 49 | // Subtract exact ints example. 50 | function intExactSubUnderflowExample() public pure { 51 | var n = int(2**255); 52 | n.exactSub(1); 53 | } 54 | 55 | // Multiply exact ints example. 56 | function intExactMulOverflowExample() public pure { 57 | var n = int(2**255 - 1); 58 | n.exactMul(2); 59 | } 60 | 61 | // Multiply exact ints example. 62 | function intExactMulUnderflowExample() public pure { 63 | var n = int(2**255); 64 | n.exactMul(2); 65 | } 66 | 67 | // Multiply exact ints example. 68 | function intExactMulIllegalUseOfIntMinExample() public pure { 69 | var n = int(2**255); 70 | n.exactMul(-1); 71 | } 72 | 73 | // Multiply exact ints example. 74 | function intExactMulIllegalUseOfIntMinExample2() public pure { 75 | var n = int(-1); 76 | n.exactMul(int(2**255)); 77 | } 78 | 79 | // Multiply exact ints example. 80 | function intExactDivIllegalUseOfIntMinExample() public pure { 81 | var n = int(2**255); 82 | n.exactDiv(-1); 83 | } 84 | } -------------------------------------------------------------------------------- /examples/patricia_tree/PatriciaTreeExamples.sol: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ethereum/solidity-examples/ba15d56b62cf384e983006bb139af59a03ce22dc/examples/patricia_tree/PatriciaTreeExamples.sol -------------------------------------------------------------------------------- /examples/strings/StringsExamples.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.4.16; 2 | pragma experimental "v0.5.0"; 3 | pragma experimental "ABIEncoderV2"; 4 | 5 | import {Strings} from "../src/strings/Strings.sol"; 6 | 7 | /* solhint-disable max-line-length */ 8 | 9 | contract StringsExamples { 10 | 11 | using Strings for string; 12 | 13 | function stringExampleValidateBrut() public pure { 14 | string memory str = "An preost wes on leoden, Laȝamon was ihoten He wes Leovenaðes sone -- liðe him be Drihten. He wonede at Ernleȝe at æðelen are chirechen, Uppen Sevarne staþe, sel þar him þuhte, Onfest Radestone, þer he bock radde."; 15 | str.validate(); 16 | } 17 | 18 | function stringExampleValidateOdysseusElytis() public pure { 19 | string memory str = "Τη γλώσσα μου έδωσαν ελληνική το σπίτι φτωχικό στις αμμουδιές του Ομήρου. Μονάχη έγνοια η γλώσσα μου στις αμμουδιές του Ομήρου. από το Άξιον Εστί του Οδυσσέα Ελύτη"; 20 | str.validate(); 21 | } 22 | 23 | function stringExampleValidatePushkinsHorseman() public pure { 24 | string memory str = "На берегу пустынных волн Стоял он, дум великих полн, И вдаль глядел. Пред ним широко Река неслася; бедный чёлн По ней стремился одиноко. По мшистым, топким берегам Чернели избы здесь и там, Приют убогого чухонца; И лес, неведомый лучам В тумане спрятанного солнца, Кругом шумел."; 25 | str.validate(); 26 | } 27 | 28 | function stringExampleValidateRunePoem() public pure { 29 | string memory str = "ᚠᛇᚻ᛫ᛒᛦᚦ᛫ᚠᚱᚩᚠᚢᚱ᛫ᚠᛁᚱᚪ᛫ᚷᛖᚻᚹᛦᛚᚳᚢᛗ ᛋᚳᛖᚪᛚ᛫ᚦᛖᚪᚻ᛫ᛗᚪᚾᚾᚪ᛫ᚷᛖᚻᚹᛦᛚᚳ᛫ᛗᛁᚳᛚᚢᚾ᛫ᚻᛦᛏ᛫ᛞᚫᛚᚪᚾ ᚷᛁᚠ᛫ᚻᛖ᛫ᚹᛁᛚᛖ᛫ᚠᚩᚱ᛫ᛞᚱᛁᚻᛏᚾᛖ᛫ᛞᚩᛗᛖᛋ᛫ᚻᛚᛇᛏᚪᚾ"; 30 | str.validate(); 31 | } 32 | 33 | } 34 | /* solhint-enable max-line-length */ -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "solidity-examples", 3 | "version": "0.1.0", 4 | "description": "Standard contracts", 5 | "bin": { 6 | "solstl": "./bin/main.js" 7 | }, 8 | "scripts": { 9 | "test": "jest && node ./bin/main.js tests --silent", 10 | "ts-lint": "tslint -p .", 11 | "ts-test": "jest", 12 | "contracts-lint": "solhint **/*.sol", 13 | "contracts-compile": "node ./bin/main.js compile", 14 | "contracts-test": "node ./bin/main.js tests", 15 | "contracts-perf": "node ./bin/main.js perf" 16 | }, 17 | "repository": { 18 | "type": "git", 19 | "url": "git+https://github.com/ethereum/solidity-examples.git" 20 | }, 21 | "author": "Andreas Olofsson", 22 | "license": "MIT", 23 | "bugs": { 24 | "url": "https://github.com/ethereum/solidity-examples/issues" 25 | }, 26 | "jest": { 27 | "moduleFileExtensions": [ 28 | "ts", 29 | "tsx", 30 | "js" 31 | ], 32 | "transform": { 33 | "\\.(ts|tsx)$": "/node_modules/ts-jest/preprocessor.js" 34 | } 35 | }, 36 | "homepage": "https://github.com/ethereum/solidity-examples#readme", 37 | "devDependencies": { 38 | "@types/jest": "^21.1.5", 39 | "@types/jest-matchers": "^20.0.0", 40 | "@types/mkdirp": "^0.5.1", 41 | "@types/node": "^8.0.47", 42 | "jest": "^21.2.1", 43 | "solhint": "^1.1.7", 44 | "ts-jest": "^21.1.4", 45 | "tslint": "^5.8.0", 46 | "typescript": "^2.6.1" 47 | }, 48 | "dependencies": { 49 | "chalk": "^2.3.0", 50 | "inquirer": "^3.3.0", 51 | "jsondiffpatch": "^0.2.5", 52 | "mkdirp": "^0.5.1" 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /perf/STLPerf.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.4.16; 2 | pragma experimental "v0.5.0"; 3 | pragma experimental "ABIEncoderV2"; 4 | 5 | 6 | // Extend for performance tests. Output of 'perf()' is the gas diff. 7 | // If buildup is required before gauging, override 'perf()' and do 8 | // gas reporting manually. 9 | // If no buildup is needed, just extend 'prefImpl()'. 10 | contract STLPerf { 11 | function perf() public payable returns (uint); 12 | } -------------------------------------------------------------------------------- /perf/bits/bits.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.4.16; 2 | pragma experimental "v0.5.0"; 3 | pragma experimental "ABIEncoderV2"; 4 | 5 | import {Bits} from "../../src/bits/Bits.sol"; 6 | import {STLPerf} from "../STLPerf.sol"; 7 | 8 | 9 | contract BitsPerf is STLPerf { 10 | using Bits for uint; 11 | } 12 | 13 | 14 | contract PerfBitsSetBit is BitsPerf { 15 | uint public res; 16 | 17 | function perf() public payable returns (uint) { 18 | uint n = 1; 19 | uint gasPre = msg.gas; 20 | var res_ = n.setBit(66); 21 | uint gasPost = msg.gas; 22 | res = res_; 23 | return gasPre - gasPost; 24 | } 25 | } 26 | 27 | 28 | contract PerfBitsClearBit is BitsPerf { 29 | uint public res; 30 | 31 | function perf() public payable returns (uint) { 32 | uint n = 1; 33 | uint gasPre = msg.gas; 34 | var res_ = n.clearBit(66); 35 | uint gasPost = msg.gas; 36 | res = res_; 37 | return gasPre - gasPost; 38 | } 39 | } 40 | 41 | 42 | contract PerfBitsToggleBit is BitsPerf { 43 | uint public res; 44 | 45 | function perf() public payable returns (uint) { 46 | uint n = 1; 47 | uint gasPre = msg.gas; 48 | var res_ = n.toggleBit(66); 49 | uint gasPost = msg.gas; 50 | res = res_; 51 | return gasPre - gasPost; 52 | } 53 | } 54 | 55 | 56 | contract PerfBitsBit is BitsPerf { 57 | uint public res; 58 | 59 | function perf() public payable returns (uint) { 60 | uint n = 1; 61 | uint gasPre = msg.gas; 62 | var res_ = n.bit(66); 63 | uint gasPost = msg.gas; 64 | res = res_; 65 | return gasPre - gasPost; 66 | } 67 | } 68 | 69 | 70 | contract PerfBitsBitSet is BitsPerf { 71 | bool public res; 72 | 73 | function perf() public payable returns (uint) { 74 | uint n = 1; 75 | uint gasPre = msg.gas; 76 | var res_ = n.bitSet(66); 77 | uint gasPost = msg.gas; 78 | res = res_; 79 | return gasPre - gasPost; 80 | } 81 | } 82 | 83 | 84 | contract PerfBitsBitEqual is BitsPerf { 85 | bool public res; 86 | 87 | function perf() public payable returns (uint) { 88 | uint n = 1; 89 | uint gasPre = msg.gas; 90 | var res_ = n.bitEqual(5, 66); 91 | uint gasPost = msg.gas; 92 | res = res_; 93 | return gasPre - gasPost; 94 | } 95 | } 96 | 97 | 98 | contract PerfBitsBitNot is BitsPerf { 99 | uint public res; 100 | 101 | function perf() public payable returns (uint) { 102 | uint n = 1; 103 | uint gasPre = msg.gas; 104 | var res_ = n.bitNot(66); 105 | uint gasPost = msg.gas; 106 | res = res_; 107 | return gasPre - gasPost; 108 | } 109 | } 110 | 111 | 112 | contract PerfBitsBitAnd is BitsPerf { 113 | uint public res; 114 | 115 | function perf() public payable returns (uint) { 116 | uint n = 1; 117 | uint gasPre = msg.gas; 118 | var res_ = n.bitAnd(0, 66); 119 | uint gasPost = msg.gas; 120 | res = res_; 121 | return gasPre - gasPost; 122 | } 123 | } 124 | 125 | 126 | contract PerfBitsBitOr is BitsPerf { 127 | uint public res; 128 | 129 | function perf() public payable returns (uint) { 130 | uint n = 1; 131 | uint gasPre = msg.gas; 132 | var res_ = n.bitOr(0, 66); 133 | uint gasPost = msg.gas; 134 | res = res_; 135 | return gasPre - gasPost; 136 | } 137 | } 138 | 139 | 140 | contract PerfBitsBitXor is BitsPerf { 141 | uint public res; 142 | 143 | function perf() public payable returns (uint) { 144 | uint n = 1; 145 | uint gasPre = msg.gas; 146 | var res_ = n.bitXor(0, 66); 147 | uint gasPost = msg.gas; 148 | res = res_; 149 | return gasPre - gasPost; 150 | } 151 | } 152 | 153 | 154 | contract PerfBitsBits is BitsPerf { 155 | uint public res; 156 | 157 | function perf() public payable returns (uint) { 158 | uint n = 1; 159 | uint gasPre = msg.gas; 160 | var res_ = n.bits(5, 66); 161 | uint gasPost = msg.gas; 162 | res = res_; 163 | return gasPre - gasPost; 164 | } 165 | } 166 | 167 | 168 | contract PerfBitsHighestBitSetLow is BitsPerf { 169 | uint public res; 170 | 171 | function perf() public payable returns (uint) { 172 | uint n = 1; 173 | uint gasPre = msg.gas; 174 | var res_ = n.highestBitSet(); 175 | uint gasPost = msg.gas; 176 | res = res_; 177 | return gasPre - gasPost; 178 | } 179 | } 180 | 181 | 182 | contract PerfBitsHighestBitSetHigh is BitsPerf { 183 | uint public res; 184 | 185 | function perf() public payable returns (uint) { 186 | uint n = uint(1) << uint(255); 187 | uint gasPre = msg.gas; 188 | var res_ = n.highestBitSet(); 189 | uint gasPost = msg.gas; 190 | res = res_; 191 | return gasPre - gasPost; 192 | } 193 | } 194 | 195 | 196 | contract PerfBitsLowestBitSetLow is BitsPerf { 197 | uint public res; 198 | 199 | function perf() public payable returns (uint) { 200 | uint n = 1; 201 | uint gasPre = msg.gas; 202 | var res_ = n.lowestBitSet(); 203 | uint gasPost = msg.gas; 204 | res = res_; 205 | return gasPre - gasPost; 206 | } 207 | } 208 | 209 | 210 | contract PerfBitsLowestBitSetHigh is BitsPerf { 211 | uint public res; 212 | 213 | function perf() public payable returns (uint) { 214 | uint n = uint(1) << uint(255); 215 | uint gasPre = msg.gas; 216 | var res_ = n.lowestBitSet(); 217 | uint gasPost = msg.gas; 218 | res = res_; 219 | return gasPre - gasPost; 220 | } 221 | } 222 | -------------------------------------------------------------------------------- /perf/math/exact_math.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.4.16; 2 | pragma experimental "v0.5.0"; 3 | pragma experimental "ABIEncoderV2"; 4 | 5 | import {ExactMath} from "../../src/math/ExactMath.sol"; 6 | import {STLPerf} from "../STLPerf.sol"; 7 | 8 | 9 | contract PerfMathExactAddUint is STLPerf { 10 | uint public res; 11 | 12 | function perf() public payable returns (uint) { 13 | uint n = 5; 14 | uint m = 2; 15 | uint gasPre = msg.gas; 16 | var res_ = ExactMath.exactAdd(n, m); 17 | uint gasPost = msg.gas; 18 | res = res_; 19 | return gasPre - gasPost; 20 | } 21 | } 22 | 23 | 24 | contract PerfMathExactSubUint is STLPerf { 25 | uint public res; 26 | 27 | function perf() public payable returns (uint) { 28 | uint n = 5; 29 | uint m = 2; 30 | uint gasPre = msg.gas; 31 | var res_ = ExactMath.exactSub(n, m); 32 | uint gasPost = msg.gas; 33 | res = res_; 34 | return gasPre - gasPost; 35 | } 36 | } 37 | 38 | 39 | contract PerfMathExactMulUint is STLPerf { 40 | uint public res; 41 | 42 | function perf() public payable returns (uint) { 43 | uint n = 5; 44 | uint m = 2; 45 | uint gasPre = msg.gas; 46 | var res_ = ExactMath.exactMul(n, m); 47 | uint gasPost = msg.gas; 48 | res = res_; 49 | return gasPre - gasPost; 50 | } 51 | } 52 | 53 | 54 | contract PerfMathExactAddInt is STLPerf { 55 | int public res; 56 | 57 | function perf() public payable returns (uint) { 58 | int n = 5; 59 | int m = 2; 60 | uint gasPre = msg.gas; 61 | var res_ = ExactMath.exactAdd(n, m); 62 | uint gasPost = msg.gas; 63 | res = res_; 64 | return gasPre - gasPost; 65 | } 66 | } 67 | 68 | 69 | contract PerfMathExactSubInt is STLPerf { 70 | int public res; 71 | 72 | function perf() public payable returns (uint) { 73 | int n = 5; 74 | int m = 2; 75 | uint gasPre = msg.gas; 76 | var res_ = ExactMath.exactSub(n, m); 77 | uint gasPost = msg.gas; 78 | res = res_; 79 | return gasPre - gasPost; 80 | } 81 | } 82 | 83 | 84 | contract PerfMathExactMulInt is STLPerf { 85 | int public res; 86 | 87 | function perf() public payable returns (uint) { 88 | int n = 5; 89 | int m = 2; 90 | uint gasPre = msg.gas; 91 | var res_ = ExactMath.exactMul(n, m); 92 | uint gasPost = msg.gas; 93 | res = res_; 94 | return gasPre - gasPost; 95 | } 96 | } 97 | 98 | 99 | contract PerfMathExactDivInt is STLPerf { 100 | int public res; 101 | 102 | function perf() public payable returns (uint) { 103 | int n = 5; 104 | int m = 2; 105 | uint gasPre = msg.gas; 106 | var res_ = ExactMath.exactDiv(n, m); 107 | uint gasPost = msg.gas; 108 | res = res_; 109 | return gasPre - gasPost; 110 | } 111 | } -------------------------------------------------------------------------------- /perf/patricia_tree/data.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.4.16; 2 | pragma experimental "v0.5.0"; 3 | pragma experimental "ABIEncoderV2"; 4 | 5 | import {Data} from "../../src/patricia_tree/Data.sol"; 6 | import {STLPerf} from "../STLPerf.sol"; 7 | 8 | 9 | contract PatriciaTreeDataPerf is STLPerf { 10 | using Data for Data.Node; 11 | using Data for Data.Edge; 12 | using Data for Data.Label; 13 | 14 | uint internal constant MAX_LENGTH = 256; 15 | uint internal constant UINT256_ZEROES = 0; 16 | uint internal constant UINT256_ONES = ~uint(0); 17 | 18 | bytes32 internal constant B32_ZEROES = bytes32(UINT256_ZEROES); 19 | bytes32 internal constant B32_ONES = bytes32(UINT256_ONES); 20 | } 21 | 22 | 23 | contract PerfPatriciaTreeDataChopFirstBit is PatriciaTreeDataPerf { 24 | function perf() public payable returns (uint) { 25 | var lbl = Data.Label(0, 1); 26 | uint gasPre = msg.gas; 27 | lbl.chopFirstBit(); 28 | uint gasPost = msg.gas; 29 | return gasPre - gasPost; 30 | } 31 | } 32 | 33 | 34 | contract PerfPatriciaTreeDataRemovePrefix is PatriciaTreeDataPerf { 35 | function perf() public payable returns (uint) { 36 | var lbl = Data.Label(0, 5); 37 | uint gasPre = msg.gas; 38 | lbl.removePrefix(5); 39 | uint gasPost = msg.gas; 40 | return gasPre - gasPost; 41 | } 42 | } 43 | 44 | 45 | contract PerfPatriciaTreeDataCommonPrefixLengthZero is PatriciaTreeDataPerf { 46 | function perf() public payable returns (uint) { 47 | var lbl = Data.Label(B32_ZEROES, 0); 48 | uint gasPre = msg.gas; 49 | lbl.commonPrefix(lbl); 50 | uint gasPost = msg.gas; 51 | return gasPre - gasPost; 52 | } 53 | } 54 | 55 | 56 | contract PerfPatriciaTreeDataCommonPrefixNoMatch is PatriciaTreeDataPerf { 57 | function perf() public payable returns (uint) { 58 | var lbl = Data.Label(B32_ZEROES, 256); 59 | var lbl2 = Data.Label(B32_ONES, 256); 60 | uint gasPre = msg.gas; 61 | lbl.commonPrefix(lbl2); 62 | uint gasPost = msg.gas; 63 | return gasPre - gasPost; 64 | } 65 | } 66 | 67 | 68 | contract PerfPatriciaTreeDataCommonPrefixFullMatch is PatriciaTreeDataPerf { 69 | function perf() public payable returns (uint) { 70 | var lbl = Data.Label(B32_ZEROES, 256); 71 | uint gasPre = msg.gas; 72 | lbl.commonPrefix(lbl); 73 | uint gasPost = msg.gas; 74 | return gasPre - gasPost; 75 | } 76 | } 77 | 78 | 79 | contract PerfPatriciaTreeDataSplitAtPositionZero is PatriciaTreeDataPerf { 80 | function perf() public payable returns (uint) { 81 | var lbl = Data.Label(B32_ZEROES, 256); 82 | uint gasPre = msg.gas; 83 | lbl.splitAt(0); 84 | uint gasPost = msg.gas; 85 | return gasPre - gasPost; 86 | } 87 | } 88 | 89 | 90 | contract PerfPatriciaTreeDataSplitAt is PatriciaTreeDataPerf { 91 | function perf() public payable returns (uint) { 92 | var lbl = Data.Label(B32_ZEROES, 256); 93 | uint gasPre = msg.gas; 94 | lbl.splitAt(55); 95 | uint gasPost = msg.gas; 96 | return gasPre - gasPost; 97 | } 98 | } 99 | 100 | 101 | contract PerfPatriciaTreeDataSplitCommonPrefix is PatriciaTreeDataPerf { 102 | function perf() public payable returns (uint) { 103 | var lbl = Data.Label(B32_ZEROES, 256); 104 | var lbl2 = Data.Label(0x1111, 256); 105 | uint gasPre = msg.gas; 106 | lbl.splitCommonPrefix(lbl2); 107 | uint gasPost = msg.gas; 108 | return gasPre - gasPost; 109 | } 110 | } -------------------------------------------------------------------------------- /perf/patricia_tree/patricia_tree.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.4.16; 2 | pragma experimental "v0.5.0"; 3 | pragma experimental "ABIEncoderV2"; 4 | 5 | import {Data} from "../../src/patricia_tree/Data.sol"; 6 | import {PatriciaTree} from "../../src/patricia_tree/PatriciaTree.sol"; 7 | import {STLPerf} from "../STLPerf.sol"; 8 | 9 | 10 | contract PatriciaTreeDataPerf is STLPerf { 11 | using Data for Data.Tree; 12 | using Data for Data.Node; 13 | using Data for Data.Edge; 14 | using Data for Data.Label; 15 | 16 | uint internal constant MAX_LENGTH = 256; 17 | uint internal constant UINT256_ZEROES = 0; 18 | uint internal constant UINT256_ONES = ~uint(0); 19 | 20 | bytes32 internal constant B32_ZEROES = bytes32(UINT256_ZEROES); 21 | bytes32 internal constant B32_ONES = bytes32(UINT256_ONES); 22 | } 23 | 24 | 25 | contract PerfPatriciaTreeInsertIntoEmpty is PatriciaTreeDataPerf { 26 | function perf() public payable returns (uint) { 27 | var pt = new PatriciaTree(); 28 | uint gasPre = msg.gas; 29 | pt.insert("val", "VAL"); 30 | uint gasPost = msg.gas; 31 | return gasPre - gasPost; 32 | } 33 | } 34 | 35 | 36 | contract PerfPatriciaTreeInsertAfterRoot is PatriciaTreeDataPerf { 37 | function perf() public payable returns (uint) { 38 | var pt = new PatriciaTree(); 39 | pt.insert("val", "VAL"); 40 | uint gasPre = msg.gas; 41 | pt.insert("val2", "VAL2"); 42 | uint gasPost = msg.gas; 43 | return gasPre - gasPost; 44 | } 45 | } 46 | 47 | 48 | contract PerfPatriciaTreeInsertReplaceRoot is PatriciaTreeDataPerf { 49 | function perf() public payable returns (uint) { 50 | var pt = new PatriciaTree(); 51 | pt.insert("val", "VAL"); 52 | uint gasPre = msg.gas; 53 | pt.insert("val", "VAL2"); 54 | uint gasPost = msg.gas; 55 | return gasPre - gasPost; 56 | } 57 | } 58 | 59 | 60 | contract PerfPatriciaTreeInsertReplaceAfterRoot is PatriciaTreeDataPerf { 61 | function perf() public payable returns (uint) { 62 | var pt = new PatriciaTree(); 63 | pt.insert("val", "VAL"); 64 | pt.insert("val2", "VAL2"); 65 | uint gasPre = msg.gas; 66 | pt.insert("val2", "VAL3"); 67 | uint gasPost = msg.gas; 68 | return gasPre - gasPost; 69 | } 70 | } 71 | 72 | 73 | contract PerfPatriciaTreeGetProofRoot is PatriciaTreeDataPerf { 74 | function perf() public payable returns (uint) { 75 | var pt = new PatriciaTree(); 76 | pt.insert("val", "VAL"); 77 | uint gasPre = msg.gas; 78 | pt.getProof("val"); 79 | uint gasPost = msg.gas; 80 | return gasPre - gasPost; 81 | } 82 | } 83 | 84 | 85 | contract PerfPatriciaTreeGetProofSecondAfterRoot is PatriciaTreeDataPerf { 86 | function perf() public payable returns (uint) { 87 | var pt = new PatriciaTree(); 88 | pt.insert("val", "VAL"); 89 | pt.insert("val2", "VAL2"); 90 | uint gasPre = msg.gas; 91 | pt.getProof("val2"); 92 | uint gasPost = msg.gas; 93 | return gasPre - gasPost; 94 | } 95 | } 96 | 97 | 98 | contract PerfPatriciaTreeValidateProofRootOnlyNode is PatriciaTreeDataPerf, PatriciaTree { 99 | function perf() public payable returns (uint) { 100 | insert("val", "VAL"); 101 | var (mask, siblings) = getProof("val"); 102 | uint gasPre = msg.gas; 103 | verifyProof(tree.root, "val", "VAL", mask, siblings); 104 | uint gasPost = msg.gas; 105 | return gasPre - gasPost; 106 | } 107 | } 108 | 109 | 110 | contract PerfPatriciaTreeEdgeHash is PatriciaTreeDataPerf, PatriciaTree { 111 | function perf() public payable returns (uint) { 112 | var e = Data.Edge(0, Data.Label(0, 1)); 113 | uint gasPre = msg.gas; 114 | e.edgeHash(); 115 | uint gasPost = msg.gas; 116 | return gasPre - gasPost; 117 | } 118 | } -------------------------------------------------------------------------------- /perf/strings/strings.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.4.16; 2 | pragma experimental "v0.5.0"; 3 | pragma experimental "ABIEncoderV2"; 4 | 5 | import {Strings} from "../../src/strings/Strings.sol"; 6 | import {STLPerf} from "../STLPerf.sol"; 7 | import {Memory} from "../../src/unsafe/Memory.sol"; 8 | 9 | /* solhint-disable max-line-length */ 10 | 11 | contract StringsPerf is STLPerf { 12 | using Strings for string; 13 | using Strings for uint; 14 | } 15 | 16 | 17 | contract PerfStringsParseRuneLengthOne is StringsPerf { 18 | 19 | function perf() public payable returns (uint) { 20 | bytes memory bts = hex"00"; 21 | uint addr = Memory.dataPtr(bts); 22 | uint gasPre = msg.gas; 23 | addr.parseRune(); 24 | uint gasPost = msg.gas; 25 | return gasPre - gasPost; 26 | } 27 | } 28 | 29 | 30 | contract PerfStringsParseRuneLengthTwo is StringsPerf { 31 | 32 | function perf() public payable returns (uint) { 33 | bytes memory bts = hex"DF80"; 34 | uint addr = Memory.dataPtr(bts); 35 | uint gasPre = msg.gas; 36 | addr.parseRune(); 37 | uint gasPost = msg.gas; 38 | return gasPre - gasPost; 39 | } 40 | } 41 | 42 | 43 | contract PerfStringsParseRuneLengthThree is StringsPerf { 44 | 45 | function perf() public payable returns (uint) { 46 | bytes memory bts = hex"E0A080"; 47 | uint addr = Memory.dataPtr(bts); 48 | uint gasPre = msg.gas; 49 | addr.parseRune(); 50 | uint gasPost = msg.gas; 51 | return gasPre - gasPost; 52 | } 53 | } 54 | 55 | 56 | contract PerfStringsParseRuneLengthFour is StringsPerf { 57 | 58 | function perf() public payable returns (uint) { 59 | bytes memory bts = hex"F0908080"; 60 | uint addr = Memory.dataPtr(bts); 61 | uint gasPre = msg.gas; 62 | addr.parseRune(); 63 | uint gasPost = msg.gas; 64 | return gasPre - gasPost; 65 | } 66 | } 67 | 68 | 69 | contract PerfStringsParseValidateQuickBrownFox is StringsPerf { 70 | 71 | function perf() public payable returns (uint) { 72 | string memory str = "The quick brown fox jumps over the lazy dog"; 73 | uint gasPre = msg.gas; 74 | str.validate(); 75 | uint gasPost = msg.gas; 76 | return gasPre - gasPost; 77 | } 78 | } 79 | 80 | 81 | contract PerfStringsParseValidateRunePoem is StringsPerf { 82 | 83 | function perf() public payable returns (uint) { 84 | string memory str = "ᚠᛇᚻ᛫ᛒᛦᚦ᛫ᚠᚱᚩᚠᚢᚱ᛫ᚠᛁᚱᚪ᛫ᚷᛖᚻᚹᛦᛚᚳᚢᛗ ᛋᚳᛖᚪᛚ᛫ᚦᛖᚪᚻ᛫ᛗᚪᚾᚾᚪ᛫ᚷᛖᚻᚹᛦᛚᚳ᛫ᛗᛁᚳᛚᚢᚾ᛫ᚻᛦᛏ᛫ᛞᚫᛚᚪᚾ ᚷᛁᚠ᛫ᚻᛖ᛫ᚹᛁᛚᛖ᛫ᚠᚩᚱ᛫ᛞᚱᛁᚻᛏᚾᛖ᛫ᛞᚩᛗᛖᛋ᛫ᚻᛚᛇᛏᚪᚾ"; 85 | uint gasPre = msg.gas; 86 | str.validate(); 87 | uint gasPost = msg.gas; 88 | return gasPre - gasPost; 89 | } 90 | } 91 | 92 | 93 | contract PerfStringsParseValidateBrut is StringsPerf { 94 | 95 | function perf() public payable returns (uint) { 96 | string memory str = "An preost wes on leoden, Laȝamon was ihoten He wes Leovenaðes sone -- liðe him be Drihten. He wonede at Ernleȝe at æðelen are chirechen, Uppen Sevarne staþe, sel þar him þuhte, Onfest Radestone, þer he bock radde."; 97 | uint gasPre = msg.gas; 98 | str.validate(); 99 | uint gasPost = msg.gas; 100 | return gasPre - gasPost; 101 | } 102 | } 103 | /* solhint-enable max-line-length */ -------------------------------------------------------------------------------- /script/compile.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { 3 | return new (P || (P = Promise))(function (resolve, reject) { 4 | function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } 5 | function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } 6 | function step(result) { result.done ? resolve(result.value) : new P(function (resolve) { resolve(result.value); }).then(fulfilled, rejected); } 7 | step((generator = generator.apply(thisArg, _arguments || [])).next()); 8 | }); 9 | }; 10 | var __generator = (this && this.__generator) || function (thisArg, body) { 11 | var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g; 12 | return g = { next: verb(0), "throw": verb(1), "return": verb(2) }, typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g; 13 | function verb(n) { return function (v) { return step([n, v]); }; } 14 | function step(op) { 15 | if (f) throw new TypeError("Generator is already executing."); 16 | while (_) try { 17 | if (f = 1, y && (t = y[op[0] & 2 ? "return" : op[0] ? "throw" : "next"]) && !(t = t.call(y, op[1])).done) return t; 18 | if (y = 0, t) op = [0, t.value]; 19 | switch (op[0]) { 20 | case 0: case 1: t = op; break; 21 | case 4: _.label++; return { value: op[1], done: false }; 22 | case 5: _.label++; y = op[1]; op = [0]; continue; 23 | case 7: op = _.ops.pop(); _.trys.pop(); continue; 24 | default: 25 | if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; } 26 | if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; } 27 | if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; } 28 | if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; } 29 | if (t[2]) _.ops.pop(); 30 | _.trys.pop(); continue; 31 | } 32 | op = body.call(thisArg, _); 33 | } catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; } 34 | if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true }; 35 | } 36 | }; 37 | var _this = this; 38 | Object.defineProperty(exports, "__esModule", { value: true }); 39 | var io_1 = require("./utils/io"); 40 | var constants_1 = require("./constants"); 41 | var solc_1 = require("./exec/solc"); 42 | var data_reader_1 = require("./utils/data_reader"); 43 | exports.compileAll = function () { return __awaiter(_this, void 0, void 0, function () { 44 | var units, _i, units_1, unit; 45 | return __generator(this, function (_a) { 46 | switch (_a.label) { 47 | case 0: 48 | io_1.ensureAndClear(constants_1.BIN_OUTPUT_PATH); 49 | units = data_reader_1.getAllContractFiles(); 50 | _i = 0, units_1 = units; 51 | _a.label = 1; 52 | case 1: 53 | if (!(_i < units_1.length)) return [3 /*break*/, 4]; 54 | unit = units_1[_i]; 55 | return [4 /*yield*/, solc_1.compileUnit(unit[0], unit[1], true)]; 56 | case 2: 57 | _a.sent(); 58 | _a.label = 3; 59 | case 3: 60 | _i++; 61 | return [3 /*break*/, 1]; 62 | case 4: return [2 /*return*/]; 63 | } 64 | }); 65 | }); }; 66 | exports.compile = function (units) { return __awaiter(_this, void 0, void 0, function () { 67 | var _i, units_2, unit; 68 | return __generator(this, function (_a) { 69 | switch (_a.label) { 70 | case 0: 71 | io_1.ensureAndClear(constants_1.BIN_OUTPUT_PATH); 72 | _i = 0, units_2 = units; 73 | _a.label = 1; 74 | case 1: 75 | if (!(_i < units_2.length)) return [3 /*break*/, 4]; 76 | unit = units_2[_i]; 77 | return [4 /*yield*/, solc_1.compileUnit(unit[0], unit[1], true)]; 78 | case 2: 79 | _a.sent(); 80 | _a.label = 3; 81 | case 3: 82 | _i++; 83 | return [3 /*break*/, 1]; 84 | case 4: return [2 /*return*/]; 85 | } 86 | }); 87 | }); }; 88 | -------------------------------------------------------------------------------- /script/compile.ts: -------------------------------------------------------------------------------- 1 | import {ensureAndClear} from "./utils/io"; 2 | import {BIN_OUTPUT_PATH} from "./constants"; 3 | import {compileUnit} from "./exec/solc"; 4 | import {getAllContractFiles} from "./utils/data_reader"; 5 | 6 | export const compileAll = async (): Promise => { 7 | ensureAndClear(BIN_OUTPUT_PATH); 8 | const units = getAllContractFiles(); 9 | for (const unit of units) { 10 | await compileUnit(unit[0], unit[1], true); 11 | } 12 | }; 13 | 14 | export const compile = async (units: Array<[string, string]>): Promise => { 15 | ensureAndClear(BIN_OUTPUT_PATH); 16 | for (const unit of units) { 17 | await compileUnit(unit[0], unit[1], true); 18 | } 19 | }; 20 | -------------------------------------------------------------------------------- /script/constants.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | Object.defineProperty(exports, "__esModule", { value: true }); 3 | var path = require("path"); 4 | // Paths. 5 | exports.ROOT_PATH = path.join(__dirname, '..'); 6 | exports.SRC_PATH = path.join(exports.ROOT_PATH, 'src'); 7 | exports.LOGS_PATH = path.join(exports.ROOT_PATH, 'logs'); 8 | exports.BIN_OUTPUT_PATH = path.join(exports.ROOT_PATH, 'bin_output'); 9 | exports.TEST_CONTRACT_PATH = path.join(exports.ROOT_PATH, 'test'); 10 | exports.TEST_LOGS_PATH = path.join(exports.LOGS_PATH, 'test'); 11 | exports.PERF_CONTRACT_PATH = path.join(exports.ROOT_PATH, 'perf'); 12 | exports.PERF_LOGS_PATH = path.join(exports.LOGS_PATH, 'perf'); 13 | exports.EXAMPLES_PATH = path.join(exports.ROOT_PATH, 'examples'); 14 | exports.DATA_PATH = path.join(exports.ROOT_PATH, 'data'); 15 | exports.DOCS_PATH = path.join(exports.ROOT_PATH, 'docs'); 16 | exports.PACKAGE_DOCS_PATH = path.join(exports.DOCS_PATH, 'packages'); 17 | // Function hashes. 18 | exports.TEST_FUN_HASH = 'f8a8fd6d'; 19 | exports.PERF_FUN_HASH = '1c4af786'; 20 | // Constant file IDs. 21 | exports.DATA_FILE = path.join(exports.DATA_PATH, 'data.json'); 22 | exports.RESULTS_NAME_OPTIMIZED = "results_optimized.json"; 23 | exports.RESULTS_NAME_UNOPTIMIZED = "results_unoptimized.json"; 24 | -------------------------------------------------------------------------------- /script/constants.ts: -------------------------------------------------------------------------------- 1 | import path = require('path'); 2 | 3 | // Paths. 4 | export const ROOT_PATH = path.join(__dirname, '..'); 5 | export const SRC_PATH = path.join(ROOT_PATH, 'src'); 6 | export const LOGS_PATH = path.join(ROOT_PATH, 'logs'); 7 | export const BIN_OUTPUT_PATH = path.join(ROOT_PATH, 'bin_output'); 8 | 9 | export const TEST_CONTRACT_PATH = path.join(ROOT_PATH, 'test'); 10 | export const TEST_LOGS_PATH = path.join(LOGS_PATH, 'test'); 11 | 12 | export const PERF_CONTRACT_PATH = path.join(ROOT_PATH, 'perf'); 13 | export const PERF_LOGS_PATH = path.join(LOGS_PATH, 'perf'); 14 | 15 | export const EXAMPLES_PATH = path.join(ROOT_PATH, 'examples'); 16 | 17 | export const DATA_PATH = path.join(ROOT_PATH, 'data'); 18 | 19 | export const DOCS_PATH = path.join(ROOT_PATH, 'docs'); 20 | export const PACKAGE_DOCS_PATH = path.join(DOCS_PATH, 'packages'); 21 | 22 | // Function hashes. 23 | export const TEST_FUN_HASH = 'f8a8fd6d'; 24 | export const PERF_FUN_HASH = '1c4af786'; 25 | 26 | // Constant file IDs. 27 | export const DATA_FILE = path.join(DATA_PATH, 'data.json'); 28 | export const RESULTS_NAME_OPTIMIZED = "results_optimized.json"; 29 | export const RESULTS_NAME_UNOPTIMIZED = "results_unoptimized.json"; 30 | -------------------------------------------------------------------------------- /script/exec/evm.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | Object.defineProperty(exports, "__esModule", { value: true }); 3 | var child = require("child_process"); 4 | var execSync = child.execSync; 5 | exports.run = function (file, input) { 6 | var cmd = "evm --codefile " + file + " --input " + input + " run"; 7 | var ret = execSync(cmd); 8 | if (ret === null) { 9 | throw new Error("Failed when running command: " + cmd); 10 | } 11 | if (ret === null) { 12 | throw new Error("Failed when running command: " + cmd); 13 | } 14 | var retStr = ret.toString(); 15 | if (retStr.length === 0) { 16 | throw new Error("Failed when running command: " + cmd); 17 | } 18 | var res = retStr.substring(0, retStr.indexOf('\n')).trim(); 19 | return res === '0x' ? '0' : res.substr(2); 20 | }; 21 | exports.version = function () { 22 | return execSync('evm --version').toString().trim(); 23 | }; 24 | -------------------------------------------------------------------------------- /script/exec/evm.ts: -------------------------------------------------------------------------------- 1 | import * as child from 'child_process'; 2 | const execSync = child.execSync; 3 | 4 | export const run = (file: string, input: string): string => { 5 | const cmd = `evm --codefile ${file} --input ${input} run`; 6 | const ret = execSync(cmd); 7 | if (ret === null) { 8 | throw new Error(`Failed when running command: ${cmd}`); 9 | } 10 | if (ret === null) { 11 | throw new Error(`Failed when running command: ${cmd}`); 12 | } 13 | const retStr = ret.toString(); 14 | if (retStr.length === 0) { 15 | throw new Error(`Failed when running command: ${cmd}`); 16 | } 17 | const res = retStr.substring(0, retStr.indexOf('\n')).trim(); 18 | return res === '0x' ? '0' : res.substr(2); 19 | }; 20 | 21 | export const version = (): string => { 22 | return execSync('evm --version').toString().trim(); 23 | }; 24 | -------------------------------------------------------------------------------- /script/exec/solc.ts: -------------------------------------------------------------------------------- 1 | import * as path from 'path'; 2 | import * as child from 'child_process'; 3 | import { 4 | BIN_OUTPUT_PATH, PERF_CONTRACT_PATH, ROOT_PATH, SRC_PATH, TEST_CONTRACT_PATH 5 | } from "../constants"; 6 | import Logger from "../utils/logger"; 7 | import {getAllContractFiles, getAllPerfFiles, getAllTestFiles} from "../utils/data_reader"; 8 | 9 | const exec = child.exec; 10 | const execSync = child.execSync; 11 | 12 | export const compileTests = async (extended: boolean, optimize: boolean) => { 13 | const units = getAllTestFiles(extended); 14 | for (const test of units) { 15 | await compileTest(test[0], test[1], optimize); 16 | } 17 | }; 18 | 19 | export const compileTest = async (subdir: string, test: string, optimize: boolean = true) => { 20 | Logger.info(`Compiling unit: ${subdir}/${test}`); 21 | const filePath = path.join(TEST_CONTRACT_PATH, subdir, test); 22 | await compile(filePath, BIN_OUTPUT_PATH, optimize); 23 | Logger.info(`Done`); 24 | }; 25 | 26 | export const compilePerfs = async (extended: boolean, optimize: boolean) => { 27 | const units = getAllPerfFiles(extended); 28 | for (const test of units) { 29 | await compilePerf(test[0], test[1], optimize); 30 | } 31 | }; 32 | 33 | export const compilePerf = async (subdir: string, perf: string, optimize: boolean = true) => { 34 | Logger.info(`Compiling unit: ${subdir}/${perf}`); 35 | const filePath = path.join(PERF_CONTRACT_PATH, subdir, perf); 36 | await compile(filePath, BIN_OUTPUT_PATH, optimize); 37 | Logger.info(`Done`); 38 | }; 39 | 40 | export const compileUnits = async (optimize: boolean = true) => { 41 | const units = getAllContractFiles(); 42 | for (const test of units) { 43 | await compileUnit(test[0], test[1], optimize); 44 | } 45 | }; 46 | 47 | export const compileUnit = async (subdir: string, contract: string, optimize: boolean = true) => { 48 | Logger.info(`Compiling unit: ${subdir}/${contract}`); 49 | const filePath = path.join(SRC_PATH, subdir, contract); 50 | await compile(filePath, BIN_OUTPUT_PATH, optimize); 51 | Logger.info(`Done`); 52 | }; 53 | 54 | export const compile = async (filePath: string, outDir: string, optimize: boolean) => { 55 | return new Promise((resolve, reject) => { 56 | const cmd = `solc .= --bin-runtime --hashes --metadata --overwrite ${optimize ? "--optimize" : ""} -o ${outDir} ${filePath}`; 57 | exec(cmd, {cwd: ROOT_PATH}, (err, stdoud, stderr) => { 58 | const ret = stderr.toString(); 59 | Logger.debug(ret); 60 | if (err) { 61 | reject(err); 62 | } else { 63 | resolve(); 64 | } 65 | }); 66 | }); 67 | }; 68 | 69 | export function version(): string { 70 | const verStr = execSync('solc --version').toString(); 71 | return verStr.substr(verStr.indexOf('\n')).substr(9).trim(); 72 | } 73 | -------------------------------------------------------------------------------- /script/perf.ts: -------------------------------------------------------------------------------- 1 | import * as fs from 'fs'; 2 | import * as path from 'path'; 3 | import {BIN_OUTPUT_PATH, PERF_FUN_HASH, PERF_LOGS_PATH, RESULTS_NAME_OPTIMIZED, RESULTS_NAME_UNOPTIMIZED} from "./constants"; 4 | import { 5 | createTimestampSubfolder, ensureAndClear, isSigInHashes, writeLatest, writeLog 6 | } from "./utils/io"; 7 | import {compilePerf, version as solcVersion} from "./exec/solc"; 8 | import {run, version as evmVersion} from './exec/evm'; 9 | import TestLogger from "./utils/test_logger"; 10 | 11 | export const perf = async (units: Array<[string, string]>, optAndUnopt: boolean = false) => { 12 | // Set up paths and check versions of the required tools. 13 | const solcV = solcVersion(); 14 | const evmV = evmVersion(); 15 | ensureAndClear(BIN_OUTPUT_PATH); 16 | 17 | const ret = await compileAndRunPerf(units, optAndUnopt); 18 | 19 | const log = { 20 | solcVersion: solcV, 21 | evmVersion: evmV, 22 | results: ret 23 | }; 24 | 25 | const logsPath = createTimestampSubfolder(PERF_LOGS_PATH); 26 | writeLog(log, logsPath, RESULTS_NAME_OPTIMIZED); 27 | 28 | // If unoptimized is enabled. 29 | if (optAndUnopt) { 30 | ensureAndClear(BIN_OUTPUT_PATH); 31 | const retU = await compileAndRunPerf(units, false); 32 | const logU = { 33 | solcVersion: solcV, 34 | evmVersion: evmV, 35 | results: retU 36 | }; 37 | writeLog(logU, logsPath, RESULTS_NAME_UNOPTIMIZED); 38 | } 39 | 40 | writeLatest(PERF_LOGS_PATH, logsPath); 41 | }; 42 | 43 | export const compileAndRunPerf = async (units: Array<[string, string]>, optimize: boolean) => { 44 | for (const unit of units) { 45 | const subDir = unit[0]; 46 | const prf = unit[1]; 47 | if (prf === '') { 48 | continue; 49 | } 50 | await compilePerf(subDir, prf, optimize); 51 | } 52 | return runPerf(); 53 | }; 54 | 55 | export const runPerf = () => { 56 | const files = fs.readdirSync(BIN_OUTPUT_PATH); 57 | const sigfiles = files.filter((file) => { 58 | const f = file.trim(); 59 | return f.length > 4 && f.substr(0, 4) === 'Perf' && f.split('.').pop() === 'signatures'; 60 | }); 61 | const results = {}; 62 | TestLogger.header("Running perf..."); 63 | for (const sigfile of sigfiles) { 64 | if (!isSigInHashes(BIN_OUTPUT_PATH, sigfile, PERF_FUN_HASH)) { 65 | throw new Error(`No perf function in signature file: ${sigfile}`); 66 | } 67 | const name = sigfile.substr(0, sigfile.length - 11); 68 | const binRuntimePath = path.join(BIN_OUTPUT_PATH, name + ".bin-runtime"); 69 | const result = run(binRuntimePath, PERF_FUN_HASH); 70 | const gasUsed = parseData(result); 71 | results[name] = {gasUsed}; 72 | } 73 | TestLogger.header("Done"); 74 | return results; 75 | }; 76 | 77 | const parseData = (output: string): number => parseInt(output, 16); 78 | -------------------------------------------------------------------------------- /script/tests.ts: -------------------------------------------------------------------------------- 1 | import * as fs from 'fs'; 2 | import * as path from 'path'; 3 | import { 4 | RESULTS_NAME_OPTIMIZED, RESULTS_NAME_UNOPTIMIZED, BIN_OUTPUT_PATH, TEST_FUN_HASH, TEST_LOGS_PATH 5 | } from "./constants"; 6 | import {createTimestampSubfolder, ensureAndClear, isSigInHashes, writeLatest, writeLog} from "./utils/io"; 7 | import {compileTest, version as solcVersion} from "./exec/solc"; 8 | import {run, version as evmVersion} from "./exec/evm"; 9 | import TestLogger from "./utils/test_logger"; 10 | 11 | export const test = async (tests: Array<[string, string]>, optAndUnopt: boolean): Promise => { 12 | const solcV = solcVersion(); 13 | const evmV = evmVersion(); 14 | ensureAndClear(BIN_OUTPUT_PATH); 15 | 16 | const ret = await compileAndRunTests(tests, true); 17 | 18 | const log = { 19 | solcVersion: solcV, 20 | evmVersion: evmV, 21 | results: ret.results 22 | }; 23 | 24 | const logsPath = createTimestampSubfolder(TEST_LOGS_PATH); 25 | writeLog(log, logsPath, RESULTS_NAME_OPTIMIZED); 26 | 27 | let retU = null; 28 | if (optAndUnopt) { 29 | ensureAndClear(BIN_OUTPUT_PATH); 30 | retU = await compileAndRunTests(tests, false); 31 | const logU = { 32 | solcVersion: solcV, 33 | evmVersion: evmV, 34 | results: retU.results 35 | }; 36 | writeLog(logU, logsPath, RESULTS_NAME_UNOPTIMIZED); 37 | } 38 | 39 | writeLatest(TEST_LOGS_PATH, logsPath); 40 | 41 | return ret.success && (!optAndUnopt || retU.success); 42 | }; 43 | 44 | export const compileAndRunTests = async (units: Array<[string, string]>, optimize: boolean) => { 45 | for (const unit of units) { 46 | const pckge = unit[0]; 47 | const tst = unit[1]; 48 | if (tst === '') { 49 | continue; 50 | } 51 | await compileTest(pckge, tst, optimize); 52 | } 53 | return runTests(); 54 | }; 55 | 56 | export const runTests = () => { 57 | const files = fs.readdirSync(BIN_OUTPUT_PATH); 58 | const sigfiles = files.filter((file) => { 59 | const f = file.trim(); 60 | return f.length > 4 && f.substr(0, 4) === 'Test' && f.split('.').pop() === 'signatures'; 61 | }); 62 | const results = {}; 63 | 64 | let tests = 0; 65 | let failed = 0; 66 | TestLogger.header('Running tests...'); 67 | for (const sigfile of sigfiles) { 68 | if (!isSigInHashes(BIN_OUTPUT_PATH, sigfile, TEST_FUN_HASH)) { 69 | throw new Error(`No test function in signature file: ${sigfile}`); 70 | } 71 | const name = sigfile.substr(0, sigfile.length - 11); 72 | const binRuntimePath = path.join(BIN_OUTPUT_PATH, name + ".bin-runtime"); 73 | const result = parseData(run(binRuntimePath, TEST_FUN_HASH)); 74 | const throws = /Throws/.test(name); 75 | let passed = true; 76 | tests++; 77 | if (throws && result) { 78 | failed++; 79 | passed = false; 80 | } else if (!throws && !result) { 81 | failed++; 82 | passed = false; 83 | } 84 | results[name] = {passed}; 85 | } 86 | TestLogger.header("Done"); 87 | return { 88 | success: failed === 0, 89 | results 90 | }; 91 | }; 92 | 93 | const parseData = (output: string): boolean => parseInt(output, 16) === 1; 94 | -------------------------------------------------------------------------------- /script/utils/data_reader.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | Object.defineProperty(exports, "__esModule", { value: true }); 3 | var io_1 = require("./io"); 4 | var constants_1 = require("../constants"); 5 | var path = require("path"); 6 | exports.getAllPackageNames = function () { 7 | var dataJSON = io_1.readJSON(constants_1.DATA_FILE); 8 | return Object.keys(dataJSON["packages"]).sort(); 9 | }; 10 | exports.getAllData = function () { 11 | var dataJSON = io_1.readJSON(constants_1.DATA_FILE); 12 | var data = { 13 | packages: {} 14 | }; 15 | var packagesJSON = dataJSON["packages"]; 16 | for (var pkg in packagesJSON) { 17 | if (packagesJSON.hasOwnProperty(pkg)) { 18 | data.packages[pkg] = io_1.readJSON(path.join(constants_1.DATA_PATH, packagesJSON[pkg])); 19 | } 20 | } 21 | return data; 22 | }; 23 | exports.getAllContractsFromData = function (data) { 24 | var contracts = []; 25 | var packages = data["packages"]; 26 | for (var pkg in packages) { 27 | if (packages.hasOwnProperty(pkg)) { 28 | var contractsJSON = packages[pkg]["contracts"]; 29 | if (contractsJSON && contractsJSON instanceof Array && contractsJSON.length > 0) { 30 | for (var _i = 0, contractsJSON_1 = contractsJSON; _i < contractsJSON_1.length; _i++) { 31 | var contract = contractsJSON_1[_i]; 32 | contracts.push([pkg, contract]); 33 | } 34 | } 35 | } 36 | } 37 | return contracts; 38 | }; 39 | exports.getAllContracts = function () { 40 | return exports.getAllContractsFromData(exports.getAllData()); 41 | }; 42 | exports.getAllContractFilesFromContractData = function (contracts) { 43 | var contractFiles = []; 44 | for (var _i = 0, contracts_1 = contracts; _i < contracts_1.length; _i++) { 45 | var contractData = contracts_1[_i]; 46 | var pkg = contractData[0]; 47 | var contract = contractData[1]; 48 | var cName = contract["name"]; 49 | if (!cName) { 50 | throw new Error("Error reading from contract data for " + pkg + ": contract has no name."); 51 | } 52 | contractFiles.push([pkg, cName]); 53 | } 54 | return contractFiles; 55 | }; 56 | exports.getAllContractFiles = function () { 57 | return exports.getAllContractFilesFromContractData(exports.getAllContracts()); 58 | }; 59 | exports.getAllTestFilesFromContractData = function (contracts, extended) { 60 | if (extended === void 0) { extended = false; } 61 | var contractFiles = []; 62 | for (var _i = 0, contracts_2 = contracts; _i < contracts_2.length; _i++) { 63 | var contractData = contracts_2[_i]; 64 | var pkg = contractData[0]; 65 | var contract = contractData[1]; 66 | var cName = contract["name"]; 67 | if (!cName) { 68 | throw new Error("Error reading from contract data for " + pkg + ": contract has no name."); 69 | } 70 | var tests = contract["tests"]; 71 | if (tests) { 72 | if (!(tests instanceof Array)) { 73 | throw new Error("Malformed tests for " + pkg + "/" + cName + ": tests is not an array."); 74 | } 75 | for (var _a = 0, tests_1 = tests; _a < tests_1.length; _a++) { 76 | var test_1 = tests_1[_a]; 77 | var tName = test_1["name"]; 78 | if (!tName) { 79 | throw new Error("Malformed tests for " + pkg + "/" + cName + ": test has no name."); 80 | } 81 | if (extended || !test_1["extended"]) { 82 | contractFiles.push([pkg, tName]); 83 | } 84 | } 85 | } 86 | } 87 | return contractFiles; 88 | }; 89 | exports.getAllTestFiles = function (extended) { 90 | if (extended === void 0) { extended = false; } 91 | return exports.getAllTestFilesFromContractData(exports.getAllContracts(), extended); 92 | }; 93 | exports.getAllPerfFilesFromContractData = function (contracts, extended) { 94 | if (extended === void 0) { extended = false; } 95 | var contractFiles = []; 96 | for (var _i = 0, contracts_3 = contracts; _i < contracts_3.length; _i++) { 97 | var contractData = contracts_3[_i]; 98 | var pkg = contractData[0]; 99 | var contract = contractData[1]; 100 | var cName = contract["name"]; 101 | if (!cName) { 102 | throw new Error("Error reading from contract data for " + pkg + ": contract has no name."); 103 | } 104 | var perf = contract["perf"]; 105 | if (perf) { 106 | if (!(perf instanceof Array)) { 107 | throw new Error("Malformed perf for " + pkg + "/" + cName + ": perf is not an array."); 108 | } 109 | for (var _a = 0, perf_1 = perf; _a < perf_1.length; _a++) { 110 | var p = perf_1[_a]; 111 | var pName = p["name"]; 112 | if (!pName) { 113 | throw new Error("Malformed perf for " + pkg + "/" + cName + ": perf has no name."); 114 | } 115 | if (extended || !p["extended"]) { 116 | contractFiles.push([pkg, pName]); 117 | } 118 | } 119 | } 120 | } 121 | return contractFiles; 122 | }; 123 | exports.getAllPerfFiles = function (extended) { 124 | if (extended === void 0) { extended = false; } 125 | return exports.getAllPerfFilesFromContractData(exports.getAllContracts(), extended); 126 | }; 127 | -------------------------------------------------------------------------------- /script/utils/data_reader.ts: -------------------------------------------------------------------------------- 1 | import {readJSON} from "./io"; 2 | import {DATA_FILE, DATA_PATH} from "../constants"; 3 | 4 | import * as path from "path"; 5 | 6 | export const getAllPackageNames = (): string[] => { 7 | const dataJSON = readJSON(DATA_FILE); 8 | return Object.keys(dataJSON["packages"]).sort(); 9 | }; 10 | 11 | export const getAllData = (): object => { 12 | const dataJSON = readJSON(DATA_FILE); 13 | const data = { 14 | packages: {} 15 | }; 16 | const packagesJSON = dataJSON["packages"]; 17 | for (const pkg in packagesJSON) { 18 | if (packagesJSON.hasOwnProperty(pkg)) { 19 | data.packages[pkg] = readJSON(path.join(DATA_PATH, packagesJSON[pkg])); 20 | } 21 | } 22 | return data; 23 | }; 24 | 25 | export const getAllContractsFromData = (data: object): Array<[string, object]> => { 26 | const contracts: Array<[string, object]> = []; 27 | const packages = data["packages"]; 28 | for (const pkg in packages) { 29 | if (packages.hasOwnProperty(pkg)) { 30 | const contractsJSON = packages[pkg]["contracts"]; 31 | if (contractsJSON && contractsJSON instanceof Array && contractsJSON.length > 0) { 32 | for (const contract of contractsJSON) { 33 | contracts.push([pkg, contract]); 34 | } 35 | } 36 | } 37 | } 38 | return contracts; 39 | }; 40 | 41 | export const getAllContracts = (): Array<[string, object]> => { 42 | return getAllContractsFromData(getAllData()); 43 | }; 44 | 45 | export const getAllContractFilesFromContractData = (contracts: Array<[string, object]>): Array<[string, string]> => { 46 | const contractFiles: Array<[string, string]> = []; 47 | for (const contractData of contracts) { 48 | const pkg = contractData[0]; 49 | const contract = contractData[1]; 50 | const cName = contract["name"]; 51 | if (!cName) { 52 | throw new Error(`Error reading from contract data for ${pkg}: contract has no name.`); 53 | } 54 | contractFiles.push([pkg, cName]); 55 | } 56 | 57 | return contractFiles; 58 | }; 59 | 60 | export const getAllContractFiles = (): Array<[string, string]> => { 61 | return getAllContractFilesFromContractData(getAllContracts()); 62 | }; 63 | 64 | export const getAllTestFilesFromContractData = (contracts: Array<[string, object]>, extended: boolean = false): Array<[string, string]> => { 65 | const contractFiles: Array<[string, string]> = []; 66 | for (const contractData of contracts) { 67 | const pkg = contractData[0]; 68 | const contract = contractData[1]; 69 | const cName = contract["name"]; 70 | if (!cName) { 71 | throw new Error(`Error reading from contract data for ${pkg}: contract has no name.`); 72 | } 73 | const tests = contract["tests"]; 74 | if (tests) { 75 | if (!(tests instanceof Array)) { 76 | throw new Error(`Malformed tests for ${pkg}/${cName}: tests is not an array.`); 77 | } 78 | for (const test of tests) { 79 | const tName = test["name"]; 80 | if (!tName) { 81 | throw new Error(`Malformed tests for ${pkg}/${cName}: test has no name.`); 82 | } 83 | if (extended || !test["extended"]) { 84 | contractFiles.push([pkg, tName]); 85 | } 86 | } 87 | } 88 | } 89 | return contractFiles; 90 | }; 91 | 92 | export const getAllTestFiles = (extended: boolean = false): Array<[string, string]> => { 93 | return getAllTestFilesFromContractData(getAllContracts(), extended); 94 | }; 95 | 96 | export const getAllPerfFilesFromContractData = (contracts: Array<[string, object]>, extended: boolean = false): Array<[string, string]> => { 97 | const contractFiles: Array<[string, string]> = []; 98 | for (const contractData of contracts) { 99 | const pkg = contractData[0]; 100 | const contract = contractData[1]; 101 | const cName = contract["name"]; 102 | if (!cName) { 103 | throw new Error(`Error reading from contract data for ${pkg}: contract has no name.`); 104 | } 105 | const perf = contract["perf"]; 106 | if (perf) { 107 | if (!(perf instanceof Array)) { 108 | throw new Error(`Malformed perf for ${pkg}/${cName}: perf is not an array.`); 109 | } 110 | for (const p of perf) { 111 | const pName = p["name"]; 112 | if (!pName) { 113 | throw new Error(`Malformed perf for ${pkg}/${cName}: perf has no name.`); 114 | } 115 | if (extended || !p["extended"]) { 116 | contractFiles.push([pkg, pName]); 117 | } 118 | } 119 | } 120 | } 121 | return contractFiles; 122 | }; 123 | 124 | export const getAllPerfFiles = (extended: boolean = false): Array<[string, string]> => { 125 | return getAllPerfFilesFromContractData(getAllContracts(), extended); 126 | }; 127 | -------------------------------------------------------------------------------- /script/utils/io.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | Object.defineProperty(exports, "__esModule", { value: true }); 3 | var fs = require("fs"); 4 | var mkdirp = require("mkdirp"); 5 | var path = require("path"); 6 | var logger_1 = require("./logger"); 7 | var constants_1 = require("../constants"); 8 | exports.print = function (text) { 9 | process.stdout.write(text); 10 | }; 11 | exports.println = function (text) { 12 | process.stdout.write(text + '\n'); 13 | }; 14 | exports.readText = function (filePath) { 15 | return fs.readFileSync(filePath).toString(); 16 | }; 17 | exports.readJSON = function (filePath) { 18 | return JSON.parse(exports.readText(filePath)); 19 | }; 20 | exports.rmrf = function (pth) { 21 | if (fs.existsSync(pth)) { 22 | fs.readdirSync(pth).forEach(function (file) { 23 | var curPath = pth + "/" + file; 24 | if (fs.lstatSync(curPath).isDirectory()) { 25 | exports.rmrf(curPath); 26 | } 27 | else { 28 | fs.unlinkSync(curPath); 29 | } 30 | }); 31 | fs.rmdirSync(pth); 32 | } 33 | }; 34 | exports.ensureAndClear = function (dir) { 35 | exports.rmrf(dir); 36 | mkdirp.sync(dir); 37 | }; 38 | exports.createTimestampSubfolder = function (root) { 39 | var folder = new Date().getTime().toString(10); 40 | var logPath = path.join(root, folder); 41 | mkdirp.sync(logPath); 42 | return logPath; 43 | }; 44 | exports.readLatest = function (dir) { 45 | var latestFile = path.join(dir, 'latest'); 46 | if (!fs.existsSync(latestFile)) { 47 | return ""; 48 | } 49 | return fs.readFileSync(latestFile).toString(); 50 | }; 51 | exports.writeLatest = function (dir, data) { 52 | var latestFile = path.join(dir, 'latest'); 53 | if (!fs.existsSync(latestFile)) { 54 | mkdirp.sync(dir); 55 | } 56 | fs.writeFileSync(latestFile, data); 57 | }; 58 | exports.writeLog = function (log, dir, name) { 59 | var optResultsPath = path.join(dir, name); 60 | fs.writeFileSync(optResultsPath, JSON.stringify(log, null, '\t')); 61 | logger_1.default.info("Logs written to: " + optResultsPath); 62 | }; 63 | exports.readLog = function (dir, name) { return exports.readJSON(path.join(dir, name)); }; 64 | exports.latestPerfLog = function (optimized) { 65 | if (optimized === void 0) { optimized = true; } 66 | var latest = exports.readLatest(constants_1.PERF_LOGS_PATH); 67 | if (latest === '') { 68 | throw new Error("No perf-logs found."); 69 | } 70 | var file = optimized ? constants_1.RESULTS_NAME_OPTIMIZED : constants_1.RESULTS_NAME_UNOPTIMIZED; 71 | return exports.readLog(latest, file); 72 | }; 73 | exports.latestTestLog = function (optimized) { 74 | if (optimized === void 0) { optimized = true; } 75 | var latest = exports.readLatest(constants_1.TEST_LOGS_PATH); 76 | if (latest === '') { 77 | throw new Error("No test-logs found."); 78 | } 79 | var file = optimized ? constants_1.RESULTS_NAME_OPTIMIZED : constants_1.RESULTS_NAME_UNOPTIMIZED; 80 | return exports.readLog(latest, file); 81 | }; 82 | exports.indexedLogFolders = function (baseDir, maxEntries) { 83 | if (maxEntries === void 0) { maxEntries = 20; } 84 | var files = fs.readdirSync(baseDir); 85 | var logFolders = files.filter(function (file) { 86 | if (!fs.statSync(path.join(baseDir, file)).isDirectory()) { 87 | return false; 88 | } 89 | var num = parseInt(file, 10); 90 | return !isNaN(num) && num > 0; 91 | }).sort().reverse(); 92 | return logFolders.length > maxEntries ? logFolders.slice(0, maxEntries) : logFolders; 93 | }; 94 | exports.isSigInHashes = function (dir, sigfile, sig) { 95 | var hashes = fs.readFileSync(path.join(dir, sigfile)).toString(); 96 | var lines = hashes.split(/\r\n|\r|\n/); 97 | if (lines.length === 0) { 98 | throw new Error("No methods found in signatures: " + sigfile); 99 | } 100 | var perfFound = false; 101 | for (var _i = 0, lines_1 = lines; _i < lines_1.length; _i++) { 102 | var line = lines_1[_i]; 103 | line = line.trim(); 104 | if (line.length === 0) { 105 | continue; 106 | } 107 | var tokens = line.split(':'); 108 | if (tokens.length !== 2) { 109 | throw new Error("No ':' separator in line: '" + line + "' in signatures: " + sigfile); 110 | } 111 | var hash = tokens[0].trim(); 112 | if (hash === sig) { 113 | if (perfFound) { 114 | throw new Error("Repeated hash of perf function in signature file: " + sigfile); 115 | } 116 | perfFound = true; 117 | } 118 | } 119 | return perfFound; 120 | }; 121 | -------------------------------------------------------------------------------- /script/utils/io.ts: -------------------------------------------------------------------------------- 1 | import * as fs from 'fs'; 2 | import * as mkdirp from 'mkdirp'; 3 | import * as path from 'path'; 4 | import Logger from "./logger"; 5 | import {PERF_LOGS_PATH, RESULTS_NAME_OPTIMIZED, RESULTS_NAME_UNOPTIMIZED, TEST_LOGS_PATH} from "../constants"; 6 | 7 | export const print = (text: string) => { 8 | process.stdout.write(text); 9 | }; 10 | 11 | export const println = (text: string) => { 12 | process.stdout.write(text + '\n'); 13 | }; 14 | 15 | export const readText = (filePath: string): string => { 16 | return fs.readFileSync(filePath).toString(); 17 | }; 18 | 19 | export const readJSON = (filePath: string): object => { 20 | return JSON.parse(readText(filePath)); 21 | }; 22 | 23 | export const rmrf = (pth: string): void => { 24 | if (fs.existsSync(pth)) { 25 | fs.readdirSync(pth).forEach((file) => { 26 | const curPath = pth + "/" + file; 27 | if (fs.lstatSync(curPath).isDirectory()) { // recurse 28 | rmrf(curPath); 29 | } else { // delete file 30 | fs.unlinkSync(curPath); 31 | } 32 | }); 33 | fs.rmdirSync(pth); 34 | } 35 | }; 36 | 37 | export const ensureAndClear = (dir: string): void => { 38 | rmrf(dir); 39 | mkdirp.sync(dir); 40 | }; 41 | 42 | export const createTimestampSubfolder = (root: string): string => { 43 | const folder = new Date().getTime().toString(10); 44 | const logPath = path.join(root, folder); 45 | mkdirp.sync(logPath); 46 | return logPath; 47 | }; 48 | 49 | export const readLatest = (dir: string): string => { 50 | const latestFile = path.join(dir, 'latest'); 51 | if (!fs.existsSync(latestFile)) { 52 | return ""; 53 | } 54 | return fs.readFileSync(latestFile).toString(); 55 | }; 56 | 57 | export const writeLatest = (dir: string, data: string) => { 58 | const latestFile = path.join(dir, 'latest'); 59 | if (!fs.existsSync(latestFile)) { 60 | mkdirp.sync(dir); 61 | } 62 | fs.writeFileSync(latestFile, data); 63 | }; 64 | 65 | export const writeLog = (log: object, dir: string, name: string): void => { 66 | const optResultsPath = path.join(dir, name); 67 | fs.writeFileSync(optResultsPath, JSON.stringify(log, null, '\t')); 68 | Logger.info(`Logs written to: ${optResultsPath}`); 69 | }; 70 | 71 | export const readLog = (dir: string, name: string): object => readJSON(path.join(dir, name)); 72 | 73 | export const latestPerfLog = (optimized: boolean = true): object => { 74 | const latest = readLatest(PERF_LOGS_PATH); 75 | if (latest === '') { 76 | throw new Error(`No perf-logs found.`); 77 | } 78 | const file = optimized ? RESULTS_NAME_OPTIMIZED : RESULTS_NAME_UNOPTIMIZED; 79 | return readLog(latest, file); 80 | }; 81 | 82 | export const latestTestLog = (optimized: boolean = true): object => { 83 | const latest = readLatest(TEST_LOGS_PATH); 84 | if (latest === '') { 85 | throw new Error(`No test-logs found.`); 86 | } 87 | const file = optimized ? RESULTS_NAME_OPTIMIZED : RESULTS_NAME_UNOPTIMIZED; 88 | return readLog(latest, file); 89 | }; 90 | 91 | export const indexedLogFolders = (baseDir: string, maxEntries: number = 20): string[] => { 92 | const files = fs.readdirSync(baseDir); 93 | const logFolders = files.filter((file) => { 94 | if (!fs.statSync(path.join(baseDir, file)).isDirectory()) { 95 | return false; 96 | } 97 | const num = parseInt(file, 10); 98 | return !isNaN(num) && num > 0; 99 | }).sort().reverse(); 100 | return logFolders.length > maxEntries ? logFolders.slice(0, maxEntries) : logFolders; 101 | }; 102 | 103 | export const isSigInHashes = (dir: string, sigfile: string, sig: string): boolean => { 104 | 105 | const hashes = fs.readFileSync(path.join(dir, sigfile)).toString(); 106 | const lines = hashes.split(/\r\n|\r|\n/); 107 | if (lines.length === 0) { 108 | throw new Error(`No methods found in signatures: ${sigfile}`); 109 | } 110 | let perfFound = false; 111 | for (let line of lines) { 112 | 113 | line = line.trim(); 114 | if (line.length === 0) { 115 | continue; 116 | } 117 | const tokens = line.split(':'); 118 | if (tokens.length !== 2) { // Should never happen with well formed signature files. 119 | throw new Error(`No ':' separator in line: '${line}' in signatures: ${sigfile}`); 120 | } 121 | const hash = tokens[0].trim(); 122 | if (hash === sig) { 123 | if (perfFound) { // Should never happen with well formed signature files. 124 | throw new Error(`Repeated hash of perf function in signature file: ${sigfile}`); 125 | } 126 | perfFound = true; 127 | } 128 | } 129 | return perfFound; 130 | }; 131 | -------------------------------------------------------------------------------- /script/utils/logger.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | Object.defineProperty(exports, "__esModule", { value: true }); 3 | var chalk = require("chalk"); 4 | var io_1 = require("./io"); 5 | var Logger = /** @class */ (function () { 6 | function Logger() { 7 | } 8 | Logger.error = function (text) { 9 | io_1.println(chalk['redBright']("[Error] " + text)); 10 | }; 11 | Logger.warn = function (text) { 12 | if (Logger.__level >= 1 /* Warn */) { 13 | io_1.println(chalk['yellowBright']("[Warning] " + text)); 14 | } 15 | }; 16 | Logger.info = function (text) { 17 | if (Logger.__level >= 2 /* Info */) { 18 | io_1.println(chalk['whiteBright']("[Info] " + text)); 19 | } 20 | }; 21 | Logger.debug = function (text) { 22 | if (Logger.__level === 3 /* Debug */) { 23 | io_1.println(chalk['blueBright']("[Debug] " + text)); 24 | } 25 | }; 26 | Logger.setLevel = function (level) { 27 | Logger.__level = level; 28 | }; 29 | Logger.level = function () { 30 | switch (Logger.__level) { 31 | case 0 /* Error */: 32 | return 'error'; 33 | case 1 /* Warn */: 34 | return 'warn'; 35 | case 2 /* Info */: 36 | return 'info'; 37 | case 3 /* Debug */: 38 | return 'debug'; 39 | } 40 | }; 41 | Logger.__level = 2 /* Info */; 42 | return Logger; 43 | }()); 44 | exports.default = Logger; 45 | -------------------------------------------------------------------------------- /script/utils/logger.ts: -------------------------------------------------------------------------------- 1 | import * as chalk from 'chalk'; 2 | import {println} from "./io"; 3 | 4 | export const enum Level { 5 | Error, 6 | Warn, 7 | Info, 8 | Debug 9 | } 10 | 11 | export default class Logger { 12 | 13 | private static __level: Level = Level.Info; 14 | 15 | public static error(text: string) { 16 | println(chalk['redBright'](`[Error] ${text}`)); 17 | } 18 | 19 | public static warn(text: string) { 20 | if (Logger.__level >= Level.Warn) { 21 | println(chalk['yellowBright'](`[Warning] ${text}`)); 22 | } 23 | } 24 | 25 | public static info(text: string) { 26 | if (Logger.__level >= Level.Info) { 27 | println(chalk['whiteBright'](`[Info] ${text}`)); 28 | } 29 | } 30 | 31 | public static debug(text: string) { 32 | if (Logger.__level === Level.Debug) { 33 | println(chalk['blueBright'](`[Debug] ${text}`)); 34 | } 35 | } 36 | 37 | public static setLevel(level: Level) { 38 | Logger.__level = level; 39 | } 40 | 41 | public static level(): string { 42 | switch (Logger.__level) { 43 | case Level.Error: 44 | return 'error'; 45 | case Level.Warn: 46 | return 'warn'; 47 | case Level.Info: 48 | return 'info'; 49 | case Level.Debug: 50 | return 'debug'; 51 | } 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /script/utils/logs.ts: -------------------------------------------------------------------------------- 1 | import * as chlk from "chalk"; 2 | import {indexedLogFolders, println, readLog} from "./io"; 3 | import * as jsondiffpatch from "jsondiffpatch"; 4 | import {PERF_LOGS_PATH, RESULTS_NAME_OPTIMIZED, RESULTS_NAME_UNOPTIMIZED} from "../constants"; 5 | import * as path from "path"; 6 | 7 | const chalk: any = chlk; // TODO Something going on with this thing. 8 | 9 | const PASSED = chalk["greenBright"]("PASSED"); 10 | const FAILED = chalk["redBright"]("FAILED"); 11 | 12 | const WHITE_ARROW = chalk["white"]("->"); 13 | 14 | export const printTestLog = (jsonObj) => { 15 | println('\n' + chalk["cyanBright"]("Test report") + '\n'); 16 | println(chalk["bold"]["white"]('Context')); 17 | println(chalk`\t{white Compiler version}: {magentaBright ${jsonObj.solcVersion}}`); 18 | println(chalk`\t{white EVM version}: {magentaBright ${jsonObj.evmVersion}}`); 19 | println(chalk["bold"]["white"]('Test results')); 20 | const results = jsonObj.results; 21 | for (const objName in results) { 22 | if (results.hasOwnProperty(objName)) { 23 | println(chalk`\t{white ${objName}}: ${results[objName].passed ? PASSED : FAILED}`); 24 | } 25 | } 26 | println('\n'); 27 | }; 28 | 29 | export const printPerfLog = (jsonObj) => { 30 | println('\n' + chalk["cyanBright"]('Perf report') + '\n'); 31 | println(chalk["bold"]["white"]('Context')); 32 | println(chalk`\t{white Compiler version}: {magentaBright ${jsonObj.solcVersion}}`); 33 | println(chalk`\t{white EVM version}: {magentaBright ${jsonObj.evmVersion}}`); 34 | println(chalk["bold"]["white"]('Gas usage')); 35 | const results = jsonObj.results; 36 | for (const objName in results) { 37 | if (results.hasOwnProperty(objName)) { 38 | println(chalk`\t{white ${objName}}: {blueBright ${results[objName].gasUsed}}`); 39 | } 40 | } 41 | println('\n'); 42 | }; 43 | 44 | export const diff = (oldLog, newLog) => jsondiffpatch["diff"](oldLog, newLog); 45 | 46 | export const printPerfDiff = (delta, oldLog, newLog) => { 47 | println('\n' + chalk["cyanBright"]('Perf diff') + '\n'); 48 | 49 | if (delta === undefined || delta === null) { 50 | println(chalk["greenBright"]('No changes')); 51 | } else { 52 | if (delta.solcVersion !== undefined || delta.evmVersion !== undefined) { 53 | println(chalk["bold"]["white"]('Context diff')); 54 | if (delta.solcVersion !== undefined) { 55 | println(chalk`\t{white Compiler version}: {magentaBright ${oldLog.solcVersion} ${WHITE_ARROW} ${newLog.solcVersion}}`); 56 | } 57 | if (delta.solcVersion !== undefined) { 58 | println(chalk`\t{white EVM version}: {magentaBright ${oldLog.evmVersion} ${WHITE_ARROW} ${newLog.evmVersion}}`); 59 | } 60 | } 61 | if (delta.results !== undefined && Object.keys(delta).length > 0) { 62 | println(chalk["bold"]["white"]('Results')); 63 | const results = delta.results; 64 | for (const objName in results) { 65 | if (results.hasOwnProperty(objName)) { 66 | const vDelta = results[objName]; 67 | if (vDelta instanceof Array && vDelta.length === 1) { 68 | println(chalk`\t({greenBright ++}) {white ${objName}}`); 69 | } else if (vDelta instanceof Array && vDelta.length === 3) { 70 | println(chalk`\t({redBright --}) {white ${objName}}`); 71 | } else { 72 | const oldGas = vDelta.gasUsed[0]; 73 | const newGas = vDelta.gasUsed[1]; 74 | if (newGas > oldGas) { 75 | println(chalk`\t{white ${objName}}: ${chalk.blueBright(oldGas)} ${WHITE_ARROW} ${chalk.redBright(newGas)}`); 76 | } else { 77 | println(chalk`\t{white ${objName}}: ${chalk.blueBright(oldGas)} ${WHITE_ARROW} ${chalk.greenBright(newGas)}`); 78 | } 79 | } 80 | } 81 | } 82 | } 83 | } 84 | println('\n'); 85 | }; 86 | 87 | export const printLatestDiff = (optimized: boolean = true): boolean => { 88 | const logFolders = indexedLogFolders(PERF_LOGS_PATH, 2); 89 | if (logFolders.length < 2) { 90 | return false; 91 | } 92 | const file = optimized ? RESULTS_NAME_OPTIMIZED : RESULTS_NAME_UNOPTIMIZED; 93 | const newLog = readLog(path.join(PERF_LOGS_PATH, logFolders[0]), file); 94 | const oldLog = readLog(path.join(PERF_LOGS_PATH, logFolders[1]), file); 95 | const delta = diff(oldLog, newLog); 96 | printPerfDiff(delta, oldLog, newLog); 97 | return true; 98 | }; 99 | -------------------------------------------------------------------------------- /script/utils/test_logger.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | Object.defineProperty(exports, "__esModule", { value: true }); 3 | var chalk = require("chalk"); 4 | var io_1 = require("./io"); 5 | var TestLogger = /** @class */ (function () { 6 | function TestLogger() { 7 | } 8 | TestLogger.header = function (text) { 9 | io_1.println(chalk["cyanBright"](text)); 10 | }; 11 | TestLogger.info = function (text) { 12 | if (!TestLogger.__silent) { 13 | io_1.println(chalk["blueBright"](text)); 14 | } 15 | }; 16 | TestLogger.success = function (text) { 17 | if (!TestLogger.__silent) { 18 | io_1.println(chalk["greenBright"](text)); 19 | } 20 | }; 21 | TestLogger.moderate = function (text) { 22 | if (!TestLogger.__silent) { 23 | io_1.println(chalk["yellowBright"](text)); 24 | } 25 | }; 26 | TestLogger.fail = function (text) { 27 | io_1.println(chalk["redBright"](text)); 28 | }; 29 | TestLogger.setSilent = function (silent) { 30 | TestLogger.__silent = silent; 31 | }; 32 | TestLogger.silent = function () { 33 | return TestLogger.__silent; 34 | }; 35 | TestLogger.__silent = false; 36 | return TestLogger; 37 | }()); 38 | exports.default = TestLogger; 39 | -------------------------------------------------------------------------------- /script/utils/test_logger.ts: -------------------------------------------------------------------------------- 1 | import * as chalk from "chalk"; 2 | import {println} from "./io"; 3 | 4 | export default class TestLogger { 5 | 6 | private static __silent = false; 7 | 8 | public static header(text: string) { 9 | println(chalk["cyanBright"](text)); 10 | } 11 | 12 | public static info(text: string) { 13 | if (!TestLogger.__silent) { 14 | println(chalk["blueBright"](text)); 15 | } 16 | } 17 | 18 | public static success(text: string) { 19 | if (!TestLogger.__silent) { 20 | println(chalk["greenBright"](text)); 21 | } 22 | } 23 | 24 | public static moderate(text: string) { 25 | if (!TestLogger.__silent) { 26 | println(chalk["yellowBright"](text)); 27 | } 28 | } 29 | 30 | public static fail(text: string) { 31 | println(chalk["redBright"](text)); 32 | } 33 | 34 | public static setSilent(silent: boolean) { 35 | TestLogger.__silent = silent; 36 | } 37 | 38 | public static silent(): boolean { 39 | return TestLogger.__silent; 40 | } 41 | 42 | } 43 | -------------------------------------------------------------------------------- /src/bits/Bits.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.4.16; 2 | pragma experimental "v0.5.0"; 3 | pragma experimental "ABIEncoderV2"; 4 | 5 | 6 | library Bits { 7 | 8 | uint constant internal ONE = uint(1); 9 | uint constant internal ONES = uint(~0); 10 | 11 | // Sets the bit at the given 'index' in 'self' to '1'. 12 | // Returns the modified value. 13 | function setBit(uint self, uint8 index) internal pure returns (uint) { 14 | return self | ONE << index; 15 | } 16 | 17 | // Sets the bit at the given 'index' in 'self' to '0'. 18 | // Returns the modified value. 19 | function clearBit(uint self, uint8 index) internal pure returns (uint) { 20 | return self & ~(ONE << index); 21 | } 22 | 23 | // Sets the bit at the given 'index' in 'self' to: 24 | // '1' - if the bit is '0' 25 | // '0' - if the bit is '1' 26 | // Returns the modified value. 27 | function toggleBit(uint self, uint8 index) internal pure returns (uint) { 28 | return self ^ ONE << index; 29 | } 30 | 31 | // Get the value of the bit at the given 'index' in 'self'. 32 | function bit(uint self, uint8 index) internal pure returns (uint8) { 33 | return uint8(self >> index & 1); 34 | } 35 | 36 | // Check if the bit at the given 'index' in 'self' is set. 37 | // Returns: 38 | // 'true' - if the value of the bit is '1' 39 | // 'false' - if the value of the bit is '0' 40 | function bitSet(uint self, uint8 index) internal pure returns (bool) { 41 | return self >> index & 1 == 1; 42 | } 43 | 44 | // Checks if the bit at the given 'index' in 'self' is equal to the corresponding 45 | // bit in 'other'. 46 | // Returns: 47 | // 'true' - if both bits are '0' or both bits are '1' 48 | // 'false' - otherwise 49 | function bitEqual(uint self, uint other, uint8 index) internal pure returns (bool) { 50 | return (self ^ other) >> index & 1 == 0; 51 | } 52 | 53 | // Get the bitwise NOT of the bit at the given 'index' in 'self'. 54 | function bitNot(uint self, uint8 index) internal pure returns (uint8) { 55 | return uint8(1 - (self >> index & 1)); 56 | } 57 | 58 | // Computes the bitwise AND of the bit at the given 'index' in 'self', and the 59 | // corresponding bit in 'other', and returns the value. 60 | function bitAnd(uint self, uint other, uint8 index) internal pure returns (uint8) { 61 | return uint8((self & other) >> index & 1); 62 | } 63 | 64 | // Computes the bitwise OR of the bit at the given 'index' in 'self', and the 65 | // corresponding bit in 'other', and returns the value. 66 | function bitOr(uint self, uint other, uint8 index) internal pure returns (uint8) { 67 | return uint8((self | other) >> index & 1); 68 | } 69 | 70 | // Computes the bitwise XOR of the bit at the given 'index' in 'self', and the 71 | // corresponding bit in 'other', and returns the value. 72 | function bitXor(uint self, uint other, uint8 index) internal pure returns (uint8) { 73 | return uint8((self ^ other) >> index & 1); 74 | } 75 | 76 | // Gets 'numBits' consecutive bits from 'self', starting from the bit at 'startIndex'. 77 | // Returns the bits as a 'uint'. 78 | // Requires that: 79 | // - '0 < numBits <= 256' 80 | // - 'startIndex < 256' 81 | // - 'numBits + startIndex <= 256' 82 | function bits(uint self, uint8 startIndex, uint16 numBits) internal pure returns (uint) { 83 | require(0 < numBits && startIndex < 256 && startIndex + numBits <= 256); 84 | return self >> startIndex & ONES >> 256 - numBits; 85 | } 86 | 87 | // Computes the index of the highest bit set in 'self'. 88 | // Returns the highest bit set as an 'uint8'. 89 | // Requires that 'self != 0'. 90 | function highestBitSet(uint self) internal pure returns (uint8 highest) { 91 | require(self != 0); 92 | uint val = self; 93 | for (uint8 i = 128; i >= 1; i >>= 1) { 94 | if (val & (ONE << i) - 1 << i != 0) { 95 | highest += i; 96 | val >>= i; 97 | } 98 | } 99 | } 100 | 101 | // Computes the index of the lowest bit set in 'self'. 102 | // Returns the lowest bit set as an 'uint8'. 103 | // Requires that 'self != 0'. 104 | function lowestBitSet(uint self) internal pure returns (uint8 lowest) { 105 | require(self != 0); 106 | uint val = self; 107 | for (uint8 i = 128; i >= 1; i >>= 1) { 108 | if (val & (ONE << i) - 1 == 0) { 109 | lowest += i; 110 | val >>= i; 111 | } 112 | } 113 | } 114 | 115 | } -------------------------------------------------------------------------------- /src/math/ExactMath.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.4.16; 2 | pragma experimental "v0.5.0"; 3 | pragma experimental "ABIEncoderV2"; 4 | 5 | 6 | library ExactMath { 7 | 8 | uint constant internal UINT_ZERO = 0; 9 | uint constant internal UINT_ONE = 1; 10 | uint constant internal UINT_TWO = 2; 11 | uint constant internal UINT_MAX = ~uint(0); 12 | uint constant internal UINT_MIN = 0; 13 | 14 | int constant internal INT_ZERO = 0; 15 | int constant internal INT_ONE = 1; 16 | int constant internal INT_TWO = 2; 17 | int constant internal INT_MINUS_ONE = -1; 18 | int constant internal INT_MAX = int(2**255 - 1); 19 | int constant internal INT_MIN = int(2**255); 20 | 21 | // Calculates and returns 'self + other' 22 | // The function will throw if the operation would result in an overflow. 23 | function exactAdd(uint self, uint other) internal pure returns (uint sum) { 24 | sum = self + other; 25 | require(sum >= self); 26 | } 27 | 28 | // Calculates and returns 'self - other' 29 | // The function will throw if the operation would result in an underflow. 30 | function exactSub(uint self, uint other) internal pure returns (uint diff) { 31 | require(other <= self); 32 | diff = self - other; 33 | } 34 | 35 | // Calculates and returns 'self * other' 36 | // The function will throw if the operation would result in an overflow. 37 | function exactMul(uint self, uint other) internal pure returns (uint prod) { 38 | prod = self * other; 39 | require(self == 0 || prod / self == other); 40 | } 41 | 42 | // Calculates and returns 'self + other' 43 | // The function will throw if the operation would result in an over/underflow. 44 | function exactAdd(int self, int other) internal pure returns (int sum) { 45 | sum = self + other; 46 | if (self > 0 && other > 0) { 47 | require(0 <= sum && sum <= INT_MAX); 48 | } else if (self < 0 && other < 0) { 49 | require(INT_MIN <= sum && sum <= 0); 50 | } 51 | } 52 | 53 | // Calculates and returns 'self - other' 54 | // The function will throw if the operation would result in an over/underflow. 55 | function exactSub(int self, int other) internal pure returns (int diff) { 56 | diff = self - other; 57 | if (self > 0 && other < 0) { 58 | require(0 <= diff && diff <= INT_MAX); 59 | } else if (self < 0 && other > 0) { 60 | require(INT_MIN <= diff && diff <= 0); 61 | } 62 | } 63 | 64 | // Calculates and returns 'self * other' 65 | // The function will throw if the operation would result in an over/underflow. 66 | function exactMul(int self, int other) internal pure returns (int prod) { 67 | prod = self * other; 68 | require(self == 0 || ((other != INT_MIN || self != INT_MINUS_ONE) && prod / self == other)); 69 | } 70 | 71 | // Calculates and returns 'self / other' 72 | // The function will throw if the operation would result in an over/underflow. 73 | function exactDiv(int self, int other) internal pure returns (int quot) { 74 | require(self != INT_MIN || other != INT_MINUS_ONE); 75 | quot = self / other; 76 | } 77 | } -------------------------------------------------------------------------------- /src/patricia_tree/PatriciaTree.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.4.16; 2 | pragma experimental "v0.5.0"; 3 | pragma experimental "ABIEncoderV2"; 4 | 5 | import {Data} from "./Data.sol"; 6 | import {Bits} from "../bits/Bits.sol"; 7 | import {PatriciaTreeFace} from "./PatriciaTreeFace.sol"; 8 | 9 | 10 | /* 11 | * Patricia tree implementation. 12 | * 13 | * More info at: https://github.com/chriseth/patricia-trie 14 | */ 15 | contract PatriciaTree is PatriciaTreeFace { 16 | 17 | using Data for Data.Tree; 18 | using Data for Data.Node; 19 | using Data for Data.Edge; 20 | using Data for Data.Label; 21 | using Bits for uint; 22 | 23 | Data.Tree internal tree; 24 | 25 | // Get the root hash. 26 | function getRootHash() public view returns (bytes32) { 27 | return tree.root; 28 | } 29 | 30 | // Get the root edge. 31 | function getRootEdge() public view returns (Data.Edge e) { 32 | e = tree.rootEdge; 33 | } 34 | 35 | // Get the node with the given key. The key needs to be 36 | // the keccak256 hash of the actual key. 37 | function getNode(bytes32 hash) public view returns (Data.Node n) { 38 | n = tree.nodes[hash]; 39 | } 40 | 41 | // Returns the Merkle-proof for the given key 42 | // Proof format should be: 43 | // - uint branchMask - bitmask with high bits at the positions in the key 44 | // where we have branch nodes (bit in key denotes direction) 45 | // - bytes32[] _siblings - hashes of sibling edges 46 | function getProof(bytes key) public view returns (uint branchMask, bytes32[] _siblings) { 47 | require(tree.root != 0); 48 | Data.Label memory k = Data.Label(keccak256(key), 256); 49 | Data.Edge memory e = tree.rootEdge; 50 | bytes32[256] memory siblings; 51 | uint length; 52 | uint numSiblings; 53 | while (true) { 54 | var (prefix, suffix) = k.splitCommonPrefix(e.label); 55 | assert(prefix.length == e.label.length); 56 | if (suffix.length == 0) { 57 | // Found it 58 | break; 59 | } 60 | length += prefix.length; 61 | branchMask |= uint(1) << 255 - length; 62 | length += 1; 63 | var (head, tail) = suffix.chopFirstBit(); 64 | siblings[numSiblings++] = tree.nodes[e.node].children[1 - head].edgeHash(); 65 | e = tree.nodes[e.node].children[head]; 66 | k = tail; 67 | } 68 | if (numSiblings > 0) { 69 | _siblings = new bytes32[](numSiblings); 70 | for (uint i = 0; i < numSiblings; i++) { 71 | _siblings[i] = siblings[i]; 72 | } 73 | } 74 | } 75 | 76 | function verifyProof(bytes32 rootHash, bytes key, bytes value, uint branchMask, bytes32[] siblings) public view returns (bool) { 77 | Data.Label memory k = Data.Label(keccak256(key), 256); 78 | Data.Edge memory e; 79 | e.node = keccak256(value); 80 | for (uint i = 0; branchMask != 0; i++) { 81 | uint bitSet = branchMask.lowestBitSet(); 82 | branchMask &= ~(uint(1) << bitSet); 83 | (k, e.label) = k.splitAt(255 - bitSet); 84 | uint bit; 85 | (bit, e.label) = e.label.chopFirstBit(); 86 | bytes32[2] memory edgeHashes; 87 | edgeHashes[bit] = e.edgeHash(); 88 | edgeHashes[1 - bit] = siblings[siblings.length - i - 1]; 89 | e.node = keccak256(edgeHashes); 90 | } 91 | e.label = k; 92 | require(rootHash == e.edgeHash()); 93 | return true; 94 | } 95 | 96 | function insert(bytes key, bytes value) public { 97 | tree.insert(key, value); 98 | } 99 | 100 | } -------------------------------------------------------------------------------- /src/patricia_tree/PatriciaTreeFace.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.4.16; 2 | pragma experimental "v0.5.0"; 3 | pragma experimental "ABIEncoderV2"; 4 | 5 | import {Data} from "./Data.sol"; 6 | 7 | 8 | /* 9 | * Interface for patricia trees. 10 | * 11 | * More info at: https://github.com/chriseth/patricia-trie 12 | */ 13 | contract PatriciaTreeFace { 14 | function getRootHash() public view returns (bytes32); 15 | function getRootEdge() public view returns (Data.Edge e); 16 | function getNode(bytes32 hash) public view returns (Data.Node n); 17 | function getProof(bytes key) public view returns (uint branchMask, bytes32[] _siblings); 18 | function verifyProof(bytes32 rootHash, bytes key, bytes value, uint branchMask, bytes32[] siblings) public view returns (bool); 19 | function insert(bytes key, bytes value) public; 20 | } -------------------------------------------------------------------------------- /src/strings/Strings.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.4.16; 2 | pragma experimental "v0.5.0"; 3 | pragma experimental "ABIEncoderV2"; 4 | 5 | import {Memory} from "../unsafe/Memory.sol"; 6 | 7 | 8 | library Strings { 9 | 10 | // Key bytes. 11 | // http://www.unicode.org/versions/Unicode10.0.0/UnicodeStandard-10.0.pdf 12 | // Table 3-7, p 126, Well-Formed UTF-8 Byte Sequences 13 | 14 | // Default 80..BF range 15 | uint constant internal DL = 0x80; 16 | uint constant internal DH = 0xBF; 17 | 18 | // Row - number of bytes 19 | 20 | // R1 - 1 21 | uint constant internal B11L = 0x00; 22 | uint constant internal B11H = 0x7F; 23 | 24 | // R2 - 2 25 | uint constant internal B21L = 0xC2; 26 | uint constant internal B21H = 0xDF; 27 | 28 | // R3 - 3 29 | uint constant internal B31 = 0xE0; 30 | uint constant internal B32L = 0xA0; 31 | uint constant internal B32H = 0xBF; 32 | 33 | // R4 - 3 34 | uint constant internal B41L = 0xE1; 35 | uint constant internal B41H = 0xEC; 36 | 37 | // R5 - 3 38 | uint constant internal B51 = 0xED; 39 | uint constant internal B52L = 0x80; 40 | uint constant internal B52H = 0x9F; 41 | 42 | // R6 - 3 43 | uint constant internal B61L = 0xEE; 44 | uint constant internal B61H = 0xEF; 45 | 46 | // R7 - 4 47 | uint constant internal B71 = 0xF0; 48 | uint constant internal B72L = 0x90; 49 | uint constant internal B72H = 0xBF; 50 | 51 | // R8 - 4 52 | uint constant internal B81L = 0xF1; 53 | uint constant internal B81H = 0xF3; 54 | 55 | // R9 - 4 56 | uint constant internal B91 = 0xF4; 57 | uint constant internal B92L = 0x80; 58 | uint constant internal B92H = 0x8F; 59 | 60 | // Checks whether a string is valid UTF-8. 61 | // If the string is not valid, the function will throw. 62 | function validate(string memory self) internal pure { 63 | uint addr; 64 | uint len; 65 | assembly { 66 | addr := add(self, 0x20) 67 | len := mload(self) 68 | } 69 | if (len == 0) { 70 | return; 71 | } 72 | uint bytePos = 0; 73 | while (bytePos < len) { 74 | bytePos += parseRune(addr + bytePos); 75 | } 76 | require(bytePos == len); 77 | } 78 | 79 | // Parses a single character, or "rune" stored at address 'bytePos' 80 | // in memory. 81 | // Returns the length of the character in bytes. 82 | // solhint-disable-next-line code-complexity 83 | function parseRune(uint bytePos) internal pure returns (uint len) { 84 | uint val; 85 | assembly { 86 | val := mload(bytePos) 87 | } 88 | val >>= 224; // Remove all but the first four bytes. 89 | uint v0 = val >> 24; // Get first byte. 90 | if (v0 <= B11H) { // Check a 1 byte character. 91 | len = 1; 92 | } else if (B21L <= v0 && v0 <= B21H) { // Check a 2 byte character. 93 | var v1 = (val & 0x00FF0000) >> 16; 94 | require(DL <= v1 && v1 <= DH); 95 | len = 2; 96 | } else if (v0 == B31) { // Check a 3 byte character in the following three. 97 | validateWithNextDefault((val & 0x00FFFF00) >> 8, B32L, B32H); 98 | len = 3; 99 | } else if (v0 == B51) { 100 | validateWithNextDefault((val & 0x00FFFF00) >> 8, B52L, B52H); 101 | len = 3; 102 | } else if ((B41L <= v0 && v0 <= B41H) || v0 == B61L || v0 == B61H) { 103 | validateWithNextDefault((val & 0x00FFFF00) >> 8, DL, DH); 104 | len = 3; 105 | } else if (v0 == B71) { // Check a 4 byte character in the following three. 106 | validateWithNextTwoDefault(val & 0x00FFFFFF, B72L, B72H); 107 | len = 4; 108 | } else if (B81L <= v0 && v0 <= B81H) { 109 | validateWithNextTwoDefault(val & 0x00FFFFFF, DL, DH); 110 | len = 4; 111 | } else if (v0 == B91) { 112 | validateWithNextTwoDefault(val & 0x00FFFFFF, B92L, B92H); 113 | len = 4; 114 | } else { // If we reach this point, the character is not valid UTF-8 115 | revert(); 116 | } 117 | } 118 | 119 | function validateWithNextDefault(uint val, uint low, uint high) private pure { 120 | uint b = (val & 0xFF00) >> 8; 121 | require(low <= b && b <= high); 122 | b = val & 0x00FF; 123 | require(DL <= b && b <= DH); 124 | } 125 | 126 | function validateWithNextTwoDefault(uint val, uint low, uint high) private pure { 127 | uint b = (val & 0xFF0000) >> 16; 128 | require(low <= b && b <= high); 129 | b = (val & 0x00FF00) >> 8; 130 | require(DL <= b && b <= DH); 131 | b = val & 0x0000FF; 132 | require(DL <= b && b <= DH); 133 | } 134 | 135 | } 136 | -------------------------------------------------------------------------------- /src/tokens/ERC20TokenFace.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.4.16; 2 | pragma experimental "v0.5.0"; 3 | pragma experimental "ABIEncoderV2"; 4 | 5 | 6 | contract ERC20TokenFace { 7 | event Transfer(address indexed _from, address indexed _to, uint256 _value); 8 | event Approval(address indexed _owner, address indexed _spender, uint256 _value); 9 | 10 | function totalSupply() public constant returns (uint256 totalSupply); 11 | function balanceOf(address _owner) public constant returns (uint256 balance); 12 | // solhint-disable-next-line no-simple-event-func-name 13 | function transfer(address _to, uint256 _value) public returns (bool success); 14 | function transferFrom(address _from, address _to, uint256 _value) public returns (bool success); 15 | function approve(address _spender, uint256 _value) public returns (bool success); 16 | function allowance(address _owner, address _spender) public constant returns (uint256 remaining); 17 | } 18 | -------------------------------------------------------------------------------- /src/unsafe/Memory.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.4.16; 2 | pragma experimental "v0.5.0"; 3 | pragma experimental "ABIEncoderV2"; 4 | 5 | 6 | library Memory { 7 | 8 | // Size of a word, in bytes. 9 | uint internal constant WORD_SIZE = 32; 10 | // Size of the header of a 'bytes' array. 11 | uint internal constant BYTES_HEADER_SIZE = 32; 12 | // Address of the free memory pointer. 13 | uint internal constant FREE_MEM_PTR = 0x40; 14 | 15 | // Compares the 'len' bytes starting at address 'addr' in memory with the 'len' 16 | // bytes starting at 'addr2'. 17 | // Returns 'true' if the bytes are the same, otherwise 'false'. 18 | function equals(uint addr, uint addr2, uint len) internal pure returns (bool equal) { 19 | assembly { 20 | equal := eq(keccak256(addr, len), keccak256(addr2, len)) 21 | } 22 | } 23 | 24 | // Compares the 'len' bytes starting at address 'addr' in memory with the bytes stored in 25 | // 'bts'. It is allowed to set 'len' to a lower value then 'bts.length', in which case only 26 | // the first 'len' bytes will be compared. 27 | // Requires that 'bts.length >= len' 28 | function equals(uint addr, uint len, bytes memory bts) internal pure returns (bool equal) { 29 | require(bts.length >= len); 30 | uint addr2; 31 | assembly { 32 | addr2 := add(bts, /*BYTES_HEADER_SIZE*/32) 33 | } 34 | return equals(addr, addr2, len); 35 | } 36 | 37 | // Allocates 'numBytes' bytes in memory. This will prevent the Solidity compiler 38 | // from using this area of memory. It will also initialize the area by setting 39 | // each byte to '0'. 40 | function allocate(uint numBytes) internal pure returns (uint addr) { 41 | // Take the current value of the free memory pointer, and update. 42 | assembly { 43 | addr := mload(/*FREE_MEM_PTR*/0x40) 44 | mstore(/*FREE_MEM_PTR*/0x40, add(addr, numBytes)) 45 | } 46 | uint words = (numBytes + WORD_SIZE - 1) / WORD_SIZE; 47 | for (uint i = 0; i < words; i++) { 48 | assembly { 49 | mstore(add(addr, mul(i, /*WORD_SIZE*/32)), 0) 50 | } 51 | } 52 | } 53 | 54 | // Copy 'len' bytes from memory address 'src', to address 'dest'. 55 | // This function does not check the or destination, it only copies 56 | // the bytes. 57 | function copy(uint src, uint dest, uint len) internal pure { 58 | // Copy word-length chunks while possible 59 | for (; len >= WORD_SIZE; len -= WORD_SIZE) { 60 | assembly { 61 | mstore(dest, mload(src)) 62 | } 63 | dest += WORD_SIZE; 64 | src += WORD_SIZE; 65 | } 66 | 67 | if (len == 0) return; 68 | 69 | // Copy remaining bytes 70 | uint mask = 256 ** (WORD_SIZE - len) - 1; 71 | assembly { 72 | let srcpart := and(mload(src), not(mask)) 73 | let destpart := and(mload(dest), mask) 74 | mstore(dest, or(destpart, srcpart)) 75 | } 76 | } 77 | 78 | // Returns a memory pointer to the provided bytes array. 79 | function ptr(bytes memory bts) internal pure returns (uint addr) { 80 | assembly { 81 | addr := bts 82 | } 83 | } 84 | 85 | // Returns a memory pointer to the data portion of the provided bytes array. 86 | function dataPtr(bytes memory bts) internal pure returns (uint addr) { 87 | assembly { 88 | addr := add(bts, /*BYTES_HEADER_SIZE*/32) 89 | } 90 | } 91 | 92 | // This function does the same as 'dataPtr(bytes memory)', but will also return the 93 | // length of the provided bytes array. 94 | function fromBytes(bytes memory bts) internal pure returns (uint addr, uint len) { 95 | len = bts.length; 96 | assembly { 97 | addr := add(bts, /*BYTES_HEADER_SIZE*/32) 98 | } 99 | } 100 | 101 | // Creates a 'bytes memory' variable from the memory address 'addr', with the 102 | // length 'len'. The function will allocate new memory for the bytes array, and 103 | // the 'len bytes starting at 'addr' will be copied into that new memory. 104 | function toBytes(uint addr, uint len) internal pure returns (bytes memory bts) { 105 | bts = new bytes(len); 106 | uint btsptr; 107 | assembly { 108 | btsptr := add(bts, /*BYTES_HEADER_SIZE*/32) 109 | } 110 | copy(addr, btsptr, len); 111 | } 112 | 113 | // Get the word stored at memory address 'addr' as a 'uint'. 114 | function toUint(uint addr) internal pure returns (uint n) { 115 | assembly { 116 | n := mload(addr) 117 | } 118 | } 119 | 120 | // Get the word stored at memory address 'addr' as a 'bytes32'. 121 | function toBytes32(uint addr) internal pure returns (bytes32 bts) { 122 | assembly { 123 | bts := mload(addr) 124 | } 125 | } 126 | 127 | /* 128 | // Get the byte stored at memory address 'addr' as a 'byte'. 129 | function toByte(uint addr, uint8 index) internal pure returns (byte b) { 130 | require(index < WORD_SIZE); 131 | uint8 n; 132 | assembly { 133 | n := byte(index, mload(addr)) 134 | } 135 | b = byte(n); 136 | } 137 | */ 138 | } 139 | -------------------------------------------------------------------------------- /test/STLTest.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.4.16; 2 | pragma experimental "v0.5.0"; 3 | pragma experimental "ABIEncoderV2"; 4 | 5 | 6 | contract STLTest { 7 | 8 | function test() public payable returns (bool ret) { 9 | ret = true; 10 | testImpl(); 11 | } 12 | 13 | function testImpl() internal; 14 | 15 | } -------------------------------------------------------------------------------- /test/math/math_consistency.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.4.16; 2 | pragma experimental "v0.5.0"; 3 | pragma experimental "ABIEncoderV2"; 4 | 5 | import {STLTest} from "../STLTest.sol"; 6 | 7 | 8 | contract TestMathUint256Overflow is STLTest { 9 | 10 | uint internal constant ZERO = 0; 11 | uint internal constant ONE = 1; 12 | uint internal constant TWO = 2; 13 | uint internal constant ONES = ~uint(0); 14 | uint internal constant HALF = uint(1) << 128; 15 | 16 | function testImpl() internal { 17 | uint n; 18 | // + 19 | assert(ONES + ONE == ZERO); 20 | // ++ 21 | n = ONES; 22 | n++; 23 | assert(n == ZERO); 24 | n = ONES; 25 | ++n; 26 | assert(n == ZERO); 27 | // += 28 | n = ONES; 29 | n += ONE; 30 | assert(n == ZERO); 31 | 32 | // - 33 | assert(ZERO - ONE == ONES); 34 | // -- 35 | n = ZERO; 36 | n--; 37 | assert(n == ONES); 38 | n = ZERO; 39 | --n; 40 | assert(n == ONES); 41 | // -= 42 | n = ZERO; 43 | n -= ONE; 44 | assert(n == ONES); 45 | // - (unary) 46 | assert(-ONE == ONES); 47 | 48 | // * 49 | assert(HALF * HALF == ZERO); 50 | // *= 51 | n = HALF; 52 | n *= HALF; 53 | assert(n == ZERO); 54 | assert(TWO ** 256 == ZERO); 55 | 56 | } 57 | } 58 | 59 | 60 | contract TestMathInt256Overflow is STLTest { 61 | 62 | int internal constant ZERO = 0; 63 | int internal constant ONE = 1; 64 | int internal constant TWO = 2; 65 | int internal constant MINUS_ONE = -1; 66 | int internal constant HALF = int(1) << 128; 67 | int internal constant INT_MAX = int(2**255 - 1); 68 | int internal constant INT_MIN = int(-(2**255)); 69 | 70 | function testImpl() internal { 71 | int n; 72 | 73 | // + 74 | assert(INT_MAX + ONE == INT_MIN); 75 | 76 | // ++ 77 | n = INT_MAX; 78 | n++; 79 | assert(n == INT_MIN); 80 | n = INT_MAX; 81 | ++n; 82 | assert(n == INT_MIN); 83 | // += 84 | n = INT_MAX; 85 | n += ONE; 86 | assert(n == INT_MIN); 87 | 88 | // - 89 | assert(INT_MIN - ONE == INT_MAX); 90 | // -- 91 | n = INT_MIN; 92 | n--; 93 | assert(n == INT_MAX); 94 | n = INT_MIN; 95 | --n; 96 | assert(n == INT_MAX); 97 | // -= 98 | n = INT_MIN; 99 | n -= ONE; 100 | assert(n == INT_MAX); 101 | // - (unary) 102 | assert(-INT_MIN == INT_MIN); 103 | 104 | // * 105 | assert(INT_MIN*MINUS_ONE == INT_MIN); 106 | assert(MINUS_ONE*INT_MIN == INT_MIN); 107 | // *= 108 | n = INT_MIN; 109 | n *= MINUS_ONE; 110 | assert(n == INT_MIN); 111 | 112 | // / 113 | assert(INT_MIN / MINUS_ONE == INT_MIN); 114 | // /= 115 | n = INT_MIN; 116 | n /= MINUS_ONE; 117 | assert(n == INT_MIN); 118 | 119 | // << TODO 120 | 121 | } 122 | } 123 | 124 | 125 | contract TestMathUint256DivWithZeroThrows is STLTest { 126 | 127 | function testImpl() internal { 128 | uint x = 5; 129 | uint y = 0; 130 | x / y; 131 | } 132 | 133 | } 134 | 135 | 136 | contract TestMathUint256RemainderWithZeroThrows is STLTest { 137 | 138 | function testImpl() internal { 139 | uint x = 5; 140 | uint y = 0; 141 | x % y; 142 | } 143 | 144 | } 145 | 146 | 147 | contract TestMathInt256DivWithZeroThrows is STLTest { 148 | 149 | function testImpl() internal { 150 | int x = 5; 151 | int y = 0; 152 | x / y; 153 | } 154 | 155 | } 156 | 157 | 158 | contract TestMathInt256RemainderWithZeroThrows is STLTest { 159 | 160 | function testImpl() internal { 161 | int x = 5; 162 | int y = 0; 163 | x % y; 164 | } 165 | 166 | } -------------------------------------------------------------------------------- /test/patricia_tree/patricia_tree.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.4.16; 2 | pragma experimental "v0.5.0"; 3 | pragma experimental "ABIEncoderV2"; 4 | 5 | import {Data} from "../../src/patricia_tree/Data.sol"; 6 | import {PatriciaTree} from "../../src/patricia_tree/PatriciaTree.sol"; 7 | import {STLTest} from "../STLTest.sol"; 8 | 9 | 10 | /* solhint-disable no-empty-blocks */ 11 | contract PatriciaTreeTest is STLTest, PatriciaTree {} 12 | /* solhint-enable no-empty-blocks */ 13 | 14 | 15 | contract TestPatriciaTreeInsert is PatriciaTreeTest { 16 | function testImpl() internal { 17 | 18 | bytes memory keyBts = "val"; 19 | bytes memory valBts = "VAL"; 20 | bytes32 keyHash = keccak256(keyBts); // 749eb9a32604a1e3d5563e475f22a54221a22999f274fb5acd84a00d16053a11 21 | bytes32 valHash = keccak256(valBts); // 6a96595ccfcb78ff3e886e67a3c94a0c6c8fe147c51512c4f9b5e8aa8d636f07 22 | 23 | insert("val", "VAL"); 24 | 25 | assert(tree.rootEdge.node == valHash); 26 | assert(tree.rootEdge.label.data == keyHash); 27 | assert(tree.rootEdge.label.length == 256); 28 | } 29 | } 30 | 31 | 32 | contract TestPatriciaTreeInsertTwo is PatriciaTreeTest { 33 | function testImpl() internal { 34 | bytes memory valBts = "VAL"; 35 | bytes memory val2Bts = "VAL2"; 36 | 37 | bytes32 valHash = keccak256(valBts); // 6a96595ccfcb78ff3e886e67a3c94a0c6c8fe147c51512c4f9b5e8aa8d636f07 38 | bytes32 val2Hash = keccak256(val2Bts); // 780f7d9be6b7b221c27f7d5e84ff9ff220b60283dd7d012fbd9195bb6bb472aa 39 | 40 | insert("val", "VAL"); 41 | insert("val2", "VAL2"); 42 | 43 | var node = tree.nodes[tree.rootEdge.node]; 44 | var c0 = node.children[0]; 45 | var c1 = node.children[1]; 46 | 47 | assert(tree.rootEdge.label.length == 1); 48 | assert(c0.node == val2Hash); 49 | assert(c0.label.length == 254); 50 | assert(c1.node == valHash); 51 | assert(c1.label.length == 254); 52 | } 53 | } 54 | 55 | 56 | contract TestPatriciaTreeInsertOrderDoesNotMatter is STLTest { 57 | function testImpl() internal { 58 | var pt1 = new PatriciaTree(); 59 | var pt2 = new PatriciaTree(); 60 | pt1.insert("testkey", "testval"); 61 | pt1.insert("testkey2", "testval2"); 62 | pt1.insert("testkey3", "testval3"); 63 | pt1.insert("testkey4", "testval4"); 64 | pt1.insert("testkey5", "testval5"); 65 | 66 | pt2.insert("testkey2", "testval2"); 67 | pt2.insert("testkey", "testval"); 68 | pt2.insert("testkey5", "testval5"); 69 | pt2.insert("testkey3", "testval3"); 70 | pt2.insert("testkey4", "testval4"); 71 | 72 | assert(pt1.getRootHash() == pt2.getRootHash()); 73 | } 74 | } 75 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "es5", 4 | "module": "commonjs", 5 | "lib": ["es2016"] 6 | } 7 | } -------------------------------------------------------------------------------- /tslint.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "tslint:recommended", 3 | "rules": { 4 | "ordered-imports": false, 5 | "quotemark": false, 6 | "no-string-literal": false, 7 | "variable-name": false, 8 | "object-literal-sort-keys": false, 9 | "trailing-comma": false, 10 | "max-line-length": [false], 11 | "no-console": false, 12 | "member-ordering": false 13 | } 14 | 15 | } --------------------------------------------------------------------------------