diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/About.tsx | 7 | ||||
-rw-r--r-- | src/Grid.css | 26 | ||||
-rw-r--r-- | src/Grid.tsx | 65 | ||||
-rw-r--r-- | src/Snow.tsx | 39 | ||||
-rw-r--r-- | src/index.tsx | 7 |
5 files changed, 82 insertions, 62 deletions
diff --git a/src/About.tsx b/src/About.tsx index 118b961..6966d6f 100644 --- a/src/About.tsx +++ b/src/About.tsx @@ -13,7 +13,6 @@ const styles = (theme: Theme): StyleRules => ({ body: { margin: '0 auto', width: 800, - fontSize: 16 }, pre: { fontFamily: "Monospace" @@ -32,6 +31,7 @@ function About(props: AboutProps) { const { classes } = props; return ( <div className={classes.body}> + <Typography variant="body1"> <article> The MIT License (MIT) <p>Copyright 2019 Maofan "Ted" Yin</p> @@ -60,6 +60,10 @@ function About(props: AboutProps) { 73d at tedyin dot com </ListItem> <ListItem> + <span className={classes.infoField}>GitHub:</span> + <Link href="https://github.com/Determinant/snow-bft-demo" target="_blank" rel="noopener"> + Determinant/snow-bft-demo + </Link> </ListItem> <ListItem> <span className={classes.infoField}>Buy me a cup of coffee:</span> @@ -69,6 +73,7 @@ function About(props: AboutProps) { </List> </ListItem> </List> + </Typography> </div> ); } diff --git a/src/Grid.css b/src/Grid.css index 620d114..33c2725 100644 --- a/src/Grid.css +++ b/src/Grid.css @@ -2,39 +2,27 @@ div.grid { display: inline-block; } - .grid div { - -webkit-transition: background-color 0.1s ease-out; - -moz-transition: background-color 0.1s ease-out; - -o-transition: background-color 0.1s ease-out; - transition: background-color 0.1s ease-out; + -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 div { +.gridCell div div { height: 20px; width: 20px; margin: 2px; display: inline-block; -} - -.gridCell div div { - height: 24px; - width: 24px; - display: inline-block; cursor: pointer; } -.smallGridCell div div div { +.smallGridCell div div { height: 15px; width: 15px; margin: 1px; display: inline-block; -} - -.smallGridCell div div { - height: 17px; - width: 17px; - display: inline-block; cursor: pointer; } diff --git a/src/Grid.tsx b/src/Grid.tsx index 1bfcaee..d1fc973 100644 --- a/src/Grid.tsx +++ b/src/Grid.tsx @@ -3,17 +3,10 @@ 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 { Theme, withStyles, StyleRules } from '@material-ui/core/styles'; import Color from 'color'; -import { TransitionGroup, CSSTransition } from "react-transition-group"; import './Grid.css'; -const styles = (theme: Theme): StyleRules => ({ -}); - interface GridProps { - classes: { - }, onClickNode: (i: number, j: number) => void onHoverNode: (i: number, j: number) => void data: {d: number[], col: number}[][] @@ -30,36 +23,48 @@ class Grid extends React.Component<GridProps> { static getColor(s: {d: number[], col: number}) { return getNodeColor(s.d[s.col], s.col); } + mouseDown = false; + mousePos = {x: -1, y: -1}; render() { - const { classes, data, onClickNode, onHoverNode } = this.props; - const { gr, gc } = data.length <= 20 ? - { gr: 'gridRow', gc: 'gridCell' } : - { gr: 'smallGridRow', gc: 'smallGridCell' }; + 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 ( - <div className={`grid ${gr} ${gc}`}> - { - data.map((row, i) => ( - <div key={i}> + <div className={`grid ${gr} ${gc}`} + onMouseDown={event => { + 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) { - row.map((cell, j) => ( - <div - onClick={event => onClickNode(i, j)} - onMouseOver={event => { - if (event.buttons === 1 || event.buttons === 3) - onHoverNode(i, j); - }}> + onHoverNode(y, x); + } + }}> + {data.map((row, i) => ( + <div key={i}> + {row.map((cell, j) => ( <div key={j} style={{backgroundColor: Grid.getColor(cell)}}> </div> - </div> - )) - } + ))} </div> - )) - } - </div> - ); + ))} + </div>); } } -export default withStyles(styles)(Grid); +export default Grid; diff --git a/src/Snow.tsx b/src/Snow.tsx index 4bba422..7b296e6 100644 --- a/src/Snow.tsx +++ b/src/Snow.tsx @@ -9,9 +9,11 @@ import TableCell from '@material-ui/core/TableCell'; import Button from '@material-ui/core/Button'; import Slider from '@material-ui/lab/Slider'; import FormGroup from '@material-ui/core/FormGroup'; -import Grid, {getNodeColor} from './Grid'; +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'; const styles = (theme: Theme): StyleRules => ({ inputLabel: { @@ -154,7 +156,7 @@ class Snow extends React.Component<SnowProps> { let s = this.getNodeState(n, u); for (let c = 0; c < 2; c++) { - if (cnt[c] > this.config.alpha) + if (cnt[c] >= this.config.alpha) { s.d[c]++; if (s.d[c] > s.d[s.col]) @@ -210,7 +212,7 @@ class Snow extends React.Component<SnowProps> { this.setState({ nError: true }); return; } - if (!Number.isInteger(k) || k < 1 || k > N) + if (!Number.isInteger(k) || k < 1 || k >= N) { this.setState({ kError: true }); return; @@ -296,6 +298,7 @@ class Snow extends React.Component<SnowProps> { onClickNode={(i: number, j: number) => this.flipNode(i, j)} onHoverNode={(i: number, j: number) => this.flipNode(i, j)} /> + <div style={{position: 'relative', height: '40vh'}}> <Line data={() => { let datasets = this.state.dcnts.map((dd, c) => dd.map((line, i) => { const base = getNodeColor(watchedD[i], c); @@ -312,14 +315,34 @@ class Snow extends React.Component<SnowProps> { labels: this.state.ticks } }} - options={{ scales: { yAxes: [{ ticks: {min: -this.state.N, max: this.state.N}}]}}}/> + options={{ + scales: { yAxes: [{ ticks: {min: -this.state.N, max: this.state.N}}]}, + maintainAspectRatio: false + }}/> + </div> </MGrid> <MGrid item lg={4} xs={12}> + <Typography variant="body1"> + <p> + This demo shows the Snowball protocol used as the core of a peer-to-peer payment system, Avalanche, introduced in + <Link href="https://avalanchelabs.org/QmT1ry38PAmnhparPUmsUNHDEGHQusBLD6T5XJh4mUUn3v.pdf" target="_blank" rel="noopener"> + this paper + </Link> + . 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. + </p> <p> 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? </p> + </Typography> <Table> <TableBody> <TableRow> @@ -352,7 +375,7 @@ class Snow extends React.Component<SnowProps> { error={this.state.kError} onChange={event => this.setState({k: event.target.value, kError: false})}/> {this.state.kError && - <span className={classes.errorHint}>k must be in 1..n</span>} + <span className={classes.errorHint}>k must be in 1..(n-1)</span>} </TableCell> </TableRow> <TableRow> @@ -421,7 +444,7 @@ class Snow extends React.Component<SnowProps> { <div className={classes.buttonSpacer} /> <div className={classes.bottomButtons}> <MGrid container item spacing={16}> - <MGrid item lg={4} xs={12}> + <MGrid item md={4} xs={12}> <FormGroup> <Button variant="contained" color="primary" @@ -429,7 +452,7 @@ class Snow extends React.Component<SnowProps> { disabled={this.state.ticking}>Run</Button> </FormGroup> </MGrid> - <MGrid item lg={4} xs={12}> + <MGrid item md={4} xs={12}> <FormGroup> <Button variant="contained" color="primary" @@ -437,7 +460,7 @@ class Snow extends React.Component<SnowProps> { disabled={!this.state.ticking}>Stop</Button> </FormGroup> </MGrid> - <MGrid item lg={4} xs={12}> + <MGrid item md={4} xs={12}> <FormGroup> <Button variant="contained" color="primary" diff --git a/src/index.tsx b/src/index.tsx index baa46f5..36bb16c 100644 --- a/src/index.tsx +++ b/src/index.tsx @@ -96,15 +96,15 @@ class MainTabs extends React.Component<MainTabsProps> { position="absolute" className={classes.appBar}> <Toolbar className={classes.toolbar}> - <Typography component="h1" variant="h6" color="inherit" noWrap className={classes.title}> - <Logo style={{width: '5em', verticalAlign: 'bottom', marginRight: '0.5em'}}/> + <Typography color="inherit" noWrap className={classes.title} style={{fontSize: 28, fontWeight: 300}}> + <Logo style={{height: 42, verticalAlign: 'bottom', marginRight: '0.5em'}}/> Snow BFT Demo </Typography> <Tabs classes={{ indicator: classes.indicator }} value={this.props.history.location.pathname} onChange={this.handleChangeTab}> - <Tab label="Snow" {...{component: Link, to: "/snow"} as any} value="/snow" /> + <Tab label="Demo" {...{component: Link, to: "/snow"} as any} value="/snow" /> <Tab label="About" {...{component: Link, to: "/about"} as any} value="/about" /> </Tabs> </Toolbar> @@ -124,7 +124,6 @@ class MainTabs extends React.Component<MainTabsProps> { }}> <div className={classes.content}> <Switch location={location}> - {console.log(location)} <Route exact path="/snow" component={Snow} /> <Route exact path="/about" component={About} /> <Route exact path="/" render={() => <Redirect to="/snow" />}/> |