Skip to content

Commit 4c05615

Browse files
foriequal0remagpie
authored andcommitted
Add snapshot sync test with Tendermint dynamic validator
1 parent 6759b5d commit 4c05615

3 files changed

Lines changed: 251 additions & 10 deletions

File tree

Lines changed: 208 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,208 @@
1+
// Copyright 2019 Kodebox, Inc.
2+
// This file is part of CodeChain.
3+
//
4+
// This program is free software: you can redistribute it and/or modify
5+
// it under the terms of the GNU Affero General Public License as
6+
// published by the Free Software Foundation, either version 3 of the
7+
// License, or (at your option) any later version.
8+
//
9+
// This program is distributed in the hope that it will be useful,
10+
// but WITHOUT ANY WARRANTY; without even the implied warranty of
11+
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12+
// GNU Affero General Public License for more details.
13+
//
14+
// You should have received a copy of the GNU Affero General Public License
15+
// along with this program. If not, see <https://www.gnu.org/licenses/>.
16+
17+
import * as chai from "chai";
18+
import { expect } from "chai";
19+
import * as chaiAsPromised from "chai-as-promised";
20+
import { SDK } from "codechain-sdk";
21+
import * as stake from "codechain-stakeholder-sdk";
22+
import * as fs from "fs";
23+
import "mocha";
24+
import * as path from "path";
25+
26+
import mkdirp = require("mkdirp");
27+
import { validators } from "../../../tendermint.dynval/constants";
28+
import { faucetAddress, faucetSecret } from "../../helper/constants";
29+
import { PromiseExpect } from "../../helper/promise";
30+
import CodeChain, { Signer } from "../../helper/spawn";
31+
import { setTermTestTimeout, withNodes } from "../setup";
32+
33+
chai.use(chaiAsPromised);
34+
35+
const SNAPSHOT_CONFIG = `${__dirname}/../../../tendermint.dynval/snapshot-config.yml`;
36+
const SNAPSHOT_PATH = `${__dirname}/../../../../snapshot/`;
37+
38+
describe("Snapshot for Tendermint with Dynamic Validator", function() {
39+
const promiseExpect = new PromiseExpect();
40+
const snapshotValidators = validators.slice(0, 3);
41+
const freshNodeValidator = validators[3];
42+
const { nodes } = withNodes(this, {
43+
promiseExpect,
44+
overrideParams: {
45+
maxNumOfValidators: 3
46+
},
47+
validators: snapshotValidators.map((signer, index) => ({
48+
signer,
49+
delegation: 5000,
50+
deposit: 10_000_000 - index // tie-breaker
51+
})),
52+
modify: () => {
53+
mkdirp.sync(SNAPSHOT_PATH);
54+
const snapshotPath = fs.mkdtempSync(SNAPSHOT_PATH);
55+
return {
56+
additionalArgv: [
57+
"--snapshot-path",
58+
snapshotPath,
59+
"--config",
60+
SNAPSHOT_CONFIG
61+
],
62+
nodeAdditionalProperties: {
63+
snapshotPath
64+
}
65+
};
66+
}
67+
});
68+
69+
it("should be exist after some time", async function() {
70+
const termWaiter = setTermTestTimeout(this, {
71+
terms: 2
72+
});
73+
const termMetadata = await termWaiter.waitNodeUntilTerm(nodes[0], {
74+
target: 2,
75+
termPeriods: 1
76+
});
77+
const snapshotBlock = await getSnapshotBlock(nodes[0], termMetadata);
78+
expect(
79+
path.join(
80+
nodes[0].snapshotPath,
81+
snapshotBlock.hash.toString(),
82+
snapshotBlock.stateRoot.toString()
83+
)
84+
).to.satisfy(fs.existsSync);
85+
});
86+
87+
it("should be able to boot with the snapshot", async function() {
88+
const termWaiter = setTermTestTimeout(this, {
89+
terms: 3
90+
});
91+
const termMetadata1 = await termWaiter.waitNodeUntilTerm(nodes[0], {
92+
target: 2,
93+
termPeriods: 1
94+
});
95+
const snapshotBlock = await getSnapshotBlock(nodes[0], termMetadata1);
96+
await makeItValidator(nodes[0], freshNodeValidator);
97+
const snapshotPath = fs.mkdtempSync(SNAPSHOT_PATH);
98+
const node = new CodeChain({
99+
chain: `${__dirname}/../../scheme/tendermint-dynval.json`,
100+
argv: [
101+
"--engine-signer",
102+
freshNodeValidator.platformAddress.toString(),
103+
"--password-path",
104+
`test/tendermint.dynval/${freshNodeValidator.platformAddress.value}/password.json`,
105+
"--force-sealing",
106+
"--snapshot-path",
107+
snapshotPath,
108+
"--config",
109+
SNAPSHOT_CONFIG,
110+
"--snapshot-hash",
111+
snapshotBlock.hash.toString(),
112+
"--snapshot-number",
113+
snapshotBlock.number.toString()
114+
],
115+
additionalKeysPath: `tendermint.dynval/${freshNodeValidator.platformAddress.value}/keys`
116+
});
117+
try {
118+
await node.start();
119+
await node.connect(nodes[0]);
120+
await termWaiter.waitNodeUntilTerm(node, {
121+
target: 4,
122+
termPeriods: 2
123+
});
124+
125+
await freshValidatorCheck(nodes[0].sdk);
126+
127+
expect(await node.sdk.rpc.chain.getBlock(snapshotBlock.number - 1))
128+
.to.be.null;
129+
expect(await node.sdk.rpc.chain.getBlock(snapshotBlock.number)).not
130+
.to.be.null;
131+
// Check that the freshNodeValidator is still a validator & make sure it doesn't have a block/header before termMetadata1.
132+
} catch (e) {
133+
node.keepLogs();
134+
throw e;
135+
} finally {
136+
await node.clean();
137+
}
138+
});
139+
140+
afterEach(async function() {
141+
promiseExpect.checkFulfilled();
142+
});
143+
144+
async function freshValidatorCheck(sdk: SDK) {
145+
const blockNumber = await sdk.rpc.chain.getBestBlockNumber();
146+
const termMedata = await stake.getTermMetadata(sdk, blockNumber);
147+
const currentTermInitialBlockNumber =
148+
termMedata!.lastTermFinishedBlockNumber + 1;
149+
const validatorsAfter = (await stake.getPossibleAuthors(
150+
sdk,
151+
currentTermInitialBlockNumber
152+
))!.map(platformAddr => platformAddr.toString());
153+
154+
expect(validatorsAfter).and.contains(
155+
freshNodeValidator.platformAddress.toString()
156+
);
157+
}
158+
});
159+
160+
async function getSnapshotBlock(
161+
node: CodeChain,
162+
termMetadata: stake.TermMetadata
163+
) {
164+
const blockNumber = termMetadata.lastTermFinishedBlockNumber + 1;
165+
await node.waitBlockNumber(blockNumber);
166+
return (await node.sdk.rpc.chain.getBlock(blockNumber))!;
167+
}
168+
169+
async function makeItValidator(node: CodeChain, freshNodeValidator: Signer) {
170+
const faucetSeq = await node.sdk.rpc.chain.getSeq(faucetAddress);
171+
const payTx = node.sdk.core
172+
.createPayTransaction({
173+
recipient: freshNodeValidator.platformAddress,
174+
quantity: 200000000
175+
})
176+
.sign({
177+
secret: faucetSecret,
178+
seq: faucetSeq,
179+
fee: 10
180+
});
181+
await node.waitForTx(await node.sdk.rpc.chain.sendSignedTransaction(payTx));
182+
const selfNominateTx = stake
183+
.createSelfNominateTransaction(node.sdk, 10000000, "")
184+
.sign({
185+
secret: freshNodeValidator.privateKey,
186+
seq: await node.sdk.rpc.chain.getSeq(
187+
freshNodeValidator.platformAddress
188+
),
189+
fee: 10
190+
});
191+
await node.waitForTx(
192+
await node.sdk.rpc.chain.sendSignedTransaction(selfNominateTx)
193+
);
194+
const delegateTx = stake
195+
.createDelegateCCSTransaction(
196+
node.sdk,
197+
freshNodeValidator.platformAddress,
198+
10000
199+
)
200+
.sign({
201+
secret: faucetSecret,
202+
seq: faucetSeq + 1,
203+
fee: 10
204+
});
205+
await node.waitForTx(
206+
await node.sdk.rpc.chain.sendSignedTransaction(delegateTx)
207+
);
208+
}

test/src/e2e.dynval/setup.ts

Lines changed: 27 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -39,17 +39,29 @@ interface ValidatorConfig {
3939
delegation?: U64Value;
4040
}
4141

42-
export function withNodes(
42+
interface NodePropertyModifier<T> {
43+
additionalArgv: string[];
44+
nodeAdditionalProperties: T;
45+
}
46+
47+
export function withNodes<T>(
4348
suite: Suite,
4449
options: {
4550
promiseExpect: PromiseExpect;
4651
validators: ValidatorConfig[];
4752
overrideParams?: Partial<CommonParams>;
4853
onBeforeEnable?: (nodes: CodeChain[]) => Promise<void>;
54+
modify?: (signer: Signer, index: number) => NodePropertyModifier<T>;
4955
}
5056
) {
51-
const nodes: CodeChain[] = [];
52-
const { overrideParams = {} } = options;
57+
const nodes: (CodeChain & T)[] = [];
58+
const {
59+
overrideParams = {},
60+
modify = () => ({
61+
additionalArgv: [],
62+
nodeAdditionalProperties: {} as T
63+
})
64+
} = options;
5365
const initialParams = {
5466
...defaultParams,
5567
...overrideParams
@@ -62,7 +74,8 @@ export function withNodes(
6274
nodes.length = 0;
6375
const newNodes = await createNodes({
6476
...options,
65-
initialParams
77+
initialParams,
78+
modify
6679
});
6780
nodes.push(...newNodes);
6881
});
@@ -95,14 +108,15 @@ export function findNode(nodes: CodeChain[], signer: Signer) {
95108
);
96109
}
97110

98-
async function createNodes(options: {
111+
async function createNodes<T>(options: {
99112
promiseExpect: PromiseExpect;
100113
validators: ValidatorConfig[];
101114
initialParams: CommonParams;
102115
onBeforeEnable?: (nodes: CodeChain[]) => Promise<void>;
103-
}): Promise<CodeChain[]> {
116+
modify: (signer: Signer, index: number) => NodePropertyModifier<T>;
117+
}): Promise<(CodeChain & T)[]> {
104118
const chain = `${__dirname}/../scheme/tendermint-dynval.json`;
105-
const { promiseExpect, validators, initialParams } = options;
119+
const { promiseExpect, validators, initialParams, modify } = options;
106120

107121
const initialNodes: CodeChain[] = [];
108122
const initialValidators = [
@@ -124,20 +138,23 @@ async function createNodes(options: {
124138
});
125139
}
126140

127-
const nodes: CodeChain[] = [];
141+
const nodes: (CodeChain & T)[] = [];
128142
for (let i = 0; i < validators.length; i++) {
129143
const { signer: validator } = validators[i];
130-
nodes[i] = new CodeChain({
144+
const modifier = modify(validator, i);
145+
const node = new CodeChain({
131146
chain,
132147
argv: [
133148
"--engine-signer",
134149
validator.platformAddress.value,
135150
"--password-path",
136151
`test/tendermint.dynval/${validator.platformAddress.value}/password.json`,
137-
"--force-sealing"
152+
"--force-sealing",
153+
...modifier.additionalArgv
138154
],
139155
additionalKeysPath: `tendermint.dynval/${validator.platformAddress.value}/keys`
140156
});
157+
nodes[i] = Object.assign(node, modifier.nodeAdditionalProperties);
141158
nodes[i].signer = validator;
142159
}
143160
let bootstrapFailed = false;
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
[codechain]
2+
3+
[mining]
4+
5+
[network]
6+
7+
[rpc]
8+
9+
[ipc]
10+
11+
[ws]
12+
13+
[snapshot]
14+
disable = false
15+
16+
[email_alarm]

0 commit comments

Comments
 (0)