Skip to content

Commit d60303a

Browse files
authored
Liju.jose/nightfall client save transaction at tx submit event test 3 (#1414)
* test: 1 * test: 2 * test: 3 * test: 4 * test: 5 * test: 6 * test: 7 * test: 8 * test: 9 * fix: workflow moved Pull request checks
1 parent e3dd769 commit d60303a

File tree

6 files changed

+274
-2
lines changed

6 files changed

+274
-2
lines changed

.github/workflows/on-pull-request-master.yml

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1051,3 +1051,57 @@ jobs:
10511051
with:
10521052
name: periodic-payment-test-logs
10531053
path: ./periodic-payment-test.log
1054+
1055+
test-client-sync:
1056+
env:
1057+
CONFIRMATIONS: 1
1058+
NF_SERVICES_TO_START: blockchain,client,deployer,mongodb,optimist,rabbitmq,worker
1059+
runs-on: ubuntu-20.04
1060+
steps:
1061+
- uses: actions/checkout@v3
1062+
- uses: actions/setup-node@v1
1063+
with:
1064+
node-version: '16.17.0'
1065+
1066+
- name: Perform npm link
1067+
run: |
1068+
cd common-files
1069+
npm link
1070+
cd ..
1071+
npm link @polygon-nightfall/common-files
1072+
cd cli
1073+
npm link @polygon-nightfall/common-files
1074+
cd ..
1075+
- name: Start Containers
1076+
run: |
1077+
./bin/setup-nightfall
1078+
./bin/start-nightfall -g -d &> test-client-sync.log &disown
1079+
- name: Wait for images to be ready
1080+
uses: Wandalen/wretry.action@v1.0.11
1081+
with:
1082+
command: |
1083+
docker wait nightfall_3_deployer_1
1084+
attempt_limit: 100
1085+
attempt_delay: 20000
1086+
1087+
- name: Debug logs - after image builds
1088+
if: always()
1089+
run: cat test-client-sync.log
1090+
1091+
- name: Run integration test
1092+
run: |
1093+
npm run test-client-sync
1094+
- name: Debug logs - after integration test run
1095+
if: always()
1096+
run: cat test-client-sync.log
1097+
1098+
- name: If integration test failed, shutdown the Containers
1099+
if: failure()
1100+
run: npm run nightfall-down
1101+
1102+
- name: If integration test failed, upload logs files as artifacts
1103+
if: failure()
1104+
uses: actions/upload-artifact@master
1105+
with:
1106+
name: test-client-sync-logs
1107+
path: ./test-client-sync.log

cli/lib/nf3.mjs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -677,7 +677,7 @@ class Nf3 {
677677
this.shieldContractAddress,
678678
0,
679679
);
680-
resolve(receipt);
680+
resolve({ ...receipt, transactionHashL2: res.data.transaction.transactionHash });
681681
} catch (err) {
682682
reject(err);
683683
}
@@ -740,7 +740,7 @@ class Nf3 {
740740
this.shieldContractAddress,
741741
0,
742742
);
743-
resolve(receipt);
743+
resolve({ ...receipt, transactionHashL2: res.data.transaction.transactionHash });
744744
} catch (err) {
745745
reject(err);
746746
}

nightfall-client/src/routes/commitment.mjs

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ import {
2020
getCommitmentsDepositedRollbacked,
2121
} from '../services/commitment-storage.mjs';
2222
import { syncState } from '../services/state-sync.mjs';
23+
import { getAllTransactions } from '../services/database.mjs';
2324

2425
const router = express.Router();
2526

@@ -149,4 +150,13 @@ router.get('/commitmentsRollbacked', async (req, res, next) => {
149150
}
150151
});
151152

153+
router.get('/transactions', async (req, res, next) => {
154+
try {
155+
const transactions = await getAllTransactions();
156+
res.json({ transactions });
157+
} catch (err) {
158+
next(err);
159+
}
160+
});
161+
152162
export default router;

package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222
"ping-pong": ". ./bin/export-multiproposer-test-env && npx hardhat test --bail --no-compile test/ping-pong/ping-pong.test.mjs",
2323
"test-administrator": "npx hardhat test --bail --no-compile test/multisig/administrator.test.mjs ",
2424
"test-optimist-sync": "npx hardhat test --no-compile --bail test/optimist-resync.test.mjs",
25+
"test-client-sync": "npx hardhat test --no-compile --bail test/client-resync.test.mjs",
2526
"test-adversary": "npx hardhat test --no-compile --bail test/adversary.test.mjs",
2627
"test-general-stuff": "npx hardhat test --bail --no-compile test/kem-dem.test.mjs test/timber.test.mjs",
2728
"test-x509": "LOG_LEVEL=debug npx hardhat test --bail --no-compile test/x509.test.mjs",

test/client-resync.test.mjs

Lines changed: 173 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,173 @@
1+
/* eslint-disable no-await-in-loop */
2+
import chai, { expect } from 'chai';
3+
// import gen from 'general-number';
4+
import chaiHttp from 'chai-http';
5+
import chaiAsPromised from 'chai-as-promised';
6+
import config from 'config';
7+
import logger from '@polygon-nightfall/common-files/utils/logger.mjs';
8+
import Nf3 from '../cli/lib/nf3.mjs';
9+
import {
10+
getLayer2Balances,
11+
expectTransaction,
12+
Web3Client,
13+
getUserCommitments,
14+
getClientTransactions,
15+
restartClient,
16+
} from './utils.mjs';
17+
18+
chai.use(chaiHttp);
19+
chai.use(chaiAsPromised);
20+
21+
// const { generalise } = gen;
22+
const environment = config.ENVIRONMENTS[process.env.ENVIRONMENT] || config.ENVIRONMENTS.localhost;
23+
const {
24+
fee,
25+
transferValue,
26+
tokenConfigs: { tokenType, tokenId },
27+
mnemonics,
28+
signingKeys,
29+
} = config.TEST_OPTIONS;
30+
31+
const web3Client = new Web3Client();
32+
const eventLogs = [];
33+
let rollbackCount = 0;
34+
35+
const nf3User = new Nf3(signingKeys.user1, environment);
36+
const nf3User2 = new Nf3(signingKeys.user2, environment);
37+
38+
const nf3Proposer = new Nf3(signingKeys.proposer1, environment);
39+
40+
async function makeBlock() {
41+
logger.debug(`Make block...`);
42+
await nf3Proposer.makeBlockNow();
43+
await web3Client.waitForEvent(eventLogs, ['blockProposed']);
44+
}
45+
46+
describe('Client synchronisation tests', () => {
47+
let erc20Address;
48+
49+
before(async () => {
50+
await nf3User.init(mnemonics.user1);
51+
await nf3User2.init(mnemonics.user2);
52+
53+
await nf3Proposer.init(mnemonics.proposer);
54+
await nf3Proposer.registerProposer('http://optimist', await nf3Proposer.getMinimumStake());
55+
56+
// Proposer listening for incoming events
57+
const newGasBlockEmitter = await nf3Proposer.startProposer();
58+
newGasBlockEmitter.on('rollback', () => {
59+
rollbackCount += 1;
60+
logger.debug(
61+
`Proposer received a signalRollback complete, Now no. of rollbacks are ${rollbackCount}`,
62+
);
63+
});
64+
65+
erc20Address = await nf3User.getContractAddress('ERC20Mock');
66+
web3Client.subscribeTo('logs', eventLogs, { address: nf3User.stateContractAddress });
67+
web3Client.subscribeTo('logs', eventLogs, { address: nf3User.shieldContractAddress });
68+
});
69+
70+
describe('Test nightfall-client', () => {
71+
it('Should do two deposit successfully', async function () {
72+
const userL2BalanceBefore = await getLayer2Balances(nf3User, erc20Address);
73+
74+
// first deposit
75+
const res = await nf3User.deposit(erc20Address, tokenType, transferValue, tokenId, fee);
76+
expectTransaction(res);
77+
78+
await web3Client.waitForEvent(eventLogs, ['TransactionSubmitted']);
79+
const transactions = await getClientTransactions(environment.clientApiUrl);
80+
81+
// passing of below expect proves that transaction are save in
82+
// transactionEventHandler
83+
expect(transactions.length).to.be.equal(1);
84+
expect(res.transactionHashL2).to.be.equal(transactions[0].transactionHash);
85+
86+
await makeBlock();
87+
88+
// second deposit
89+
await nf3User.deposit(erc20Address, tokenType, transferValue, tokenId, fee);
90+
91+
await makeBlock();
92+
93+
const userL2BalanceAfter = await getLayer2Balances(nf3User, erc20Address);
94+
expect(userL2BalanceAfter - userL2BalanceBefore).to.be.equal(transferValue * 2 - fee * 2);
95+
});
96+
97+
// this test is to check nightfall-client behaviour in a case
98+
// where two same transfer transactions is created but second one with higher fee
99+
context('Test nightfall-client duplicate transaction deletion logic', () => {
100+
let userCommitments;
101+
let firstTransfer;
102+
let userL2BalanceBefore;
103+
before(async () => {
104+
userCommitments = await getUserCommitments(
105+
environment.clientApiUrl,
106+
nf3User.zkpKeys.compressedZkpPublicKey,
107+
);
108+
userL2BalanceBefore = await getLayer2Balances(nf3User, erc20Address);
109+
});
110+
111+
it('Should successfully create a transfer transaction', async function () {
112+
const res = await nf3User.transfer(
113+
false,
114+
erc20Address,
115+
tokenType,
116+
transferValue,
117+
tokenId,
118+
nf3User2.zkpKeys.compressedZkpPublicKey,
119+
fee,
120+
userCommitments.map(c => c.commitmentHash),
121+
);
122+
expectTransaction(res);
123+
firstTransfer = res.transactionHashL2;
124+
await web3Client.waitForEvent(eventLogs, ['TransactionSubmitted']);
125+
const transactions = await getClientTransactions(environment.clientApiUrl);
126+
127+
expect(transactions.length).to.be.equal(3);
128+
expect(res.transactionHashL2).to.be.equal(transactions[2].transactionHash);
129+
});
130+
131+
it('Should successfully do a transfer with higher fee with create block', async function () {
132+
let transactions;
133+
const res = await nf3User.transfer(
134+
false,
135+
erc20Address,
136+
tokenType,
137+
transferValue,
138+
tokenId,
139+
nf3User2.zkpKeys.compressedZkpPublicKey,
140+
fee + 1,
141+
userCommitments.map(c => c.commitmentHash),
142+
);
143+
expectTransaction(res);
144+
145+
transactions = await getClientTransactions(environment.clientApiUrl);
146+
expect(transactions.length).to.be.equal(3);
147+
expect(firstTransfer).to.be.equal(transactions[2].transactionHash);
148+
149+
// here we will also test client resync atleast for TransactionSubmitEvent Handler
150+
await restartClient(nf3User);
151+
152+
transactions = await getClientTransactions(environment.clientApiUrl);
153+
// if below expect passes it proves client resync is working.
154+
expect(transactions.length).to.be.equal(4);
155+
expect(res.transactionHashL2).to.be.equal(transactions[3].transactionHash);
156+
157+
// client re-sync has made sure higer fee transfer transaction is received in
158+
// transaction submit eventHandler, infact that what passing of above expect proves
159+
// But for optimist to receive same transaction lets wait for TransactionSubmitted
160+
// event trigger, needed before proceeding to makeBLock
161+
await web3Client.waitForEvent(eventLogs, ['TransactionSubmitted']);
162+
await makeBlock();
163+
const userL2BalanceAfter = await getLayer2Balances(nf3User, erc20Address);
164+
expect(userL2BalanceAfter - userL2BalanceBefore).to.be.equal(-(transferValue + fee + 1));
165+
166+
transactions = await getClientTransactions(environment.clientApiUrl);
167+
// if below expect passes it proves blockEventHandler delete duplicate transaction is working.
168+
expect(transactions.length).to.be.equal(3);
169+
expect(res.transactionHashL2).to.be.equal(transactions[2].transactionHash);
170+
});
171+
});
172+
});
173+
});

test/utils.mjs

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -571,6 +571,14 @@ const healthy = async nf3Proposer => {
571571
logger.debug('optimist is healthy');
572572
};
573573

574+
const healthyClient = async nf3User => {
575+
while (!(await nf3User.healthcheck('client'))) {
576+
await waitForTimeout(1000);
577+
}
578+
579+
logger.debug('client is healthy');
580+
};
581+
574582
const dropOptimistMongoDatabase = async () => {
575583
logger.debug(`Dropping Optimist's Mongo database`);
576584
let mongoConn;
@@ -717,3 +725,29 @@ export async function restartOptimist(nf3Proposer, dropDb = true) {
717725

718726
await healthy(nf3Proposer);
719727
}
728+
729+
// unlike optimist, client cannot drop its database.
730+
// because of commitments stored in database.
731+
// restartClient function is only used in client-resync.test.mjs
732+
export async function restartClient(nf3User) {
733+
const options = {
734+
config: [
735+
'docker/docker-compose.yml',
736+
'docker/docker-compose.dev.yml',
737+
'docker/docker-compose.ganache.yml',
738+
],
739+
log: process.env.LOG_LEVEL || 'silent',
740+
composeOptions: [['-p', 'nightfall_3']],
741+
};
742+
743+
await compose.stopOne('client', options);
744+
await compose.rm(options, 'client');
745+
746+
await compose.upOne('client', options);
747+
await healthyClient(nf3User);
748+
}
749+
750+
export async function getClientTransactions(clientApiUrl) {
751+
const { transactions } = (await axios.get(`${clientApiUrl}/commitment/transactions`)).data;
752+
return transactions;
753+
}

0 commit comments

Comments
 (0)