Skip to content

Commit

Permalink
Merge pull request Agoric#23 from Agoric/add-gallery-demo
Browse files Browse the repository at this point in the history
Add gallery demo
  • Loading branch information
michaelfig authored Jun 14, 2019
2 parents 69404f7 + 8e26f13 commit 2e0d5be
Show file tree
Hide file tree
Showing 10 changed files with 243 additions and 88 deletions.
118 changes: 118 additions & 0 deletions README-ag-solo-gallery-demo.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,118 @@
# The ag-solo Tool

The `ag-solo` tool is used to create and launch a SwingSet vat-machine. This
machine can interact with the outside world either through a WebSocket
listening port, or by exchanging messages with a Cosmos-SDK -based
vat-machine (launched with `ag-chain-cosmos`).

## Build from source

```
$ make
$ npm install
```

(the `make` step builds some Go libraries and header files, which are
necessary for `npm install` to compile the `.so` extensions for node.js to
load, so it must be performed before `npm install` can work)

## Create a vat-machine

Choose a working directory for the machine. We'll call it `$BASEDIR`.

```
$ bin/ag-solo init $BASEDIR
$ cd $BASEDIR
$ ../bin/ag-solo start
```

Now open a web browser and point it at `http://localhost:8000`

## REPL

The browser page provides a basic REPL (Read-Eval-Print Loop). You can type
strings of Javascript (SES expressions, to be precise) into the text box and
they will be transmitted into the vat-machine for evaluation. The results are
displayed in the history log above the text box.

The SES expressions are evaluated in a confined environment which has access
to a couple of interesting objects (and more to come). These are available as
properties of the `home` object.

## Gallery Pixel Demo

In this Gallery Demo, the goal is to be able to create designs in a
pixel canvas. You start with access to the gallery and a handful of
methods that allow you to obtain pixels, color them, and buy or sell
pixels.

To access the gallery, type `home.gallery` in the REPL. `home.gallery` is a
Presence. In the SwingSet environment, Presences are remote references to objects on
other vats. To invoke them, use the `E` wrapper. For example, the
first thing you might want to do is tap the gallery faucet to get a
pixel for free:

```js
home.myFirstPixelPayment = E(home.gallery).tapFaucet()
```

This returns a presence for the pixel that you receive from the
faucet, and saves it under `home.myFirstPixelPayment`.

To color the pixel, we need to split the pixel into "transfer" and
"use" rights. The right to use the pixel means that you can color it,
and we'll be using it to color.

```js
home.transferAndUseRights = E(home.gallery).transformToTransferAndUse(home.myFirstPixelPayment)
```

```js
home.useAndTransfer.useRightPayment
```

------
This returns a Promise for the result. The REPL will update the history log
when the Promise resolves (to the number `100`, in this case).

For ease of experimentation, the contents of the history log are available in
the `history` object:

```js
1 + 2
# now history[0] = 3
history[0] + 4
# now history[1] = 7
E(home.purse).getBalance()
# history[2] starts out as a Promise, but is quickly replaced by 100
history[2] + 1
# history[3] = 101
```

A basic Purse transfer looks like this:

```js
E(home.purse).getIssuer()
E(history[1]).makeEmptyPurse()
# now history[2] is an empty Purse
E(history[2]).deposit(20, home.purse)
# that transfers 20 units from home.purse into the history[2] purse
E(history[2]).getBalance()
# should say 20
E(home.purse).getBalance()
# should say 80
```

## Modifying the demo objects

The `lib/ag-solo/vats/` directory contains the source code for all the Vats
created in the solo vat-machine. The actual filenames are enumerated in
`lib/ag-solo/init-basedir.js`, so if you add a new Vat, be sure to add it to
`init-basedir.js` too.

The objects added to `home` are created in `lib/ag-solo/vats/bootstrap.js`.

The REPL handler is in `lib/ag-solo/vats/vat-http.js`.

The HTML frontend code is pure JS/DOM (no additional libraries yet), in
`lib/ag-solo/html/index.html` and `lib/ag-solo/html/main.js`.
45 changes: 39 additions & 6 deletions lib/ag-solo/html/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -3,17 +3,50 @@
<head>
<meta charset="utf-8"/>
<title>SwingSet Solo REPL Demo</title>
<style>
* { padding: 0; margin: 0; }
canvas {
background: #eee;
display: block;
border: 3px solid black;
}
h1 {
text-align: center;
margin-top:40px;
}
.container {
display: grid;
grid-template-columns: 40px 520px auto 40px;
grid-template-rows: 40px auto;
}
.left {
grid-column: 2;
grid-row: 2;
}
.right {
grid-column: 3;
grid-row: 2;
margin: 20px;
}
</style>
</head>
<body>
<noscript>
You need to enable JavaScript to run this app.</noscript>
<div>Public Key: <span id="root">(unknown)</div>
<br>

<div>Use <code>home</code> to see useful objects, and <code>history[N]</code> to refer to result history</div>
<div id="history"></div>
<div>command[<span class="historyNumber">0</span>]> <input id="input" type="text" size="80"/> <input id="go" type="submit" value="eval"/></div>

<div class="container">
<div class="left">
<canvas id="myCanvas" width="500" height="500"></canvas>
</div>
<div class="right">
<div>Use <code>home</code> to see useful objects, and <code>history[N]</code> to refer to result history</div>
<div id="history"></div>
<div>command[<span class="historyNumber">0</span>]>
<input id="input" type="text" size="80"/>
<input id="go" type="submit" value="eval"/>
</div>
</div>
</div>
<script src="./main.js"></script>

</body>
Expand Down
27 changes: 25 additions & 2 deletions lib/ag-solo/html/main.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
/* global WebSocket fetch */
/* global WebSocket fetch document window */

function run() {
let nextHistNum = -1;
Expand Down Expand Up @@ -68,17 +68,40 @@ function run() {
}
}

const canvas = document.getElementById('myCanvas');
const ctx = canvas.getContext('2d');
const PIXEL_SIZE = 50; // actual pixels per pixel

function updateCanvas(state) {
console.log(state);
function renderPixel(x, y, color) {
ctx.beginPath();
ctx.rect(x * PIXEL_SIZE, y * PIXEL_SIZE, PIXEL_SIZE, PIXEL_SIZE);
ctx.fillStyle = color;
ctx.fill();
ctx.closePath();
}
for (let x = 0; x < state.length; x += 1) {
for (let y = 0; y < state.length; y += 1) {
renderPixel(x, y, state[x][y]);
}
}
}

function handleMessage(obj) {
// we receive commands to update result boxes
if (obj.type === 'updateHistory') {
// these args come from calls to vat-http.js updateHistorySlot()
updateHistory(obj.histnum, obj.result);
} else if (obj.type === 'updateCanvas') {
updateCanvas(JSON.parse(obj.state));
} else {
console.log(`unknown WS type in:`, obj);
}
}

// history updates (promises being resolved) are delivered by websocket
// history updates (promises being resolved) and canvas updates
// (pixels being colored) are delivered by websocket
// broadcasts
ws.addEventListener('message', ev => {
try {
Expand Down
2 changes: 1 addition & 1 deletion lib/ag-solo/vats/bootstrap.js
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ export default function setup(syscall, state, helpers) {
(E, D) => {
// TODO: Any wiring necessary to make the demo work.
async function startDemo(vats) {
await E(vats.demo).startup(vats.mint);
await E(vats.demo).startup(vats.http);
return vats.demo;
}

Expand Down
33 changes: 21 additions & 12 deletions lib/ag-solo/vats/vat-demo.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import harden from '@agoric/harden';
import { makeHandoffService } from '@agoric/ertp/more/handoff/handoff';
import { makeGallery } from '@agoric/ertp/more/pixels/gallery';

// This vat contains the server-side resources for the demo. To
// enable local testing, it is loaded both into the chain and solo vat machines.
Expand All @@ -14,31 +15,39 @@ import { makeHandoffService } from '@agoric/ertp/more/handoff/handoff';
// use (named the "chainBundle"). The client will fetch this record when it
// starts up, making it available to the REPL as `home.chainBundle`.

function build(E) {
let sharedMint;
let sharedPurse;
let sharedIssuer;
function build(E, log) {
let sharedGalleryUserFacet;
let sharedHandoffService;
let sharedDustIssuer;

async function startup(mint) {


async function startup(http) {
// define shared resources
sharedMint = await E(mint).makeMint('sharedRoot');
sharedPurse = await E(sharedMint).mint(500, 'shared purse');
sharedIssuer = await E(sharedPurse).getIssuer();

function stateChangeHandler(newState) {
E(http).updateCanvas(newState);
}

const canvasSize = 10;
const gallery = makeGallery(E, log, stateChangeHandler, canvasSize);
sharedGalleryUserFacet = gallery.userFacet;
const issuers = await E(gallery.userFacet).getIssuers();
sharedDustIssuer = issuers.dustIssuer;
sharedHandoffService = makeHandoffService();
}

async function getChainBundle(nickname) {
const purse = E(sharedMint).mint(100);
const issuer = sharedIssuer;
const gallery = sharedGalleryUserFacet;
const handoffService = sharedHandoffService;
const chainBundle = { purse, issuer, handoffService };
const purse = await sharedDustIssuer.makeEmptyPurse();
const chainBundle = { gallery, handoffService, purse};
return harden(chainBundle);
}

return harden({ startup, getChainBundle });
}

export default function setup(syscall, state, helpers) {
return helpers.makeLiveSlots(syscall, state, E => build(E), helpers.vatID);
return helpers.makeLiveSlots(syscall, state, E => build(E, helpers.log), helpers.vatID);
}
21 changes: 21 additions & 0 deletions lib/ag-solo/vats/vat-host.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
// Copyright (C) 2018 Agoric, under Apache License 2.0

import harden from '@agoric/harden';
import evaluate from '@agoric/evaluate';

import { makeContractHost } from '@agoric/ertp/core/contractHost';

function setup(syscall, state, helpers) {
return helpers.makeLiveSlots(
syscall,
state,
E =>
harden({
makeHost() {
return harden(makeContractHost(E, evaluate));
},
}),
helpers.vatID,
);
}
export default harden(setup);
11 changes: 8 additions & 3 deletions lib/ag-solo/vats/vat-http.js
Original file line number Diff line number Diff line change
Expand Up @@ -20,11 +20,12 @@ function build(E, D) {
handler = {};
if (ROLES.client) {
addReplHandler(handler, E, homeObjects, msg =>
D(commandDevice).sendBroadcast(msg));
D(commandDevice).sendBroadcast(msg),
);
}
if (ROLES.controller) {
handler.pleaseProvision = (obj) => {
const {nickname, pubkey} = obj;
handler.pleaseProvision = obj => {
const { nickname, pubkey } = obj;
return E(provisioner).pleaseProvision(nickname, pubkey);
};
}
Expand All @@ -43,6 +44,10 @@ function build(E, D) {
homeObjects.chain = p;
},

updateCanvas(canvas) {
D(commandDevice).sendBroadcast({ type: 'updateCanvas', state: canvas });
},

// devices.command invokes our inbound() because we passed to
// registerInboundHandler()
inbound(count, obj) {
Expand Down
24 changes: 0 additions & 24 deletions lib/ag-solo/vats/vat-mint.js

This file was deleted.

Loading

0 comments on commit 2e0d5be

Please sign in to comment.