-
Notifications
You must be signed in to change notification settings - Fork 226
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat(cosmic-swingset): use a fake chain for scenario3 (#322)
* feat(cosmic-swingset): use a fake chain for scenario3 This introduces block latency so that the scenario3 chain behaves much more like a real blockchain, but within the same process (for debuggability) and without the actual Cosmos SDK usage. * fix(Makefile): refine rules Make a `deprecated-scenario3-setup` and `deprecated-scenario3-run-client` to illustrate the old scenario3. Create `scenario3-run` to make tab-completion better. * doc(README): remove Golang prerequisite: no longer needed
- Loading branch information
1 parent
8dc31a0
commit f833610
Showing
7 changed files
with
213 additions
and
25 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,6 @@ | ||
Introduce "fake chain" to scenario3 configuration. | ||
|
||
Notably, have a simulated 5-second block time. To | ||
reset this to the old behaviour, use: | ||
|
||
make scenario3-setup FAKE_CHAIN_DELAY=0 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,98 @@ | ||
/* eslint-disable no-await-in-loop */ | ||
import path from 'path'; | ||
import fs from 'fs'; | ||
import stringify from '@agoric/swingset-vat/src/kernel/json-stable-stringify'; | ||
import { launch } from '../launch-chain'; | ||
|
||
const PRETEND_BLOCK_DELAY = 5; | ||
|
||
async function readMap(file) { | ||
let content; | ||
const map = new Map(); | ||
try { | ||
content = await fs.promises.readFile(file); | ||
} catch (e) { | ||
return map; | ||
} | ||
const obj = JSON.parse(content); | ||
Object.entries(obj).forEach(([k, v]) => map.set(k, v)); | ||
return map; | ||
} | ||
|
||
async function writeMap(file, map) { | ||
const obj = {}; | ||
[...map.entries()].forEach(([k, v]) => (obj[k] = v)); | ||
const json = stringify(obj); | ||
await fs.promises.writeFile(file, json); | ||
} | ||
|
||
export async function connectToFakeChain(basedir, GCI, role, delay, inbound) { | ||
const stateFile = path.join(basedir, `fake-chain-${GCI}-state.json`); | ||
const mailboxFile = path.join(basedir, `fake-chain-${GCI}-mailbox.json`); | ||
const bootAddress = `${GCI}-client`; | ||
|
||
const mailboxStorage = await readMap(mailboxFile); | ||
|
||
const vatsdir = path.join(basedir, 'vats'); | ||
const argv = [`--role=${role}`, bootAddress]; | ||
const s = await launch(mailboxStorage, stateFile, vatsdir, argv); | ||
const { deliverInbound, deliverStartBlock } = s; | ||
|
||
let pretendLast = Date.now(); | ||
let blockHeight = 0; | ||
let intoChain = []; | ||
let thisBlock = []; | ||
async function simulateBlock() { | ||
const actualStart = Date.now(); | ||
// Gather up the new messages into the latest block. | ||
thisBlock.push(...intoChain); | ||
intoChain = []; | ||
|
||
try { | ||
const commitStamp = pretendLast + PRETEND_BLOCK_DELAY * 1000; | ||
const blockTime = Math.floor(commitStamp / 1000); | ||
await deliverStartBlock(blockHeight, blockTime); | ||
for (let i = 0; i < thisBlock.length; i += 1) { | ||
const [newMessages, acknum] = thisBlock[i]; | ||
await deliverInbound( | ||
bootAddress, | ||
newMessages, | ||
acknum, | ||
blockHeight, | ||
blockTime, | ||
); | ||
} | ||
|
||
// Done processing, "commit the block". | ||
await writeMap(mailboxFile, mailboxStorage); | ||
thisBlock = []; | ||
pretendLast = commitStamp + Date.now() - actualStart; | ||
blockHeight += 1; | ||
} catch (e) { | ||
console.log(`error fake processing`, e); | ||
} | ||
|
||
if (delay) { | ||
setTimeout(simulateBlock, delay * 1000); | ||
} | ||
|
||
// TODO: maybe add latency to the inbound messages. | ||
const mailbox = JSON.parse(mailboxStorage.get(`mailbox.${bootAddress}`)); | ||
const { outbox, ack } = mailbox || { | ||
outbox: [], | ||
ack: 0, | ||
}; | ||
inbound(GCI, outbox, ack); | ||
} | ||
|
||
async function deliver(newMessages, acknum) { | ||
intoChain.push([newMessages, acknum]); | ||
if (!delay) { | ||
await simulateBlock(); | ||
} | ||
} | ||
if (delay) { | ||
setTimeout(simulateBlock, delay * 1000); | ||
} | ||
return deliver; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,51 @@ | ||
import fs from 'fs'; | ||
import path from 'path'; | ||
|
||
export default function setFakeChain(basedir, GCI, role, fakeDelay) { | ||
const fn = path.join(basedir, 'connections.json'); | ||
const connsByType = {}; | ||
const add = c => { | ||
const { type } = c; | ||
const conns = connsByType[type]; | ||
if (!conns) { | ||
connsByType[type] = [c]; | ||
return; | ||
} | ||
|
||
switch (type) { | ||
case 'fake-chain': { | ||
// Replace duplicate GCIs. | ||
const { GCI: newGCI } = c; | ||
const index = conns.findIndex(({ GCI: oldGCI }) => oldGCI === newGCI); | ||
if (index < 0) { | ||
conns.push(c); | ||
} else { | ||
conns[index] = c; | ||
} | ||
break; | ||
} | ||
default: | ||
conns.push(c); | ||
} | ||
}; | ||
|
||
JSON.parse(fs.readFileSync(fn)).forEach(add); | ||
const newconn = { | ||
type: 'fake-chain', | ||
GCI, | ||
fakeDelay, | ||
role, | ||
}; | ||
add(newconn); | ||
const connections = []; | ||
Object.entries(connsByType).forEach(([_type, conns]) => | ||
connections.push(...conns), | ||
); | ||
fs.writeFileSync(fn, `${JSON.stringify(connections, undefined, 2)}\n`); | ||
|
||
const gciFileContents = `\ | ||
export const GCI = ${JSON.stringify(GCI)}; | ||
`; | ||
const bfn = path.join(basedir, 'vats', 'gci.js'); | ||
fs.writeFileSync(bfn, gciFileContents); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters