-
Notifications
You must be signed in to change notification settings - Fork 106
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
21 changed files
with
1,056 additions
and
4 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
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 |
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
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,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(); | ||
} | ||
} | ||
} |
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,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); | ||
} | ||
}); | ||
} | ||
} |
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 @@ | ||
{ | ||
"extends": "../tsconfig", | ||
"include": [ | ||
"./**/*" | ||
] | ||
} |
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,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); | ||
}); |
Oops, something went wrong.