|
16 | 16 | import { blake256, getPublicFromPrivate, H160, H256, recoverSchnorr, SchnorrSignature, signSchnorr, U256, U64 } from "codechain-primitives"; |
17 | 17 | import { SignedTransaction } from "codechain-sdk/lib/core/SignedTransaction"; |
18 | 18 | import * as RLP from "rlp"; |
| 19 | +import { readUIntRLP } from "../rlp"; |
19 | 20 | import { BlockSyncMessage, Emitter, IBodiesq, IHeadersq, MessageType, ResponseMessage } from "./blockSyncMessage"; |
20 | 21 | import { Header } from "./cHeader"; |
21 | 22 | import { P2pLayer } from "./p2pLayer"; |
22 | | -import { ConsensusMessage, Emitter as TendermintEmitter, Step as TendermintStep, StepState, TendermintMessage } from "./tendermintMessage"; |
| 23 | +import { |
| 24 | + ConsensusMessage, |
| 25 | + Emitter as TendermintEmitter, |
| 26 | + ProposalBlock, |
| 27 | + Step as TendermintStep, |
| 28 | + StepState, |
| 29 | + TendermintMessage |
| 30 | +} from "./tendermintMessage"; |
23 | 31 | import { TransactionSyncMessage } from "./transactionSyncMessage"; |
24 | 32 |
|
25 | 33 |
|
@@ -438,6 +446,74 @@ export class Mock { |
438 | 446 | TendermintEmitter.removeAllListeners("stepstate"); |
439 | 447 | } |
440 | 448 |
|
| 449 | + public startDoubleProposal(priv: string) { |
| 450 | + const pub = getPublicFromPrivate(priv); |
| 451 | + TendermintEmitter.on("proposalblock", (message: ProposalBlock) => { |
| 452 | + const digest = (on: ConsensusMessage["messages"][number]["on"]) => |
| 453 | + blake256( |
| 454 | + RLP.encode([ |
| 455 | + [ |
| 456 | + new U64(on.step.height).toEncodeObject(), |
| 457 | + new U64(on.step.view).toEncodeObject(), |
| 458 | + new U64(on.step.step).toEncodeObject(), |
| 459 | + ], |
| 460 | + on.blockHash == null ? [] : [on.blockHash.toEncodeObject()], |
| 461 | + ]) |
| 462 | + ); |
| 463 | + |
| 464 | + const signature: SchnorrSignature = { |
| 465 | + r: message.signature.slice(0, 64), |
| 466 | + s: message.signature.slice(64), |
| 467 | + }; |
| 468 | + |
| 469 | + const block: any = RLP.decode(message.message); |
| 470 | + const oldOn: Parameters<typeof digest>[0] = { |
| 471 | + step: { |
| 472 | + height: readUIntRLP(block[0][5]), |
| 473 | + view: message.view, |
| 474 | + step: TendermintStep.Propose, |
| 475 | + }, |
| 476 | + blockHash: new H256(blake256(RLP.encode(block[0]))), |
| 477 | + }; |
| 478 | + const recovered = recoverSchnorr(digest(oldOn), signature); |
| 479 | + if (recovered === pub) { |
| 480 | + const newHeader = [ |
| 481 | + ...block[0].slice(0, 6), |
| 482 | + new U64(readUIntRLP(block[0][6]) + 1).toEncodeObject(), // timestamp |
| 483 | + ...block[0].slice(7), |
| 484 | + ]; |
| 485 | + const newDigest = digest({ |
| 486 | + ...oldOn, |
| 487 | + blockHash: new H256(blake256(RLP.encode(newHeader))), |
| 488 | + }); |
| 489 | + const newSignature = signSchnorr(newDigest, priv); |
| 490 | + |
| 491 | + this.sendTendermintMessage(new TendermintMessage({ |
| 492 | + type: "proposalblock", |
| 493 | + view: message.view, |
| 494 | + message: RLP.encode([newHeader, block[1]]), |
| 495 | + signature: newSignature.r + newSignature.s, |
| 496 | + })); |
| 497 | + } |
| 498 | + }); |
| 499 | + TendermintEmitter.on("stepstate", (message: StepState) => { |
| 500 | + if (message.voteStep.step === TendermintStep.Propose) { |
| 501 | + setTimeout(() => { |
| 502 | + this.sendTendermintMessage(new TendermintMessage({ |
| 503 | + type: "requestproposal", |
| 504 | + height: message.voteStep.height, |
| 505 | + view: message.voteStep.view, |
| 506 | + })); |
| 507 | + }, 200) |
| 508 | + } |
| 509 | + }); |
| 510 | + } |
| 511 | + |
| 512 | + public stopDoubleProposal() { |
| 513 | + TendermintEmitter.removeAllListeners("proposalblock"); |
| 514 | + TendermintEmitter.removeAllListeners("stepstate"); |
| 515 | + } |
| 516 | + |
441 | 517 | private async waitForBlockSyncMessage(type: MessageType): Promise<{}> { |
442 | 518 | return new Promise((resolve, reject) => { |
443 | 519 | switch (type) { |
|
0 commit comments