Skip to content

Commit

Permalink
Merge pull request #1748 from dedis/js_rpc_improvement
Browse files Browse the repository at this point in the history
Use the roster to fetch blocks
  • Loading branch information
kc1212 committed Mar 1, 2019
2 parents 537fe02 + 3ffb2c5 commit df3a126
Show file tree
Hide file tree
Showing 6 changed files with 42 additions and 52 deletions.
8 changes: 4 additions & 4 deletions external/js/cothority/package-lock.json

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

4 changes: 2 additions & 2 deletions 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.0.0-pre5",
"version": "3.0.0-pre6",
"description": "A typescript implementation of the cothority",
"main": "index.js",
"browser": "bundle.min.js",
Expand Down Expand Up @@ -30,7 +30,7 @@
"license": "LGPL-3.0-or-later",
"dependencies": {
"@babel/polyfill": "^7.2.5",
"@dedis/kyber": "^3.0.0-pre4",
"@dedis/kyber": "^3.0.0-pre5",
"@stablelib/blake2xs": "^0.10.4",
"@types/crypto-js": "^3.1.43",
"@types/node": "^10.12.18",
Expand Down
4 changes: 2 additions & 2 deletions external/js/cothority/spec/skipchain/skipchain-rpc.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@ describe("SkipchainRPC Tests", () => {
}

expect(err).toBeDefined();
expect(err.message).toBe("No such block");
expect(err.message).toContain("No such block");
err = null;

try {
Expand All @@ -72,7 +72,7 @@ describe("SkipchainRPC Tests", () => {
err = e;
}

expect(err.message).toBe("no conode has the latest block");
expect(err.message).toContain("Couldn't find latest skipblock");
});

it("should verify the chain", async () => {
Expand Down
4 changes: 3 additions & 1 deletion external/js/cothority/src/network/connection.ts
Original file line number Diff line number Diff line change
Expand Up @@ -130,6 +130,7 @@ export class RosterWSConnection extends WebSocketConnection {
const addresses = this.addresses.slice();
shuffle(addresses);

const errors = [];
for (const addr of addresses) {
this.url = addr;

Expand All @@ -138,10 +139,11 @@ export class RosterWSConnection extends WebSocketConnection {
return await super.send(message, reply);
} catch (e) {
Logger.lvl3(`fail to send on ${addr} with error:`, e);
errors.push(e.message);
}
}

throw new Error("no conodes are available or all conodes returned an error");
throw new Error(`send fails with errors: [${errors.join("; ")}]`);
}
}

Expand Down
72 changes: 30 additions & 42 deletions external/js/cothority/src/skipchain/skipchain-rpc.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import Logger from "../log";
import { IConnection, WebSocketConnection } from "../network/connection";
import { IConnection, RosterWSConnection, WebSocketConnection } from "../network/connection";
import { Roster } from "../network/proto";
import {
GetAllSkipChainIDs,
Expand All @@ -22,10 +22,12 @@ export default class SkipchainRPC {
static serviceName = "Skipchain";

private roster: Roster;
private pool: IConnection;
private conn: IConnection[];

constructor(roster: Roster) {
this.roster = roster;
this.pool = new RosterWSConnection(roster, SkipchainRPC.serviceName);
this.conn = roster.list.map((srvid) => {
return new WebSocketConnection(srvid.getWebSocketAddress(), SkipchainRPC.serviceName);
});
Expand Down Expand Up @@ -70,7 +72,7 @@ export default class SkipchainRPC {
async getSkipBlock(bid: Buffer): Promise<SkipBlock> {
const req = new GetSingleBlock({ id: bid });

const block = await this.conn[0].send<SkipBlock>(req, SkipBlock);
const block = await this.pool.send<SkipBlock>(req, SkipBlock);
if (!block.computeHash().equals(block.hash)) {
throw new Error("invalid block: hash does not match");
}
Expand All @@ -88,7 +90,7 @@ export default class SkipchainRPC {
async getSkipBlockByIndex(genesis: Buffer, index: number): Promise<GetSingleBlockByIndexReply> {
const req = new GetSingleBlockByIndex({ genesis, index });

const reply = await this.conn[0].send<GetSingleBlockByIndexReply>(req, GetSingleBlockByIndexReply);
const reply = await this.pool.send<GetSingleBlockByIndexReply>(req, GetSingleBlockByIndexReply);
if (!reply.skipblock.computeHash().equals(reply.skipblock.hash)) {
throw new Error("invalid block: hash does not match");
}
Expand All @@ -104,68 +106,54 @@ export default class SkipchainRPC {
async getAllSkipChainIDs(): Promise<Buffer[]> {
const req = new GetAllSkipChainIDs();

const ret = await this.conn[0].send<GetAllSkipChainIDsReply>(req, GetAllSkipChainIDsReply);
const ret = await this.pool.send<GetAllSkipChainIDsReply>(req, GetAllSkipChainIDsReply);

return ret.skipChainIDs.map((id) => Buffer.from(id));
}

/**
* Get the shortest path to the more recent block starting from latestID
*
* @param latestID ID of the block
* @param latestID ID of the block
* @param verify Verify the integrity of the chain when true
* @returns a promise that resolves with the list of blocks
*/
async getUpdateChain(latestID: Buffer): Promise<SkipBlock[]> {
async getUpdateChain(latestID: Buffer, verify = true): Promise<SkipBlock[]> {
const req = new GetUpdateChain({ latestID });
const ret = await this.conn[0].send<GetUpdateChainReply>(req, GetUpdateChainReply);
const ret = await this.pool.send<GetUpdateChainReply>(req, GetUpdateChainReply);
const blocks = ret.update;

const err = this.verifyChain(ret.update, latestID);
if (err) {
throw new Error(`invalid chain received: ${err.message}`);
const last = blocks[blocks.length - 1];
if (last && last.forwardLinks.length > 0) {
// more blocks exist but typically the roster has changed
const rpc = new SkipchainRPC(last.roster);
const more = await rpc.getUpdateChain(last.hash, verify);

blocks.splice(-1, 1, ...more);
}

if (verify) {
const err = this.verifyChain(blocks, latestID);
if (err) {
throw new Error(`invalid chain received: ${err.message}`);
}
}

return ret.update;
return blocks;
}

/**
* Get the latest known block of the skipchain. It will follow the forward
* links as much as possible and it is resistant to roster changes.
*
* @param latestID the current latest block
* @param roster use a different roster than the RPC
* @param verify Verify the integrity of the chain
* @returns a promise that resolves with the block, or reject with an error
*/
async getLatestBlock(latestID: Buffer): Promise<SkipBlock> {
const req = new GetUpdateChain({ latestID });
let reply: GetUpdateChainReply;

for (const c of this.conn) {
try {
reply = await c.send(req, GetUpdateChainReply);
} catch (err) {
Logger.lvl3(`error from ${c.getURL()}: ${err.message}`);
continue;
}

const err = this.verifyChain(reply.update, latestID);
if (!err) {
const b = reply.update.pop();

if (b.forwardLinks.length === 0) {
return b;
} else {
// it might happen a conode doesn't have the latest
// block stored so we contact the most updated
// roster to try to get it
return new SkipchainRPC(b.roster).getLatestBlock(b.hash);
}
} else {
Logger.lvl3("Received corrupted skipchain with error:", err);
}
}
async getLatestBlock(latestID: Buffer, verify = true): Promise<SkipBlock> {
const blocks = await this.getUpdateChain(latestID, verify);

// in theory that should not happen as at least the leader has the latest block
throw new Error("no conode has the latest block");
return blocks.pop();
}

/**
Expand Down
2 changes: 1 addition & 1 deletion external/js/kyber/package-lock.json

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

0 comments on commit df3a126

Please sign in to comment.