From f8b6ec4c684b3892f0a6e0add1f3bebee6a7f944 Mon Sep 17 00:00:00 2001 From: Determinant Date: Thu, 11 Apr 2019 16:27:38 -0400 Subject: ... --- src/About.tsx | 98 ++++++++------- src/Grid.css | 37 ------ src/Grid.tsx | 70 ----------- src/Logo.tsx | 20 +-- src/Matrix.css | 37 ++++++ src/Matrix.tsx | 69 +++++++++++ src/Snow.tsx | 384 ++++++++++++++++++++++++++++----------------------------- src/index.tsx | 5 +- 8 files changed, 362 insertions(+), 358 deletions(-) delete mode 100644 src/Grid.css delete mode 100644 src/Grid.tsx create mode 100644 src/Matrix.css create mode 100644 src/Matrix.tsx (limited to 'src') diff --git a/src/About.tsx b/src/About.tsx index 6966d6f..83eb8c1 100644 --- a/src/About.tsx +++ b/src/About.tsx @@ -31,51 +31,61 @@ function About(props: AboutProps) { const { classes } = props; return (
- -
- The MIT License (MIT) -

Copyright 2019 Maofan "Ted" Yin

- -

Permission is hereby granted, free of charge, to any person obtaining a copy - of this software and associated documentation files (the "Software"), to deal - in the Software without restriction, including without limitation the rights - to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - copies of the Software, and to permit persons to whom the Software is - furnished to do so, subject to the following conditions:

- -

The above copyright notice and this permission notice shall be included in - all copies or substantial portions of the Software.

- -

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - THE SOFTWARE.

-
- - - Email: - 73d at tedyin dot com - - - GitHub: - - Determinant/snow-bft-demo - - - - Buy me a cup of coffee: - - Ether: 0xFEeed0f0BA87824819aabfa789f41FA2dd9ad81e - Bitcoin: 1CbVBB6Gv7WP4u39wsN416SJrjmvQDjggw +
+ + The MIT License (MIT) + + + + Copyright 2019 Maofan "Ted" Yin + + + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + THE SOFTWARE. + +
+ + + + Email: + 73d at tedyin dot com + + + GitHub: + + Determinant/snow-bft-demo + + + + Buy me a cup of coffee: + + Ether: 0xFEeed0f0BA87824819aabfa789f41FA2dd9ad81e + Bitcoin: 1CbVBB6Gv7WP4u39wsN416SJrjmvQDjggw + + -
-
-
-
- ); + + ); } export default withStyles(styles)(About); diff --git a/src/Grid.css b/src/Grid.css deleted file mode 100644 index 33c2725..0000000 --- a/src/Grid.css +++ /dev/null @@ -1,37 +0,0 @@ -div.grid { - display: inline-block; -} - -.grid div { - -webkit-transition: background-color 0.3s ease-out; - -moz-transition: background-color 0.3s ease-out; - -o-transition: background-color 0.3s ease-out; - transition: background-color 0.3s ease-out; - user-select: none; -} - -.gridCell div div { - height: 20px; - width: 20px; - margin: 2px; - display: inline-block; - cursor: pointer; -} - -.smallGridCell div div { - height: 15px; - width: 15px; - margin: 1px; - display: inline-block; - cursor: pointer; -} - -.gridRow div { - height: 24px; - text-align: center; -} - -.smallGridRow div { - height: 17px; - text-align: center; -} diff --git a/src/Grid.tsx b/src/Grid.tsx deleted file mode 100644 index d1fc973..0000000 --- a/src/Grid.tsx +++ /dev/null @@ -1,70 +0,0 @@ -import React from 'react'; -import 'typeface-roboto'; -import MGrid from '@material-ui/core/Grid'; -import orange from '@material-ui/core/colors/orange'; -import blue from '@material-ui/core/colors/blue'; -import Color from 'color'; -import './Grid.css'; - -interface GridProps { - onClickNode: (i: number, j: number) => void - onHoverNode: (i: number, j: number) => void - data: {d: number[], col: number}[][] -} - -export function getNodeColor(d: number, col: number) { - const color = [orange[300], blue[300]]; - let base = Color(color[col]).hsl().array(); - base[2] = 80; - return Color.hsl(base).darken(Math.min(d / 15, 0.5)).hex(); -} - -class Grid extends React.Component { - static getColor(s: {d: number[], col: number}) { - return getNodeColor(s.d[s.col], s.col); - } - mouseDown = false; - mousePos = {x: -1, y: -1}; - render() { - const { data, onClickNode, onHoverNode } = this.props; - const { gr, gc, l } = data.length <= 20 ? - { gr: 'gridRow', gc: 'gridCell', l: 24 } : - { gr: 'smallGridRow', gc: 'smallGridCell', l: 15 }; - return ( -
{ - event.preventDefault(); - this.mouseDown = true; - onClickNode(this.mousePos.y, this.mousePos.x); - }} - onMouseUp={event => { - event.preventDefault(); - this.mouseDown = false; - }} - onMouseOver={event => { - const bounds = (event.currentTarget as Element).getBoundingClientRect(); - const x = Math.floor((event.clientX - bounds.left) / l); - const y = Math.floor((event.clientY - bounds.top) / l); - console.log(y, x, this.mouseDown); - const t = this.mousePos; - this.mousePos = {x, y}; - if (!this.mouseDown) return; - if (t.x !== x || t.y !== y) - { - onHoverNode(y, x); - } - }}> - {data.map((row, i) => ( -
- {row.map((cell, j) => ( -
-
- ))} -
- ))} -
); - } -} - -export default Grid; diff --git a/src/Logo.tsx b/src/Logo.tsx index 1bac2b7..09700d1 100644 --- a/src/Logo.tsx +++ b/src/Logo.tsx @@ -1,12 +1,12 @@ import React from 'react'; export default (props: {style: {[key: string]: string | number }}) => - - - + + + ; diff --git a/src/Matrix.css b/src/Matrix.css new file mode 100644 index 0000000..18d5a4d --- /dev/null +++ b/src/Matrix.css @@ -0,0 +1,37 @@ +div.matrix { + display: inline-block; +} + +.matrix div { + -webkit-transition: background-color 0.3s ease-out; + -moz-transition: background-color 0.3s ease-out; + -o-transition: background-color 0.3s ease-out; + transition: background-color 0.3s ease-out; + user-select: none; +} + +.matrixCell div div { + height: 20px; + width: 20px; + margin: 2px; + display: inline-block; + cursor: pointer; +} + +.smallMatrixCell div div { + height: 15px; + width: 15px; + margin: 1px; + display: inline-block; + cursor: pointer; +} + +.matrixRow div { + height: 24px; + text-align: center; +} + +.smallMatrixRow div { + height: 17px; + text-align: center; +} diff --git a/src/Matrix.tsx b/src/Matrix.tsx new file mode 100644 index 0000000..53faf51 --- /dev/null +++ b/src/Matrix.tsx @@ -0,0 +1,69 @@ +import React from 'react'; +import 'typeface-roboto'; +import orange from '@material-ui/core/colors/orange'; +import blue from '@material-ui/core/colors/blue'; +import Color from 'color'; +import './Matrix.css'; + +interface MatrixProps { + onClickNode: (i: number, j: number) => void + onHoverNode: (i: number, j: number) => void + data: { d: number[], col: number }[][] +} + +export function getNodeColor(d: number, col: number) { + const color = [orange[300], blue[300]]; + let base = Color(color[col]).hsl().array(); + base[2] = 80; + return Color.hsl(base).darken(Math.min(d / 15, 0.5)).hex(); +} + +class Matrix extends React.Component { + static getColor(s: { d: number[], col: number }) { + return getNodeColor(s.d[s.col], s.col); + } + mouseDown = false; + mousePos = { x: -1, y: -1 }; + render() { + const { data, onClickNode, onHoverNode } = this.props; + const { mr, mc, l } = data.length <= 20 ? + { mr: 'matrixRow', mc: 'matrixCell', l: 24 } : + { mr: 'smallMatrixRow', mc: 'smallMatrixCell', l: 15 }; + return ( +
{ + event.preventDefault(); + this.mouseDown = true; + onClickNode(this.mousePos.y, this.mousePos.x); + }} + onMouseUp={event => { + event.preventDefault(); + this.mouseDown = false; + }} + onMouseOver={event => { + const bounds = (event.currentTarget as Element).getBoundingClientRect(); + const x = Math.floor((event.clientX - bounds.left) / l); + const y = Math.floor((event.clientY - bounds.top) / l); + console.log(y, x, this.mouseDown); + const t = this.mousePos; + this.mousePos = {x, y}; + if (!this.mouseDown) return; + if (t.x !== x || t.y !== y) + { + onHoverNode(y, x); + } + }}> + {data.map((row, i) => ( +
+ {row.map((cell, j) => ( +
+
+ ))} +
+ ))} +
); + } +} + +export default Matrix; diff --git a/src/Snow.tsx b/src/Snow.tsx index 7b296e6..b053a51 100644 --- a/src/Snow.tsx +++ b/src/Snow.tsx @@ -1,5 +1,5 @@ import React from 'react'; -import MGrid from '@material-ui/core/Grid'; +import Grid from '@material-ui/core/Grid'; import { Theme, withStyles, StyleRules } from '@material-ui/core/styles'; import TextField from '@material-ui/core/TextField'; import Table from '@material-ui/core/Table'; @@ -13,7 +13,8 @@ import Typography from '@material-ui/core/Typography'; import Link from '@material-ui/core/Link'; import { Line } from 'react-chartjs-2'; import Color from 'color'; -import Grid, {getNodeColor} from './Grid'; + +import Matrix, { getNodeColor } from './Matrix'; const styles = (theme: Theme): StyleRules => ({ inputLabel: { @@ -62,14 +63,15 @@ function getRandomInt(max: number) { return Math.floor(Math.random() * Math.floor(max)); } +/* Code from: https://stackoverflow.com/a/11935263/544806 */ function getRandomSubarray(arr: number[], size: number) { let shuffled = arr.slice(0), i = arr.length, min = i - size, temp, index; while (i-- > min) { - index = Math.floor((i + 1) * Math.random()); - temp = shuffled[index]; - shuffled[index] = shuffled[i]; - shuffled[i] = temp; - } + index = Math.floor((i + 1) * Math.random()); + temp = shuffled[index]; + shuffled[index] = shuffled[i]; + shuffled[i] = temp; + } return shuffled.slice(min); } @@ -83,7 +85,7 @@ class Snow extends React.Component { { let r = []; for (let j = 0; j < n; j++) - r.push({d: [0, 0], col: getRandomInt(2)}); + r.push({ d: [0, 0], col: getRandomInt(2) }); d.push(r); } return d; @@ -100,7 +102,7 @@ class Snow extends React.Component { ticking: false, simulationSpeed: 100, dialogOpen: false, - dialogMsg: {title: '', message: ''}, + dialogMsg: { title: '', message: '' }, nError: false, kError: false, alphaError: false, @@ -128,12 +130,12 @@ class Snow extends React.Component { return this.state.colorMatrix[r][c]; } - setNodeState(n: number, u: number, s: {d: number[], col: number}) { + setNodeState(n: number, u: number, s: { d: number[], col: number }) { let r = Math.floor(u / n); let c = u % n; let m = [...this.state.colorMatrix]; m[r][c] = s; - this.setState({colorMatrix: m}); + this.setState({ colorMatrix: m }); } tick(n: number, m: number) { @@ -196,7 +198,7 @@ class Snow extends React.Component { } pauseTick() { - this.setState({ticking: false}); + this.setState({ ticking: false }); } startTick() { @@ -255,7 +257,7 @@ class Snow extends React.Component { } autoTick() { - this.setState({ticking: true}); + this.setState({ ticking: true }); setTimeout(() => { let active = this.tick(this.config.n, this.config.nodesPerTick); this.config.iter++; @@ -291,187 +293,181 @@ class Snow extends React.Component { const { classes } = this.props; return ( - - - this.flipNode(i, j)} - onHoverNode={(i: number, j: number) => this.flipNode(i, j)} - /> -
- { - let datasets = this.state.dcnts.map((dd, c) => dd.map((line, i) => { - const base = getNodeColor(watchedD[i], c); - return { - data: line, - label: `${c == 0 ? 'A' : 'B'}(d-${watchedD[i]})`, - borderColor: base, - backgroundColor: Color(base).fade(0.5).rgb().string(), - borderWidth: 2 - }; - })).flat(); - return { - datasets, - labels: this.state.ticks - } - }} - options={{ - scales: { yAxes: [{ ticks: {min: -this.state.N, max: this.state.N}}]}, - maintainAspectRatio: false - }}/> -
-
- - -

- This demo shows the Snowball protocol used as the core of a peer-to-peer payment system, Avalanche, introduced in  - - this paper - -  . It visualizes the process of a binary, - single-decree, probabilistic Snowball consensus that harnesses - metastability to guarantee safety. - Little squares represent different nodes, wherein - the color of each square represents its current - proposal. Darkness of the color shows the node's - conviction in that proposal. Expectedly, all nodes - will collapse to the same color in the end. -

-

- Try to click or move the mouse when clicked to flip the - color of squares. Are you able to prevent them from - going to a single color? -

-
- - - - - n = - - - this.setState({n: event.target.value, nError: false})}/> - 2 - {this.state.nError && - n must be in 2..40} - - - - - k = - - - this.setState({k: event.target.value, kError: false})}/> - {this.state.kError && - k must be in 1..(n-1)} - - - - - alpha = - - - this.setState({alpha: event.target.value, alphaError: false})}/> - {this.state.alphaError && - alpha must be in (k/2, k]} - - - - - nodesPerTick = - - - this.setState({nodesPerTick: event.target.value, nodesPerTickError: false})}/> - {this.state.nodesPerTickError && - nodesPerTick must be in 1..n} - - - - - maxInactiveTicks = - - - this.setState({maxInactiveTicks: event.target.value, maxInactiveTicksError: false})}/> - {this.state.maxInactiveTicksError && - maxInactiveTicks must be in 1..1000000} - - - - - simulationSpeed - - - this.setState({simulationSpeed: value})} /> - - - -
-
-
- - - - - - - - - - - - - - - - - -
- - - ); + + + this.flipNode(i, j)} + onHoverNode={(i: number, j: number) => this.flipNode(i, j)} /> +
+ { + let datasets = this.state.dcnts.map((dd, c) => dd.map((line, i) => { + const base = getNodeColor(watchedD[i], c); + return { + data: line, + label: `${c == 0 ? 'A' : 'B'}(d-${watchedD[i]})`, + borderColor: base, + backgroundColor: Color(base).fade(0.5).rgb().string(), + borderWidth: 2 + }; + })).flat(); + return { + datasets, + labels: this.state.ticks + } + }} + options={{ + scales: { yAxes: [{ ticks: { min: -this.state.N, max: this.state.N }}]}, + maintainAspectRatio: false + }} /> +
+
+ + + This demo shows the Snowball protocol used as the core of a peer-to-peer payment system, Avalanche, introduced in  + + this paper + +  . It visualizes the process of a binary, + single-decree, probabilistic Snowball consensus that harnesses + metastability to guarantee safety. + Little squares represent different nodes, wherein + the color of each square represents its current + proposal. Darkness of the color shows the node's + conviction in that proposal. Expectedly, all nodes + will collapse to the same color in the end. + + + Try to click or move the mouse when clicked to flip the + color of squares. Are you able to prevent them from + going to a single color? + + + + + + n = + + + this.setState({ n: event.target.value, nError: false })} /> + 2 + {this.state.nError && n must be in 2..40} + + + + + k = + + + this.setState({ k: event.target.value, kError: false })}/> + {this.state.kError && k must be in 1..(n-1)} + + + + + alpha = + + + this.setState({ alpha: event.target.value, alphaError: false })}/> + {this.state.alphaError && alpha must be in (k/2, k]} + + + + + nodesPerTick = + + + this.setState({ nodesPerTick: event.target.value, nodesPerTickError: false })}/> + {this.state.nodesPerTickError && nodesPerTick must be in 1..n} + + + + + maxInactiveTicks = + + + this.setState({ maxInactiveTicks: event.target.value, maxInactiveTicksError: false })}/> + {this.state.maxInactiveTicksError && maxInactiveTicks must be in 1..1000000} + + + + + simulationSpeed + + + this.setState({ simulationSpeed: value })} /> + + + +
+
+
+ + + + + + + + + + + + + + + + + +
+ + ); } } diff --git a/src/index.tsx b/src/index.tsx index 36bb16c..7f51b9b 100644 --- a/src/index.tsx +++ b/src/index.tsx @@ -3,13 +3,12 @@ import ReactDOM from 'react-dom'; import 'typeface-roboto'; import { Theme, withStyles, MuiThemeProvider } from '@material-ui/core/styles'; import CssBaseline from '@material-ui/core/CssBaseline'; +import Grid from '@material-ui/core/Grid'; import AppBar from '@material-ui/core/AppBar'; import Toolbar from '@material-ui/core/Toolbar'; import Typography from '@material-ui/core/Typography'; import Tabs from '@material-ui/core/Tabs'; -import Tab, { TabProps } from '@material-ui/core/Tab'; -import { LinkProps } from '@material-ui/core/Link'; -import Grid from '@material-ui/core/Grid'; +import Tab from '@material-ui/core/Tab'; import { HashRouter as Router, RouteComponentProps, withRouter, Route, Link, Redirect, Switch } from "react-router-dom"; import { TransitionGroup, CSSTransition } from "react-transition-group"; -- cgit v1.2.3-70-g09d2