Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Adding new v2-byzcoin requests #2331

Merged
merged 2 commits into from
Oct 22, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
76 changes: 76 additions & 0 deletions external/js/cothority/spec/helpers/bctest.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
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();
}

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