# Intro to Hardened JavaScript _Status: WIP_. --- ## Intro to Hardened JavaScript _Status: WIP_. - JavaScript is popular - JavaScript has some messy parts - Stick to **Hardened JavaScript** --- Overview - Preface: Tools / Try it - Objects - Defensive Consistency - **Electronic Rights** - Reference: Data Structures, expressions, programs --- ## Try the Jessie linter (WIP!) ```bash yarn add eslint @jessie.js/eslint-plugin ``` Then in `package.json`: ```json "eslintConfig": { "extends": [ "@jessie.js" ] } ``` ---- ## Try the Jessie linter (WIP!) At top of `myFile.js`: ```javascript // @jessie-check ``` then run ```bash yarn eslint --fix path/to/your-source.js ``` Edit based on advice in error messages; try again. --- ## Simple Objects Singleton, stateless: ```javascript const origin = { getX: () => 0, getY: () => 0, }; ``` ```console > origin.getY() 0 ``` --- ## Object makers ```javascript const makeCounter = init => { let value = init; return { increment: () => (value += 1), decrement: () => (value -= 1), makeOffsetCounter: delta => makeCounter(value + delta), }; }; ``` ```javascript $ node Welcome to Node.js v14.16.0. > const c1 = makeCounter(1); c1.increment(); 2 > const c2 = c1.makeOffsetCounter(10); > c2.increment(); 13 > [c1.increment(), c2.increment()]; [ 3, 14 ] ``` --- ## Object makers ```javascript const makeCounter = init => { let value = init; return { increment: () => (value += 1), decrement: () => (value -= 1), ... ``` An object is a **record** of **functions** that close over **shared state**. ---- ## Object makers: no `this`, `class` ```javascript const makeCounter = init => { let value = init; return { increment: () => (value += 1), decrement: () => (value -= 1), ... ``` - The `this` keyword is not part of Jessie, so neither are constructors. - A `makeCounter` function takes the place of the `class Counter` constructor syntax. --- ## WARNING: Pervasive Mutability ```javascript > c1.increment = () => console.log('launch the missiles!'); [Function (anonymous)] > c1.increment() launch the missiles! ``` --- ## Defensive objects with `harden()` ```javascript const makeCounter = init => { let value = init; return harden({ increment: () => (value += 1), decrement: () => (value -= 1), makeOffsetCounter: delta => makeCounter(value + delta), }); }; ``` ```javascript > const c3 = Object.freeze(c1.makeOffsetCounter(10)); undefined > c3.increment = () => { console.log('launch the missiles!'); } TypeError: Cannot assign to read only property 'increment' of object '#<Object>' > c3.increment() 15 ``` ---- ## Defensive objects with `harden()` - _caveat_: exception is thrown in strict mode. REPL might not throw. - regardless, the object defended itself - Jessie pre-defines `harden` (_as does Agoric smart contract framework_) - see the `ses` [hardened Javascript package](https://github.com/endojs/endo/tree/master/packages/ses#harden) otherwise --- ## Types: advisory ```javascript // @ts-check /** @param {number} init */ const makeCounter = init => { let value = init; return { increment: () => (value += 1), decrement: () => (value -= 1), /** @param {number} delta */ makeOffsetCounter: delta => makeCounter(value + delta), }; }; ``` - [TypeScript: Documentation \- Type Checking JavaScript Files](https://www.typescriptlang.org/docs/handbook/type-checking-javascript-files.html) --- ## Types: advisory (cont.) If we're not careful, our clients can cause us to mis-behave: ```javascript > const evil = makeCounter('poison') > evil2.increment() 'poison1' ``` or worse: ``` > const evil2 = makeCounter({ valueOf: () => { console.log('launch the missiles!'); return 1; } }); > evil2.increment() launch the missiles! 2 ``` --- ## Types: defensive ```javascript /** @param {number | bignum} init */ const makeCounter = init => { let value = Nat(init); return harden({ increment: () => (value += 1n), /** @param {number | bignum} delta */ makeOffsetCounter: delta => makeCounter(value + Nat(delta)), }); }; ``` ```javascript > makeCounter('poison') Uncaught TypeError: poison is a string but must be a bigint or a number ``` --- ## Defensive Consistency A program is _defensively consistent_ if it will never provide incorrect service to its well behaved clients desspite arbitrary behavior of its other clients. - [Miller 2006](http://www.erights.org/talks/thesis/markm-thesis.pdf) --- ## Details: Stay Tuned - Ordinary programming in JavaScript follows in a later section - Much overlap with Java, Python, C, etc. _If you are **not** familiar with programming in some language, study details a bit and then come back here._ --- ## Electronic Rights [![Agoric \+ Protocol Labs // Higher\-order Smart Contracts across Chains \- Mark Miller](https://user-images.githubusercontent.com/150986/129462162-4599c0f4-8519-4a04-a707-88ef6e6044d7.png) ](https://youtu.be/iyuo0ymTt4g?t=1525) [**Watch 8 min**](https://youtu.be/iyuo0ymTt4g?t=1525) ---- ### Electonic Rights **Watch**: [the mint pattern](https://youtu.be/iyuo0ymTt4g?t=1525), an 8 minute segment starting at 25:00 ```javascript const makeMint = () => { const ledger = makeWeakMap(); const issuer = harden({ makeEmptyPurse: () => mint.makePurse(0), }); const mint = harden({ makePurse: initialBalance => { const purse = harden({ getIssuer: () => issuer, getBalance: () => ledger.get(purse), deposit: (amount, src) => { Nat(ledger.get(purse) + Nat(amount)); ledger.set(src, Nat(ledger.get(src) - amount)); ledger.set(purse, ledger.get(purse) + amount); }, withdraw: amount => { const newPurse = issuer.makeEmptyPurse(); newPurse.deposit(amount, purse); return newPurse; }, }); ledger.set(purse, initialBalance); return purse; }, }); return mint; }; ``` --- ## Agoric JavaScript APIs - [ERTP Introduction](https://agoric.com/documentation/getting-started/ertp-introduction.html#creating-assets-with-ertp) - [Introduction to Zoe](https://agoric.com/documentation/getting-started/intro-zoe.html#what-is-zoe) - [Remote object communication with E\(\)](https://agoric.com/documentation/guides/js-programming/eventual-send.html) --- ## DIY Data Structures Using only the object patterns we have seen so far: ```javascript const makeFlexList = () => { let head; let tail; const list = harden({ push: item => { const cell = { item, next: undefined, previous: tail }; tail = cell; if (head === undefined) { head = cell; } }, at: target => { if (!Number.isInteger(target) || target < 0) { throw RangeError(target); } const position = 0; for (let cell = head; cell !== undefined; cell = cell.next) { if (position === target) { return cell.item; } position += 1; } return undefined; }, forEach: f => { let position = 0; for (let cell = head; cell !== undefined; cell = cell.next) { f(cell.item, position, list); position += 1; } return false; }, }); return list; }; ``` _TODO: harden the cells? They don't escape, so technically we don't need to._ --- ## Built-in Data Structures: Array - syntax: `[...]` expressions - built-in `Array` objects - methods: `indexOf`, `reverse`, `sort`, ... - full API: [Array in MDN](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array) --- ## `Array` and `harden` **WARNING: mutable by default**: ```javascript const makeGame = (...players) => { // TODO: harden(players) return harden({ playing: () => players, }); }; ``` Game creator: ```javascript > const g1 = makeGame('alice', 'bob'); ``` Malicious client: ```javascript > const who = g1.playing(); > who.push('mallory'); g1.playing() [ 'alice', 'bob', 'mallory' ] ``` So we `harden(players)` before we `return ...`: ```javascript > who.push('mallory'); Uncaught TypeError: Cannot add property 2, object is not extensible ``` --- ## Built-in Data Structures: Map `m.set(key, value)`, `m.get(key)`, `m.has(key)`: ```javascript > const m1 = makeMap([['alice', 1], ['bob', 2], ['charlie', 3]]) Map(3) { 'alice' => 1, 'bob' => 2, 'charlie' => 3 } > m1.has('bob') true > m1.get('charlie') 3 > m1.set('dave', 'pickles') Map(4) { 'alice' => 1, 'bob' => 2, 'charlie' => 3, 'dave' => 'pickles' } ``` Full API: [Map \- JavaScript \| MDN](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Map) **NOTE:** `makeMap()` is a Jessie library function because `new Map()` is not included in Jessie. --- ## Built-in Data Structures: Set `s.delete(key)`, `s.size`: (_also on `Map`_) ```javascript > const s1 = makeSet([1, 2, 3]); Set(3) { 1, 2, 3 } > s1.has(2) true > s1.delete(2) true > s1.size 2 > [...s1] [ 1, 3 ] ``` Full API: [Set \- JavaScript \| MDN](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Set) --- ## Reference: JSON for Data JSON is a ubiquitous language for structured data: - Literals: - `1, 2, 3, true, false, "abc", null` - `"emoji: \uD83D\uDC36"`, `"🐶"` - Array: `[1, true, "three"]` - Record (aka Object): `{ "size": 5, "color": "blue" }` --- ## Reference: Justin for Safe Expressions - more flexible syntax: - string delimiters: `"abc"` or `'abc'` - trailing commas: `{ size: 1, }`, `[1, 2, 3,]` - un-quoted property names: `{ size: 1, color: "blue" }` - short-hand properties: `{ size }` - comments: - single line: `// ...` - multi-line: `/* ... */` - property lookup: `{ size: 1 }.size` - array index: `[1, 2, 3][2]` --- ## Justin: both bottom values (**:-/**) - `null` - `undefined` --- ## Justin: function and variable use In an environment with pre-declared variables: - variable use: `x` - excludes reserved words / keywords: `if`, `else`, ... - function call: `sqrt(2)`, `inventory.total()` - spread: `sum(1, 2, ...rest)`, `{ color: 'red', ...options }` --- ## Justin: Quasi-Literals - interpolation: `` `the answer is: ${a}` `` - tagged templates: ``html`<a href=${url}>${linkText}</a>` `` --- ## Justin: operators - add, subtract, etc.: `+`, `-`, `*`, `/`, `%` - parens `(x + 4) * y` - comparison: `===`, `!==`, `<`, `>`, `<=`, `>=` - **no `==`!** - relational: `&&`, `||` - bitwise: `&`, `|`, `^` - shift: `<<`, `>>` - unsigned: `>>>` - runtime type check: `typeof thing === "string"` - conditional (ternary operator): ``a > b ? a : b`` - void operator: `void ignoreMyResult()` --- ## Jessie: operators - pre-increment, decrement: `++x`, `--x` - post-increment, decrement: `x++`, `x--` --- ## Reference: Jessie for simple, universal safe mobile code statements, declarations: - end with `;` - arrow functions: - simple: `const double = x => x * 2;` - compound: ```javascript const f = (a, b) => { g(a); h(b); return a + b; }; ``` - bindings: `const c = a + b;`, `let x = 1;` - assignment: `x = x + 2`; - combined: `x += 2`; - `if (cond) { /* then block */ } else { /* else block */ }` - `switch`: ```javascript switch (value) { case 'a': case 'b': { /* 'a' and 'b' block */ break; } default: { /* default block */ } } ``` - `while (condition) { /* body */ }` - `break`, `continue` - `try` / `catch` / `finally` - `for (const x of items) { /* body */ }` - **no `for (const x in items)`!** Use `for (const x of Object.keys(items))` instead _TODO: accessor methods?_ --- ## Destructuring Assignments, Patterns, and Shorthand Properties ```javascript const s = order.size; // better as... const { size: s } = order; const size = order.size; // better as... const { size } = order; const s1 = sizes[0]; const s2 = sizes[1]; const rest = sizes.slice(2); // better as... const [s1, s2, ...rest] = sizes; // combine them and go nuts const [{ size: s1, color, ...details }, { size: s2 }] = orders; ``` --- ## Parameters: destructuring, optional, defaults ```javascript const serviceOrder = ({ size, shape }) => { ... }; ``` ```javascript const f = (a, opt) => { if (typeof opt !== 'undefined') { ... } }; ``` ```javascript const f = (a, b = 0) => { } ``` _TODO: examples that concretely motivate these features_ --- ## Modules - import: - `import harden from '@agoric/harden';` - `import { Nat } from '@agoric/nat';` - `import { Nat as N } from '@agoric/nat';` - `import * as fs from 'fs';` - export: - `export const f = ...`; - `export default f;` - `export { f, g, h};` --- ## Appendix / Colophon: Fodder / Brainstorm - structure from [E in a Walnut](http://www.skyhunter.com/marcs/ewalnut.html#SEC8) - as adapted in [Practical Security: The Mafia game — Monte 0\.1 documentation](https://monte.readthedocs.io/en/latest/ordinary-programming.html) - JSON / Justin / Jessie as in [Jessica](https://github.com/agoric-labs/jessica) - Build slides with [Remark](https://remarkjs.com/#1) - example: [kumc\-bmi/naaccr\-tumor\-data](https://github.com/kumc-bmi/naaccr-tumor-data) _TODO: facets_
{"metaMigratedAt":"2023-06-16T10:31:15.121Z","metaMigratedFrom":"YAML","title":"Intro to Hardened JavaScript","breaks":true,"slideOptions":"{\"theme\":\"sky\",\"previewLinks\":true}","contributors":"[{\"id\":\"cb157d25-12e4-4439-9995-d4ad60a29bee\",\"add\":239,\"del\":404},{\"id\":\"64595b09-a475-434e-94cc-793ab5c9f676\",\"add\":15059,\"del\":1334}]","description":"Status: WIP."}
    908 views
   Owned this note