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

fix(core-p2p): check claimed state of peer #2686

Merged
merged 10 commits into from
Jun 12, 2019
34 changes: 29 additions & 5 deletions __tests__/integration/core-p2p/peer-verifier.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ const stubPeer: P2P.IPeer = createStubPeer({ ip: "127.0.0.1", port: 4009 });

let socketManager: MockSocketManager;
let service: P2P.IPeerService;
let claimedState: P2P.IPeerState;

beforeAll(async () => {
process.env.CORE_ENV = "test"; // important for socket server setup (testing), see socket-server/index.ts
Expand All @@ -28,13 +29,19 @@ afterAll(async () => {

beforeEach(async () => {
await socketManager.resetAllMocks();
claimedState = {
currentSlot: 1,
forgingAllowed: true,
height: 1,
header: { height: 1, id: genesisBlock.data.id },
};
});

describe("Peer Verifier", () => {
describe("checkState", () => {
it("identical chains", async () => {
const peerVerifier = new PeerVerifier(service.getCommunicator(), stubPeer);
const state = { header: { height: 1, id: genesisBlock.data.id } };
const state = Object.assign(claimedState, { header: genesisBlock.data });
const result = await peerVerifier.checkState(state, new Date().getTime() + 10000);
expect(result).toBeObject();
expect(result.forked).toBe(false);
Expand All @@ -44,7 +51,24 @@ describe("Peer Verifier", () => {
await socketManager.addMock("getCommonBlocks", { common: undefined });

const peerVerifier = new PeerVerifier(service.getCommunicator(), stubPeer);
const state = { header: { height: 1, id: "123" } };
const state = Object.assign(claimedState, { header: { height: 1, id: "123" } });
const result = await peerVerifier.checkState(state, new Date().getTime() + 10000);
expect(result).toBeUndefined();
});

it("contradicting heights", async () => {
const peerVerifier = new PeerVerifier(service.getCommunicator(), stubPeer);
const state = Object.assign(claimedState, { height: 1, header: blocks2to100Json[5] });
const result = await peerVerifier.checkState(state, new Date().getTime() + 10000);
expect(result).toBeUndefined();
});

it("garbage block header", async () => {
const peerVerifier = new PeerVerifier(service.getCommunicator(), stubPeer);
const garbageBlock = Object.assign({}, blocks2to100Json[0]);
garbageBlock.numberOfTransactions = 9000;

const state = Object.assign(claimedState, { height: 2, header: garbageBlock });
const result = await peerVerifier.checkState(state, new Date().getTime() + 10000);
expect(result).toBeUndefined();
});
Expand All @@ -63,7 +87,7 @@ describe("Peer Verifier", () => {
await socketManager.addMock("getCommonBlocks", { common: commonBlockReply });

const peerVerifier = new PeerVerifier(service.getCommunicator(), stubPeer);
const state = { header: { height: 1, id: "123" } };
const state = Object.assign(claimedState, { header: { height: 1, id: "123" } });
const result = await peerVerifier.checkState(state, new Date().getTime() + 10000);
expect(result).toBeUndefined();
}
Expand All @@ -90,7 +114,7 @@ describe("Peer Verifier", () => {
await socketManager.addMock("getBlocks", [block2]);

const peerVerifier = new PeerVerifier(service.getCommunicator(), stubPeer);
const state = { header: { height: 2, id: block2.id } };
const state = Object.assign(claimedState, { header: { height: 2, id: block2.id } });
const result = await peerVerifier.checkState(state, new Date().getTime() + 10000);
expect(result).toBeUndefined();
}
Expand All @@ -104,7 +128,7 @@ describe("Peer Verifier", () => {
await socketManager.addMock("getBlocks", [blocks2to100Json[0]]);

const peerVerifier = new PeerVerifier(service.getCommunicator(), stubPeer);
const state = { header: { height: 2, id: blocks2to100Json[0].id } };
const state = Object.assign(claimedState, { height: 2, header: blocks2to100Json[0] });
const result = await peerVerifier.checkState(state, new Date().getTime() + 10000);
expect(result).toBeObject();
expect(result.forked).toBe(false);
Expand Down
43 changes: 41 additions & 2 deletions packages/core-p2p/src/peer-verifier.ts
Original file line number Diff line number Diff line change
Expand Up @@ -76,9 +76,16 @@ export class PeerVerifier {
* the peer's state could not be verified.
* @throws {Error} if the state verification could not complete before the deadline
*/
public async checkState(claimedState: any, deadline: number): Promise<PeerVerificationResult | undefined> {
const ourHeight: number = await this.ourHeight();
public async checkState(
claimedState: P2P.IPeerState,
deadline: number,
): Promise<PeerVerificationResult | undefined> {
if (!this.checkStateHeader(claimedState)) {
return undefined;
}

const claimedHeight = Number(claimedState.header.height);
const ourHeight: number = await this.ourHeight();
if (await this.weHavePeersHighestBlock(claimedState, ourHeight)) {
// Case3 and Case5
return new PeerVerificationResult(ourHeight, claimedHeight, claimedHeight);
Expand All @@ -98,6 +105,38 @@ export class PeerVerifier {
return new PeerVerificationResult(ourHeight, claimedHeight, highestCommonBlockHeight);
}

private checkStateHeader(claimedState: P2P.IPeerState): boolean {
const blockHeader: Interfaces.IBlockData = claimedState.header as Interfaces.IBlockData;
const claimedHeight: number = Number(blockHeader.height);
if (claimedHeight !== claimedState.height) {
this.log(
Severity.DEBUG_EXTRA,
`Peer claimed contradicting heights: state height=${claimedState.height} vs ` +
`state header height: ${claimedHeight}`,
);
return false;
}

try {
const claimedBlock: Interfaces.IBlock = Blocks.BlockFactory.fromData(blockHeader);
if (claimedBlock.verifySignature()) {
return true;
}

this.log(
Severity.DEBUG_EXTRA,
`Claimed block header ${blockHeader.height}:${blockHeader.id} failed signature verification`,
);
return false;
} catch (error) {
this.log(
Severity.DEBUG_EXTRA,
`Claimed block header ${blockHeader.height}:${blockHeader.id} failed verification: ` + error.message,
);
return false;
}
}

/**
* Retrieve the height of the highest block in our chain.
* @return {Number} chain height
Expand Down