diff --git a/main/.vuepress/config.js b/main/.vuepress/config.js
index 96aa4a3f5..d2a7e7fc3 100644
--- a/main/.vuepress/config.js
+++ b/main/.vuepress/config.js
@@ -21,6 +21,34 @@ module.exports = {
// Links must be absolute with trailing slash '/guide/'
// Trailing slash implies it is looking for a .md file
sidebar: {
+ '/getting-started/': [
+ {
+ title: 'Getting Started',
+ path: '/getting-started/',
+ collapsable: false,
+ sideBarDepth: 3,
+ children: [
+ {
+ title: 'Agoric\'s Cosmic SwingSet',
+ path: '/getting-started/',
+ collapsable: false,
+ sideBarDepth: 3
+ },
+ {
+ title: 'Pixel Demo',
+ path: '/getting-started/pixel-demo',
+ collapsable: false,
+ sideBarDepth: 3
+ },
+ {
+ title: 'Timer Service',
+ path: '/getting-started/timer-service',
+ collapsable: false,
+ sideBarDepth: 3
+ }
+ ]
+ },
+ ],
'/ertp/': [
{
title: 'ERTP Guide',
diff --git a/main/.vuepress/themeConfig/nav.js b/main/.vuepress/themeConfig/nav.js
index 865546268..b81ede6a6 100644
--- a/main/.vuepress/themeConfig/nav.js
+++ b/main/.vuepress/themeConfig/nav.js
@@ -6,7 +6,46 @@
module.exports = [
{
- text: 'ERTP ', // spaces to add some distance to next link
+ text: 'Getting Started',
+ ariaLabel: 'Getting Started Menu',
+ link: '/getting-started/',
+ items: [
+ {
+ text: 'Agoric\'s Cosmic SwingSet',
+ ariaLabel: 'Agoric\'s Cosmic SwingSet Menu',
+ link: '/getting-started/',
+ },
+ {
+ text: 'Timer Service',
+ ariaLabel: 'Timer Service Link',
+ link: '/getting-started/timer-service'
+ },
+ {
+ text: 'Pixel Demo',
+ ariaLabel: 'Pixel Demo Link',
+ link: '/getting-started/pixel-demo'
+ },
+ {
+ text: 'Tutorials',
+ ariaLabel: 'Tutorials Menu',
+ link: '/smart-contracts-tutorials/guess37-one',
+ items: [
+ {
+ text: 'Guess 37 - One Participant',
+ ariaLabel: 'Guess 37 - One Participant',
+ link: '/smart-contracts-tutorials/guess37-one'
+ },
+ {
+ text: 'Guess37 - Multiple Participants',
+ ariaLabel: 'Guess37 Multiple Participants',
+ link: '/smart-contracts-tutorials/guess37-multiple'
+ }
+ ]
+ }
+ ]
+ },
+ {
+ text: 'ERTP', // spaces to add some distance to next link
ariaLabel: 'ERTP Menu',
link: '/ertp/guide/',
items: [
@@ -54,23 +93,6 @@ module.exports = [
}
],
},
- {
- text: 'Tutorials',
- ariaLabel: 'Tutorials Menu',
- link: '/smart-contracts-tutorials/guess37-one',
- items: [
- {
- text: 'Guess 37 - One Participant',
- ariaLabel: 'Guess 37 - One Participant',
- link: '/smart-contracts-tutorials/guess37-one'
- },
- {
- text: 'Guess37 - Multiple Participants',
- ariaLabel: 'Guess37 Multiple Participants',
- link: '/smart-contracts-tutorials/guess37-multiple'
- }
- ]
- },
{
text: 'Learn More',
ariaLabel: 'Learn More Menu',
diff --git a/main/getting-started/README.md b/main/getting-started/README.md
new file mode 100644
index 000000000..afae9c6cb
--- /dev/null
+++ b/main/getting-started/README.md
@@ -0,0 +1,161 @@
+# Agoric's Cosmic SwingSet
+
+Agoric's Cosmic SwingSet enables developers to test smart contracts build with [ERTP](https://github.com/Agoric/ERTP) in various blockchain setup environments
+
+## Overview
+This document gives an overview of the process of setting up
+
+1. a local environment that will allow you to build and debug
+2. an environment that emulates a remote setup for testing
+3. deploying to the TestNet
+4. (not available yet) deploying to MainNet
+
+In order to build a DeFi app in the SwingSet environment, your team will have to write code for three things:
+
+* The UI that displays in interface and talks to the handler via WebSockets
+* The Handler that receives commands from the user via WebSockets and sends
+ transactions to the local solo SwingSet for relay to the Chain
+* The Dapp code that runs in the Chain SwingSet and has access to objects on
+ other chains
+
+
+To develop and deploy new code, you'll have to clone our [Cosmic SwingSet](https://github.com/Agoric/cosmic-swingset) repo from GitHib
+
+```sh
+$ git clone https://github.com/Agoric/cosmic-swingset
+```
+
+There is more thorough documentation there. This is an overview.
+
+
+## Different ways to run the Pixel Demo
+
+Running the demo requires a local solo node to serve as your access point.
+Whichever environment you want to develop in, you'll start by building a solo
+node from the source code.
+
+**Choose a scenario:**
+
+## Scenario 3: no testnet
+### Develop off-chain demo locally
+
+In this scenario, you run:
+- a **solo node** with the server-side Pixel Demo running and exposing an HTTP server in localhost
+- a **web browser** connecting to the solo node and enabling user interaction with the Pixel Demo
+
+No blockchain is involved.
+
+| |
+|:--:|
+| *A Local Solo SwingSet. Notice that there's no chain.* |
+
+Run:
+```sh
+make scenario3-setup
+make scenario3-run-client
+```
+
+[`lib/ag-solo/vats/vat-demo.js`](https://github.com/Agoric/cosmic-swingset/tree/master/lib/ag-solo/vats/vat-demo.js) contains the code running a vat with
+the Pixel Gallery Demo.
+
+Also, as part of `make scenario3-setup`, `bin/ag-solo init ` gets called and all the
+content of the [`vats`](https://github.com/Agoric/cosmic-swingset/tree/master/lib/ag-solo/vats) directory gets copied to the ``
+
+The objects added to `home` are created in
+[`lib/ag-solo/vats/vat-demo.js`](https://github.com/Agoric/cosmic-swingset/tree/master/lib/ag-solo/vats/vat-demo.js).
+
+The [REPL](https://en.wikipedia.org/wiki/Read%E2%80%93eval%E2%80%93print_loop] handler is in
+[`lib/ag-solo/vats/vat-http.js`](https://github.com/Agoric/cosmic-swingset/tree/master/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`.
+
+
+## Scenario 2: a single local testnet node
+### Develop on-chain demo
+
+In this scenario, you run:
+- one or several **solo node(s)** each exposing an HTTP server in localhost (each to a different port)
+- a **single local blockchain testnet node** with the server-side Pixel Demo running
+- a **web browser** connecting to each solo node via a different port and enabling user interaction with the Pixel Demo
+
+
+| |
+|:--:|
+| *A Local Chain SwingSet. Notice that the chain is private.* |
+
+The solo nodes communicate with the testnet node
+
+Before using this scenario, it is recommanded that you test your code with Scenario 3.
+
+Prepare the chain and solo nodes:
+```sh
+make scenario2-setup BASE_PORT=8000 NUM_SOLOS=3
+```
+
+fThis prepares for creating 3 solo nodes. Each node exposes a web server to a different port. The
+ports start at `8000` (`BASE_PORT`). So the solo node ports here will be `8000`, `8001` and `8002`
+
+Start the chain:
+```sh
+make scenario2-run-chain
+```
+
+Wait about 5 seconds for the chain to produce its first block, then switch to another terminal:
+```sh
+make scenario2-run-client BASE_PORT=8000
+```
+
+You can communicate with the node by opening http://localhost:8000/
+
+You can start other solo nodes with `make scenario2-run-client BASE_PORT=8001` and `make
+scenario2-run-client BASE_PORT=8002` and communicate with them respectively with on
+http://localhost:8001/ and http://localhost:8002/
+
+
+## Scenario 1: your own local testnet
+### Develop testnet provisioner
+
+In this scenario, you run:
+- a **solo node** exposing an HTTP server in localhost
+- a **several local blockchain testnet nodes** with the server-side Pixel Demo running on top.
+- a **web browser** connecting to the solo node and enabling user interaction with the Pixel Demo
+
+This scenario is only useful for moving toward deploying the local source code as a new
+testnet. Before using this scenario, you should test your on-chain code under Scenario 2.
+
+| |
+|:--:|
+| *A Shared Chain setup. You can connect to the chain with multiple solo SwingSets.* |
+
+```sh
+make scenario1-setup
+make scenario1-run-chain
+```
+
+Wait until the bootstrap produces a provisioning server URL and visit it. Then run in another terminal:
+
+```sh
+make scenario1-run-client
+```
+
+See [Testnet Tutorial](https://github.com/Agoric/cosmic-swingset#testnet-tutorial) for more guidance.
+
+#### Scenario 0: a public testnet (kick the tires)
+
+In this scenario, you run:
+- a **solo node** exposing an HTTP server in localhost
+- a **web browser** connecting to the solo node and enabling user interaction with the Pixel Demo
+
+This scenario assumes your solo node can access a **blockchain running on the Internet**
+
+To run the solo node using the current directory's source code against a public testnet, use:
+```
+$ make scenario0-setup
+$ make scenario0-run-client
+```
+
+Alternatively, running the solo node from a Docker image and no local source code is described in the [top section](#overview).
+
+Now go to http://localhost:8000/ to interact with your new solo node.
+
diff --git a/main/getting-started/assets/LocalChain.pdf b/main/getting-started/assets/LocalChain.pdf
new file mode 100644
index 000000000..72bbc1283
Binary files /dev/null and b/main/getting-started/assets/LocalChain.pdf differ
diff --git a/main/getting-started/assets/LocalChain.png b/main/getting-started/assets/LocalChain.png
new file mode 100644
index 000000000..84a2dcf7e
Binary files /dev/null and b/main/getting-started/assets/LocalChain.png differ
diff --git a/main/getting-started/assets/LocalSolo.pdf b/main/getting-started/assets/LocalSolo.pdf
new file mode 100644
index 000000000..7ca2c5d79
Binary files /dev/null and b/main/getting-started/assets/LocalSolo.pdf differ
diff --git a/main/getting-started/assets/LocalSolo.png b/main/getting-started/assets/LocalSolo.png
new file mode 100644
index 000000000..0e4ca8717
Binary files /dev/null and b/main/getting-started/assets/LocalSolo.png differ
diff --git a/main/getting-started/assets/SharedChain.pdf b/main/getting-started/assets/SharedChain.pdf
new file mode 100644
index 000000000..944064824
Binary files /dev/null and b/main/getting-started/assets/SharedChain.pdf differ
diff --git a/main/getting-started/assets/SharedChain.png b/main/getting-started/assets/SharedChain.png
new file mode 100644
index 000000000..5b9ed899e
Binary files /dev/null and b/main/getting-started/assets/SharedChain.png differ
diff --git a/main/getting-started/assets/pixel-demo.png b/main/getting-started/assets/pixel-demo.png
new file mode 100644
index 000000000..8732eba49
Binary files /dev/null and b/main/getting-started/assets/pixel-demo.png differ
diff --git a/main/getting-started/assets/rplace.png b/main/getting-started/assets/rplace.png
new file mode 100644
index 000000000..1a15a5e71
Binary files /dev/null and b/main/getting-started/assets/rplace.png differ
diff --git a/main/getting-started/pixel-demo.md b/main/getting-started/pixel-demo.md
new file mode 100644
index 000000000..19fe303eb
--- /dev/null
+++ b/main/getting-started/pixel-demo.md
@@ -0,0 +1,162 @@
+# Pixel Demo
+
+This demo is roughly based on [Reddit's
+r/Place](https://en.wikipedia.org/wiki/Place_(Reddit)), but has a
+number of additional features that showcase the unique affordances of
+the Agoric platform, including: higher-order contracts, easy creation
+of new assets, and safe code reusability.
+
+| ![Reddit's r/place](./assets/rplace.png) |
+|:--:|
+| *Reddit's r/place as a social experiment in cooperation* |
+
+
+## Installation
+
+| ![Pixel Gallery](./assets/pixel-demo.png) |
+|:--:|
+| *The testnet pixel demo. Slightly fewer pixels.* |
+
+
+The pixel demo runs on [our private testnet](https://github.com/Agoric/cosmic-swingset#agorics-cosmic-swingset). For instructions on how to
+run a local, off-chain version for yourself, please see [Scenario 3
+here](https://github.com/Agoric/cosmic-swingset#different-scenarios).
+
+## Getting Started
+
+In the pixel demo, the goal is to be able to amass enough pixels to
+draw a design on a pixel canvas. You start out empty-handed, with no
+money and no pixels to your name. However, you do have access to the *gallery*, the
+administrator of the canvas. The gallery has a handful of
+methods that allow you to obtain a few pixels for free, color them,
+sell them, and buy more.
+
+To access the gallery, type `home.gallery` in the REPL. `home.gallery`
+is a remote object (what we call a *presence*). It actually lives in
+another environment (what we call a *vat*). Instead of `obj.foo()`, we
+can write `E(obj).foo()` or the syntactic sugar, `obj~.foo()` and get a
+promise for the result. We call this syntactic sugar ['wavy dot'](https://github.com/Agoric/proposal-wavy-dot). The syntax
+means "deliver the message foo() to the actual object asynchronously,
+in its own turn, wherever and whenever it is, even if it is local."
+Using `E` or `~.`, you can talk asynchronously to local and remote objects
+in exactly the same way. For example, the first thing you might want
+to do is tap the gallery faucet to get a pixel for free:
+
+```js
+px = home.gallery~.tapFaucet()
+```
+
+`tapFaucet` returns a pixel and saves it under `px`. The pixel that you receive is
+actually in the form of an ERTP payment. [ERTP](/ertp/guide/) (Electronic Rights Transfer Protocol)
+is our smart contract framework for handling transferable objects.
+Payments have a few functions. Let's call `getBalance()` on our payment
+to see which pixel we received.
+
+```js
+px~.getBalance()
+```
+
+You might see something like:
+
+```js
+{
+ "label": {
+ "assay": [Presence 15],
+ "allegedName": "pixels"
+ },
+ "units" : [{ "x":1, "y":4 }]
+}
+```
+
+The `units` tells us which pixels we've received. `{ x:1, y:4 }`
+means that we got a pixel that is in the fifth row (`y:4`) and 2 pixels
+from the left (`x:1`). To color the pixel, we need to get the use
+object from the payment. You can think of the use object as a regular
+JavaScript object that just happens to be associated with an ERTP
+payment.
+
+```js
+use = px~.getUse()
+```
+
+Your use object will be stored under `use`. Now we
+can use it to color.
+
+```js
+use~.changeColorAll('#FF69B4')
+```
+
+The following commands show a pixel being obtained from the faucet,
+getting the 'use' object, coloring the pixel, and selling a pixel to the gallery through an
+escrow smart contract.
+
+```
+px = home.gallery~.tapFaucet();
+px~.getBalance();
+use = px~.getUse();
+use~.changeColorAll('yellow');
+px2 = home.gallery~.tapFaucet();
+asset2 = px2~.getBalance();
+asset2.then(a => home.gallery~.pricePixelUnitOps(a));
+hostInvite = home.gallery~.sellToGallery(asset2);
+seat = hostInvite~.host~.redeem(hostInvite~.inviteP);
+offered = seat~.offer(px2);
+assays = home.gallery~.getAssays();
+pxPurse = assays~.pixelAssay~.makeEmptyPurse();
+dustPurse = assays~.dustAssay~.makeEmptyPurse();
+collected = offered.then(_ => home.gallery~.collectFromGallery(seat, dustPurse, pxPurse, 'my escrow'));
+collected.then(_ => dustPurse~.getBalance());
+```
+
+Woohoo! We're now a few dust richer than when we started.
+
+Learn more about ERTP and our pixel demo [here](https://github.com/Agoric/ERTP).
+
+To see the contracts you've uploaded [as per the README](https://github.com/Agoric/cosmic-swingset/blob/master/lib/ag-solo/contracts/README-contract.md), try:
+
+```js
+home.uploads~.list()
+home.uploads~.get('encouragementBot')~.spawn()~.encourageMe('Person')
+```
+
+## Initial Endowments
+
+When a client is started up, it has a few items in a record named home.
+
+* gallery: the Pixel Gallery, described above
+* purse: a purse that can hold pixel Dust
+* moolah: a purse that starts out with 1000 `moolah`
+* sharingService: a service that makes it possible to pass capabilities between vats
+* canvasStatePublisher: a service with the message subscribe(callback)
+* [uploads](https://github.com/Agoric/cosmic-swingset/blob/master/lib/ag-solo/contracts/README-contract.md): a private directory
+ of contracts you've uploaded
+* registrar: a public directory for published objects
+* localTimerService and chainTimerService: tools for scheduling
+* [zoe](/zoe/guide/): support for contracts with Offer-Safety Enforcement
+* [contractHost](/ertp/guide/contract-hosts): secure smart contracts
+
+### sharingService
+
+`home.sharingService` is a service that lets you connect to
+other vats that are connected to the same remote chain vat. sharingService
+has three methods: `createSharedMap(name)`, `grabSharedMap(name)`, and
+`validate(sharedMap)`. These allow you to create a SharedMap which you can
+use to pass items to and from another vat. The sharingService's
+methods are designed to allow you to share a newly created sharedMap
+with one other vat, after which the name can't be reused.
+
+The way to use it is to call `createSharedMap() `with a name that you share
+with someone else. They then call `grabSharedMap`() and pass the name you
+gave. If they get a valid SharedMap, then you have a private
+channel. If they don't get it, then someone else must have tried to
+grab the name first, and you can discard that one and try again.
+
+Once you each have an end, either of you can call `addEntry(key, value)`
+to store an object, which the other party can retrieve with
+`lookup(key)`.
+
+### canvasStatePublisher
+
+`home.canvasStatePublisher` has a `subscribe()` method, which takes a callback
+function. When the state of the pixel gallery changes, the callback's
+`notify()` method is called with the new state.
diff --git a/main/getting-started/timer-service.md b/main/getting-started/timer-service.md
new file mode 100644
index 000000000..f6d44e174
--- /dev/null
+++ b/main/getting-started/timer-service.md
@@ -0,0 +1,104 @@
+# Timer Service
+
+## Description
+
+There will be one or two `timerServices` in home. One is from the chain (if
+present), the other from the local vat. It would probably be sensible to use a
+chain-based timer for contracts, but more efficient to use the local timer
+for operations that don't need consensus or consistency. Each `timerService`
+gives the ability to get the current time, schedule a single `wake()` call,
+create a repeater that will allow scheduling of events at regular intervals,
+or remove scheduled calls.
+
+## API
+The timerService's API is:
+
+```js
+interface TimerService {
+ // Retrieve the time of the start of the current block.
+ getCurrentTimestamp() -> (integer);
+
+ // Return value is the time at which the call is scheduled to take place.
+ setWakeup(baseTime :integer, handler :Handler) -> (integer);
+
+ // Remove the handler from all its scheduled wakeups, whether
+ // produced by timer.setWakeup(h) or repeater.schedule(h).
+ removeWakeup(handler :Handler) -> (List(integer));
+
+ // Create and return a repeater that will schedule wake() calls repeatedly at
+ // times that are a multiple of interval following baseTime. Interval is the
+ // delay between successive times at which wake will be called. When
+ // schedule(h) is called, h.wake() will be scheduled to be called after the
+ // next multiple of interval from the base. Since block times are coarse-
+ // grained, the actual call may occur later, but this won't change when the
+ // next event will be called.
+ createRepeater(delaySecs :integer, interval :integer) -> (Repeater);
+}
+
+interface Repeater {
+ // Return value is the time scheduled for the first call on handler.
+ // The handler will continue to be scheduled for a wake() call every
+ // interval until the repeater is disabled.
+ schedule(handler :Handler) -> (integer);
+
+ // Disable this repeater, so schedule() can't be called, and handlers
+ // already scheduled with this repeater won't be rescheduled again after
+ // wake() is next called on them.
+ disable();
+}
+
+interface Handler {
+ // The time passed to wake() is the time that the call was scheduled to
+ // occur.
+ wake(time);
+}
+```
+
+## Transcript
+Here's a transcript of a session showing the use of the repeater.
+
+```
+command[0] home
+history[0] {"LOADING":[Promise],"gallery":[Presence o-50],"sharingService":[Presence o-51],
+"purse":[Presence o-52],"canvasStatePublisher":[Presence o-53],"contractHost":[Presence o-54],
+"chainTimerService":[Presence o-55],"sharing":[Presence o-56],"registry":[Presence o-57],"zoe":
+[Presence o-58],"localTimerService":[Presence o-59],"uploads":[Presence o-60]}
+command[1] home.localTimerService~.getCurrentTimestamp()
+history[1] 1571782780000
+command[2] home.chainTimerService~.getCurrentTimestamp()
+history[2] 1571782793
+command[3] makeHandler = () => { let calls = 0; const args = []; return { getCalls() {
+return calls; }, getArgs() { return args; }, wake(arg) { args.push(arg); calls += 1; }, }; }
+history[3] [Function makeHandler]
+command[4] h1 = makeHandler()
+history[4] {"getCalls":[Function getCalls],"getArgs":[Function getArgs],"wake":[Function wake]}
+command[5] h2 = makeHandler()
+history[5] {"getCalls":[Function getCalls],"getArgs":[Function getArgs],"wake":[Function wake]}
+command[6] tl = home.localTimerService
+history[6] [Presence o-59]
+command[7] tc = home.chainTimerService
+history[7] [Presence o-55]
+command[8] rl = tl~.createRepeater(7, 1500)
+history[8] [Presence o-64]
+command[9] rc = tc~.createRepeater(7, 1)
+history[9] [Presence o-65]
+command[10] rl~.schedule(h1)
+history[10] 1571783040007
+command[11] rc~.schedule(h2)
+history[11] 1571783051
+command[12] h1.getCalls()
+history[12] 3
+command[13] h2.getCalls()
+history[13] 1
+...
+command[22] h1.getCalls()
+history[22] 50
+command[23] h1.getCalls()
+history[23] 53
+command[24] h1.getCalls()
+history[24] 54
+command[25] tl~.getCurrentTimestamp()
+history[25] 1571783375000
+command[26] tc~.getCurrentTimestamp()
+history[26] 1571783384
+```