Skip to content

Commit

Permalink
Merge 9a94b72 into 72d0dd3
Browse files Browse the repository at this point in the history
  • Loading branch information
ineiti committed Oct 20, 2020
2 parents 72d0dd3 + 9a94b72 commit 322134d
Show file tree
Hide file tree
Showing 21 changed files with 1,056 additions and 4 deletions.
6 changes: 6 additions & 0 deletions external/js/cothority/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
3.6.0 - 2020 10 16
- added a new api for the instances using rxjs observables
- changed the way ByzCoinRPC.getUpdates behaves

3.5.3 - 2020 09 24
- remove buffer import in log.ts
24 changes: 24 additions & 0 deletions external/js/cothority/package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 2 additions & 1 deletion external/js/cothority/package.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"private": true,
"name": "@dedis/cothority",
"version": "3.5.5",
"version": "3.6.0",
"description": "A typescript implementation of the cothority",
"main": "index.js",
"browser": "bundle.min.js",
Expand Down Expand Up @@ -83,6 +83,7 @@
"prettier": "^1.19.1",
"ts-loader": "^5.3.3",
"ts-node": "^8.0.1",
"tsconfig-paths": "^3.9.0",
"tslint": "^5.12.1",
"typedoc": "^0.15.8",
"typescript": "^3.6.4",
Expand Down
79 changes: 79 additions & 0 deletions external/js/cothority/spec/helpers/bctest.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
import { Log } from "../../src";
import { ByzCoinRPC, IStorage, LocalCache } from "../../src/byzcoin";
import { Darc, Rule } from "../../src/darc";
import { RosterWSConnection } from "../../src/network";
import { StatusRPC } from "../../src/status";
import { StatusRequest, StatusResponse } from "../../src/status/proto";
import { TransactionBuilder } from "../../src/v2/byzcoin";
import { CoinContract, DarcInst } from "../../src/v2/byzcoin/contracts";
import { BLOCK_INTERVAL, ROSTER, SIGNER, startConodes, stopConodes } from "../support/conondes";

/**
* BCTest allows for using a single ByzCoin instance for multiple tests. It should be called with
*
* const bct = await BCTest.singleton()
*
* in every test where a byzcoin-instance is used. Thereafter the test can use the genesisInst
* to create new CoinInstances and DarcInstances.
*
* Using this class reduces the time to test, as the same ByzCoin instance is used for all tests.
* But it also means that the tests need to make sure that the genesis-darc is not made
* unusable.
*/
export class BCTest {

static async singleton(): Promise<BCTest> {
if (BCTest.bct === undefined) {
BCTest.bct = await BCTest.init();
} else {
// Wait for 1s in case a transaction from a previous test is still being accepted.
await new Promise((resolve) => setTimeout(resolve, 1000));
}

return BCTest.bct;
}
private static bct: BCTest | undefined;

private static async init(): Promise<BCTest> {
Log.lvl = 1;
const roster4 = ROSTER.slice(0, 4);

let usesDocker = true;
try {
const ws = new RosterWSConnection(roster4, StatusRPC.serviceName);
ws.setParallel(1);
await ws.send(new StatusRequest(), StatusResponse);
Log.warn("Using already running nodes for test!");
usesDocker = false;
} catch (e) {
await startConodes();
}

const cache = new LocalCache();
const genesis = ByzCoinRPC.makeGenesisDarc([SIGNER], roster4, "initial");
[CoinContract.ruleFetch, CoinContract.ruleMint, CoinContract.ruleSpawn, CoinContract.ruleStore,
CoinContract.ruleTransfer]
.forEach((rule) => genesis.addIdentity(rule, SIGNER, Rule.OR));
const rpc = await ByzCoinRPC.newByzCoinRPC(roster4, genesis, BLOCK_INTERVAL, cache);
rpc.setParallel(1);
const tx = new TransactionBuilder(rpc);
const genesisInst = await DarcInst.retrieve(rpc, genesis.getBaseID());
return new BCTest(cache, genesis, genesisInst, rpc, tx, usesDocker);
}

private constructor(
public cache: IStorage,
public genesis: Darc,
public genesisInst: DarcInst,
public rpc: ByzCoinRPC,
public tx: TransactionBuilder,
public usesDocker: boolean,
) {
}

async shutdown() {
if (this.usesDocker) {
return stopConodes();
}
}
}
96 changes: 96 additions & 0 deletions external/js/cothority/spec/support/historyObs.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
import { Log } from "../../src";

/**
* HistoryObs allows a test to wait for a set of changes to occur and to throw an error if a timeout occurs before that.
* To use it, the `push` method should be called for every new occurrence of the item to be observed.
* This is usually done in an observer:
*
* const ho = new HistoryObs();
* coinInstance.subscribe((c) => ho.push(coinInstance.value.toString()));
*
* After that, the test can wait for a number of occurrences on this value:
*
* await h.resolve("0", "100000");
*
* This will wait for the history to have at least two elements: "0" and "100000". If during the timeout less than
* two elements are available, the `resolve` throws an error. It also throws an error if the two first history elements
* don't correspond to the `resolve` call.
*/
export class HistoryObs {

private readonly entries: string[] = [];

constructor(private maxWait = 20) {}

push(...e: string[]) {
this.entries.push(...e);
}

async resolveInternal(newEntries: string[], complete?: boolean): Promise<void> {
await expectAsync(this.expect(newEntries, true, complete)).toBeResolved();
}

async resolve(...newEntries: string[]): Promise<void> {
return this.resolveInternal(newEntries);
}

async resolveComplete(...newEntries: string[]): Promise<void> {
return this.resolveInternal(newEntries, true);
}

async resolveAll(newEntries: string[]): Promise<void> {
let found = true;
while (found) {
try {
await this.expect(newEntries, true, false, true);
} catch (e) {
Log.lvl4(e);
found = false;
}
}
}

async reject(newEntries: string[], complete?: boolean): Promise<void> {
await expectAsync(this.expect(newEntries, false, complete)).toBeRejected();
}

async expect(newEntries: string[], succeed: boolean, complete?: boolean, silent?: boolean): Promise<void> {
return new Promise(async (res, rej) => {
try {
for (let i = 0; i < this.maxWait && this.entries.length < newEntries.length; i++) {
if (!silent) {
Log.lvl3("waiting", i, this.entries.length, newEntries.length);
}
await new Promise((resolve) => setTimeout(resolve, 200));
}
if (!silent) {
if (succeed) {
Log.lvl2("History:", this.entries, "wanted:", newEntries);
} else {
Log.lvl2("Want history:", this.entries, "to fail with:", newEntries);
}
}
if (this.entries.length < newEntries.length) {
throw new Error("not enough entries");
}
for (const e of newEntries) {
const h = this.entries.splice(0, 1)[0];
if (e !== h) {
throw new Error(`Got ${h} instead of ${e}`);
}
}
if (complete && this.entries.length !== 0) {
throw new Error(`didn't describe all history: ${this.entries}`);
}
res();
} catch (e) {
if (succeed) {
if (!silent) {
Log.error(e);
}
}
rej(e);
}
});
}
}
2 changes: 1 addition & 1 deletion external/js/cothority/spec/support/jasmine.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,6 @@
"helpers": [
"helpers/**/*"
],
"stopSpecOnExpectationFailure": false,
"stopSpecOnExpectationFailure": true,
"random": false
}
6 changes: 6 additions & 0 deletions external/js/cothority/spec/tsconfig.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
{
"extends": "../tsconfig",
"include": [
"./**/*"
]
}
72 changes: 72 additions & 0 deletions external/js/cothority/spec/v2/coin-inst.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
import Long from "long";

import { Log } from "../../src";
import { CoinContract, CoinInst } from "../../src/v2/byzcoin/contracts";
import { BCTest } from "../helpers/bctest";
import { SIGNER } from "../support/conondes";
import { HistoryObs } from "../support/historyObs";

describe("CoinInst should", () => {
const name = Buffer.alloc(32);

beforeAll(async () => {
name.write("coinName");
});

it("retrieve an instance from byzcoin", async () => {
const {genesisInst, tx, rpc} = await BCTest.singleton();
const coinID = genesisInst.spawnCoin(tx, name);
await tx.send([[SIGNER]], 10);
const ci = await CoinInst.retrieve(rpc, coinID);
expect(ci.getValue().name.equals(name)).toBeTruthy();
});

it("mint some coins", async () => {
const {genesisInst, tx, rpc} = await BCTest.singleton();
const coinID = genesisInst.spawnCoin(tx, name);
await tx.send([[SIGNER]], 10);

const ci = await CoinInst.retrieve(rpc, coinID);
const h = new HistoryObs();
ci.subscribe((c) => h.push(c.value.toString()));
await h.resolve("0");

ci.mint(tx, Long.fromNumber(1e6));
await tx.send([[SIGNER]], 10);
await h.resolve(1e6.toString());
});

it("transfer coins", async () => {
const {genesisInst, tx, rpc} = await BCTest.singleton();

Log.lvl2("Spawning 2 coins");
const sourceID = genesisInst.spawnCoin(tx, name);
const targetID = genesisInst.spawnCoin(tx, name);
CoinContract.mint(tx, sourceID, Long.fromNumber(1e6));
CoinContract.transfer(tx, sourceID, targetID, Long.fromNumber(1e5));
await tx.send([[SIGNER]], 10);

Log.lvl2("Getting coins and values");
const target = await CoinInst.retrieve(rpc, targetID);
const hTarget = new HistoryObs();
target.subscribe((ci) => hTarget.push(ci.value.toString()));
await hTarget.resolve(1e5.toString());

Log.lvl2("Transferring some coins from source to target");
const source = await CoinInst.retrieve(rpc, sourceID);
const hSource = new HistoryObs();
source.subscribe((ci) => hSource.push(ci.value.toString()));
source.mint(tx, Long.fromNumber(1e6));
source.transfer(tx, targetID, Long.fromNumber(2e5));
await tx.send([[SIGNER]], 10);
await hSource.resolve(9e5.toString(), 17e5.toString());
await hTarget.resolve(3e5.toString());

Log.lvl2("Using fetch and store for transfer");
source.fetch(tx, Long.fromNumber(3e5));
target.store(tx);
await tx.send([[SIGNER]], 10);
await hSource.resolve(14e5.toString());
await hTarget.resolve(6e5.toString());
}, 600000);
});
Loading

0 comments on commit 322134d

Please sign in to comment.