Skip to content
This repository was archived by the owner on Jun 10, 2022. It is now read-only.

Commit 488004b

Browse files
fboucquezrg911
andauthored
Dev -> Main (#559)
* Fixed aggregate transaction codex issue * fixed lint * Updated change log * Updated change log * - Added fromHeight & toHeight to receipt endpoint (#557) * Cosigner notification fix (#554) * Fixed transactions * Added multisig check on ws channel subscription * Fixed lint * Updated bootstrap * reset symbol-bootstrap version for travis build * - Added fromHeight & toHeight to receipt endpoint (#557) * Fixed transactions * Added multisig check on ws channel subscription * Fixed lint * Updated bootstrap * reset symbol-bootstrap version for travis build * Use async in buildAccountConditions * PR review fixes * - Restored the changes on subscription manager * Fixed bug in catapultDB, add self to the multisig list * Only check multisig account for partial transactions * Account pagination after filter (#560) * Fixed #558 * Update package.json Co-authored-by: fboucquez <[email protected]> * Updated symbol-bootstrap version * Removed pageIndex in account search second query * Restore previous comments * v2.3.2 change log (#565) * v2.3.2 change log * Fixed travis and travis tests Co-authored-by: fernando <[email protected]> Co-authored-by: Steven Liu <[email protected]>
1 parent 7d077ba commit 488004b

13 files changed

+184
-91
lines changed

.travis.yml

+3
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ node_js:
44
- "12"
55
services:
66
- docker
7+
- mongodb
78
env:
89
global:
910
- RELEASE_BRANCH=main
@@ -19,6 +20,8 @@ before_script:
1920
- . ./travis/docker-functions.sh
2021
- log_env_variables
2122
- echo '$SUBPROJECT'
23+
- mongo mydb_test --eval 'db.createUser({user:"travis",pwd:"test",roles:["readWrite"]});'
24+
- if [[ ! -z "$DOCKER_USERNAME" ]] ; then echo "${DOCKER_PASSWORD}" | docker login -u "${DOCKER_USERNAME}" --password-stdin; fi
2225
- sh yarn_setup.sh
2326

2427
script: cd ${SUBPROJECT} && yarn run lint && yarn run test:travis && cd ..

CHANGELOG.md

+10
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,16 @@ All notable changes to this project will be documented in this file.
44

55
The changelog format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
66

7+
## [v2.3.2] - 02-Feb-2021
8+
9+
### Added
10+
11+
- `FromHeight` and `ToHeight` to receipt search endpoint.
12+
13+
### Fixed
14+
15+
- Fixed issues on only multisig and aggregate initiator can query partial transactions.
16+
717
## [v2.3.1] - 19-Jan-2021
818

919
### Fixed

rest/bootstrap-preset.yml

+1
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ nodes:
1010
- trustedHosts: '127.0.0.1, 172.20.0.25, 172.20.0.1'
1111
localNetworks: '127.0.0.1, 172.20.0.25, 172.20.0.1'
1212
brokerOpenPort: 7902
13+
openPort: '{{add $index 7900}}'
1314
gateways:
1415
- excludeDockerService: true
1516
name: rest

rest/package.json

+3-3
Original file line numberDiff line numberDiff line change
@@ -13,14 +13,14 @@
1313
"test": "mocha --full-trace --recursive",
1414
"test:coverage": "nyc npm test && nyc report --reporter=text-lcov",
1515
"test:jenkins": "cross-env JUNIT_REPORT_PATH=test-results.xml mocha --reporter mocha-jenkins-reporter --mongoHost db --forbid-only --full-trace --recursive test || exit 0",
16-
"test:travis": "npm run bootstrap-start-detached && nyc npm test && nyc report --reporter=text-lcov | coveralls && npm run bootstrap-stop ",
16+
"test:travis": "nyc npm test && nyc report --reporter=text-lcov | coveralls",
1717
"lint": "eslint src test",
1818
"lint:fix": "eslint src test --fix",
1919
"lint:jenkins": "eslint -o tests.catapult.lint.xml -f junit src test || exit 0",
2020
"bootstrap-clean": "symbol-bootstrap clean",
2121
"bootstrap-start": "symbol-bootstrap start -a light -c bootstrap-preset.yml --healthCheck",
2222
"bootstrap-start-testnet": "symbol-bootstrap start -p testnet -a dual -c bootstrap-preset-testnet.yml --healthCheck",
23-
"bootstrap-start-detached": "symbol-bootstrap start -a light -c bootstrap-preset.yml --detached --healthCheck",
23+
"bootstrap-start-detached": "symbol-bootstrap start -a light -c bootstrap-preset.yml --detached --healthCheck --pullImages",
2424
"bootstrap-stop": "symbol-bootstrap stop",
2525
"bootstrap-run": "symbol-bootstrap run",
2626
"bootstrap-run-detached": "symbol-bootstrap run --detached --healthCheck",
@@ -47,7 +47,7 @@
4747
"nodemon": "^2.0.6",
4848
"rimraf": "^2.6.3",
4949
"sinon": "^7.3.2",
50-
"symbol-bootstrap": "0.3.0-alpha-202012081631"
50+
"symbol-bootstrap": "0.4.1"
5151
},
5252
"dependencies": {
5353
"catapult-sdk": "link:../catapult-sdk",

rest/src/db/CatapultDb.js

+23-12
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323

2424
const connector = require('./connector');
2525
const { convertToLong, buildOffsetCondition } = require('./dbUtils');
26+
const MultisigDb = require('../plugins/multisig/MultisigDb');
2627
const catapult = require('catapult-sdk');
2728
const MongoDb = require('mongodb');
2829

@@ -304,24 +305,33 @@ class CatapultDb {
304305
* `pageSize` and `pageNumber`. 'sortField' must be within allowed 'sortingOptions'.
305306
* @returns {Promise.<object>} Transactions page.
306307
*/
307-
transactions(group, filters, options) {
308+
async transactions(group, filters, options) {
308309
const sortingOptions = { id: '_id' };
309310

310-
const buildAccountConditions = () => {
311-
if (undefined !== filters.address)
311+
const buildAccountConditions = async () => {
312+
// Check multisig graph if address is used in search criteria for cosigning,
313+
// Then, show transactions for other cosigers.
314+
if (undefined !== filters.address) {
315+
if ('partial' === group) {
316+
const multisigEntries = await new MultisigDb(this).multisigsByAddresses([filters.address]);
317+
318+
if (multisigEntries.length && multisigEntries[0].multisig.multisigAddresses.length) {
319+
const buffers = multisigEntries[0].multisig.multisigAddresses.map(address => address.buffer);
320+
buffers.push(Buffer.from(filters.address));
321+
return { 'meta.addresses': { $in: buffers } };
322+
}
323+
}
312324
return { 'meta.addresses': Buffer.from(filters.address) };
313-
325+
}
314326
const accountConditions = {};
315327
if (undefined !== filters.signerPublicKey)
316328
accountConditions['transaction.signerPublicKey'] = Buffer.from(filters.signerPublicKey);
317-
318329
if (undefined !== filters.recipientAddress)
319330
accountConditions['transaction.recipientAddress'] = Buffer.from(filters.recipientAddress);
320-
321331
return accountConditions;
322332
};
323333

324-
const buildConditions = () => {
334+
const buildConditions = async () => {
325335
let conditions = {};
326336

327337
const offsetCondition = buildOffsetCondition(options, sortingOptions);
@@ -363,7 +373,7 @@ class CatapultDb {
363373
conditions[amountPath].$lte = convertToLong(filters.toTransferAmount);
364374
}
365375

366-
const accountConditions = buildAccountConditions();
376+
const accountConditions = await buildAccountConditions();
367377
if (accountConditions)
368378
conditions = Object.assign(conditions, accountConditions);
369379

@@ -372,7 +382,7 @@ class CatapultDb {
372382

373383
const removedFields = ['meta.addresses'];
374384
const sortConditions = { [sortingOptions[options.sortField]]: options.sortDirection };
375-
const conditions = buildConditions();
385+
const conditions = await buildConditions();
376386

377387
return this.queryPagedDocuments(conditions, removedFields, sortConditions, TransactionGroup[group], options);
378388
}
@@ -470,17 +480,18 @@ class CatapultDb {
470480
// fetch result sorted by specific mosaic amount, this unwinds mosaics and only returns matching mosaics (incomplete response)
471481
queryPromise = this.database.collection('accounts')
472482
.aggregate([], { promoteLongs: false })
473-
.skip(pageSize * pageIndex)
474-
.limit(pageSize)
475483
.unwind('$account.mosaics')
476484
.match(conditions)
477485
.sort(sortConditions)
486+
.skip(pageSize * pageIndex)
487+
.limit(pageSize)
478488
.toArray()
479489
.then(accounts => {
480490
const accountIds = accounts.map(account => account._id);
481491
const newConditions = { _id: { $in: accountIds } };
482-
483492
// repeat the response with the found and sorted account ids, so that the result can be complete with all the mosaics
493+
// Second query set pageIndex to 0;
494+
options.pageNumber = 1;
484495
return this.queryPagedDocuments(newConditions, [], {}, 'accounts', options)
485496
.then(fullAccountsPage => {
486497
// $in results do not preserve query order

rest/src/plugins/multisig/multisigRoutes.js

+2-50
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
* along with Catapult. If not, see <http://www.gnu.org/licenses/>.
2020
*/
2121

22+
const multisigUtils = require('./multisigUtils');
2223
const merkleUtils = require('../../routes/merkleUtils');
2324
const routeUtils = require('../../routes/routeUtils');
2425
const catapult = require('catapult-sdk');
@@ -44,58 +45,9 @@ module.exports = {
4445
});
4546
});
4647

47-
const getMultisigEntries = (multisigEntries, fieldName) => {
48-
const addresses = new Set();
49-
multisigEntries.forEach(multisigEntry => multisigEntry.multisig[fieldName].forEach(address => {
50-
addresses.add(address.buffer);
51-
}));
52-
53-
return db.multisigsByAddresses(Array.from(addresses));
54-
};
55-
5648
server.get('/account/:address/multisig/graph', (req, res, next) => {
5749
const accountAddress = routeUtils.parseArgument(req.params, 'address', 'address');
58-
59-
const multisigLevels = [];
60-
return db.multisigsByAddresses([accountAddress])
61-
.then(multisigEntries => {
62-
if (0 === multisigEntries.length)
63-
return Promise.resolve(undefined);
64-
65-
multisigLevels.push({
66-
level: 0,
67-
multisigEntries: [multisigEntries[0]]
68-
});
69-
70-
return Promise.resolve(multisigEntries[0]);
71-
})
72-
.then(multisigEntry => {
73-
if (undefined === multisigEntry)
74-
return Promise.resolve(undefined);
75-
76-
const handleUpstream = (level, multisigEntries) => getMultisigEntries(multisigEntries, 'multisigAddresses')
77-
.then(entries => {
78-
if (0 === entries.length)
79-
return Promise.resolve();
80-
81-
multisigLevels.unshift({ level, multisigEntries: entries });
82-
return handleUpstream(level - 1, entries);
83-
});
84-
85-
const handleDownstream = (level, multisigEntries) => getMultisigEntries(multisigEntries, 'cosignatoryAddresses')
86-
.then(entries => {
87-
if (0 === entries.length)
88-
return Promise.resolve();
89-
90-
multisigLevels.push({ level, multisigEntries: entries });
91-
return handleDownstream(level + 1, entries);
92-
});
93-
94-
const upstreamPromise = handleUpstream(-1, [multisigEntry]);
95-
const downstreamPromise = handleDownstream(1, [multisigEntry]);
96-
return Promise.all([upstreamPromise, downstreamPromise])
97-
.then(() => multisigLevels);
98-
})
50+
return multisigUtils.getMultisigGraph(db, accountAddress)
9951
.then(response => {
10052
const sender = routeUtils.createSender('multisigGraph');
10153
return undefined === response
+77
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
/*
2+
* Copyright (c) 2016-2019, Jaguar0625, gimre, BloodyRookie, Tech Bureau, Corp.
3+
* Copyright (c) 2020-present, Jaguar0625, gimre, BloodyRookie.
4+
* All rights reserved.
5+
*
6+
* This file is part of Catapult.
7+
*
8+
* Catapult is free software: you can redistribute it and/or modify
9+
* it under the terms of the GNU Lesser General Public License as published by
10+
* the Free Software Foundation, either version 3 of the License, or
11+
* (at your option) any later version.
12+
*
13+
* Catapult is distributed in the hope that it will be useful,
14+
* but WITHOUT ANY WARRANTY; without even the implied warranty of
15+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16+
* GNU Lesser General Public License for more details.
17+
*
18+
* You should have received a copy of the GNU Lesser General Public License
19+
* along with Catapult. If not, see <http://www.gnu.org/licenses/>.
20+
*/
21+
22+
const multisigUtils = {
23+
getMultisigGraph: (db, address) => {
24+
const getMultisigEntries = (multisigEntries, fieldName) => {
25+
const addresses = new Set();
26+
multisigEntries.forEach(multisigEntry => multisigEntry.multisig[fieldName].forEach(multisigAddress => {
27+
addresses.add(multisigAddress.buffer);
28+
}));
29+
30+
return db.multisigsByAddresses(Array.from(addresses));
31+
};
32+
33+
const multisigLevels = [];
34+
return db.multisigsByAddresses([address])
35+
.then(multisigEntries => {
36+
if (0 === multisigEntries.length)
37+
return Promise.resolve(undefined);
38+
39+
multisigLevels.push({
40+
level: 0,
41+
multisigEntries: [multisigEntries[0]]
42+
});
43+
44+
return Promise.resolve(multisigEntries[0]);
45+
})
46+
.then(multisigEntry => {
47+
if (undefined === multisigEntry)
48+
return Promise.resolve(undefined);
49+
50+
const handleUpstream = (level, multisigEntries) => getMultisigEntries(multisigEntries, 'multisigAddresses')
51+
.then(entries => {
52+
if (0 === entries.length)
53+
return Promise.resolve();
54+
55+
multisigLevels.unshift({ level, multisigEntries: entries });
56+
return handleUpstream(level - 1, entries);
57+
});
58+
59+
const handleDownstream = (level, multisigEntries) => getMultisigEntries(multisigEntries, 'cosignatoryAddresses')
60+
.then(entries => {
61+
if (0 === entries.length)
62+
return Promise.resolve();
63+
64+
multisigLevels.push({ level, multisigEntries: entries });
65+
return handleDownstream(level + 1, entries);
66+
});
67+
68+
const upstreamPromise = handleUpstream(-1, [multisigEntry]);
69+
const downstreamPromise = handleDownstream(1, [multisigEntry]);
70+
return Promise.all([upstreamPromise, downstreamPromise])
71+
.then(() => multisigLevels);
72+
});
73+
}
74+
75+
};
76+
77+
module.exports = multisigUtils;

rest/src/plugins/receipts/ReceiptsDb.js

+11
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,17 @@ class ReceiptsDb {
7272
conditions[[`statement.receipts.${artifactIdType}`]] = convertToLong(filters.artifactId);
7373
}
7474

75+
if (undefined !== filters.fromHeight || undefined !== filters.toHeight) {
76+
const heightPath = 'statement.height';
77+
conditions[heightPath] = {};
78+
79+
if (undefined !== filters.fromHeight)
80+
conditions[heightPath].$gte = convertToLong(filters.fromHeight);
81+
82+
if (undefined !== filters.toHeight)
83+
conditions[heightPath].$lte = convertToLong(filters.toHeight);
84+
}
85+
7586
const sortConditions = { [sortingOptions[options.sortField]]: options.sortDirection };
7687
return this.catapultDb.queryPagedDocuments(conditions, [], sortConditions, 'transactionStatements', options);
7788
}

rest/src/plugins/receipts/receiptsRoutes.js

+2
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,8 @@ module.exports = {
2929
const { params } = req;
3030
const filters = {
3131
height: params.height ? routeUtils.parseArgument(params, 'height', 'uint64') : undefined,
32+
fromHeight: params.fromHeight ? routeUtils.parseArgument(params, 'fromHeight', 'uint64') : undefined,
33+
toHeight: params.toHeight ? routeUtils.parseArgument(params, 'toHeight', 'uint64') : undefined,
3234
receiptType: params.receiptType ? routeUtils.parseArgumentAsArray(params, 'receiptType', 'uint') : undefined,
3335
recipientAddress: params.recipientAddress ? routeUtils.parseArgument(params, 'recipientAddress', 'address') : undefined,
3436
senderAddress: params.senderAddress ? routeUtils.parseArgument(params, 'senderAddress', 'address') : undefined,

rest/src/routes/transactionRoutes.js

-2
Original file line numberDiff line numberDiff line change
@@ -72,15 +72,13 @@ module.exports = {
7272
recipientAddress: params.recipientAddress ? routeUtils.parseArgument(params, 'recipientAddress', 'address') : undefined,
7373
transactionTypes: params.type ? routeUtils.parseArgumentAsArray(params, 'type', 'uint') : undefined,
7474
embedded: params.embedded ? routeUtils.parseArgument(params, 'embedded', 'boolean') : undefined,
75-
7675
/** transfer transaction specific filters */
7776
transferMosaicId: params.transferMosaicId ? routeUtils.parseArgument(params, 'transferMosaicId', 'uint64hex') : undefined,
7877
fromTransferAmount: params.fromTransferAmount
7978
? routeUtils.parseArgument(params, 'fromTransferAmount', 'uint64') : undefined,
8079
toTransferAmount: params.toTransferAmount ? routeUtils.parseArgument(params, 'toTransferAmount', 'uint64') : undefined
8180
};
8281
const options = routeUtils.parsePaginationArguments(params, services.config.pageSize, { id: 'objectId' });
83-
8482
return db.transactions(params.group, filters, options)
8583
.then(result => routeUtils.createSender(routeResultTypes.transaction).sendPage(res, next)(result));
8684
});

rest/test/plugins/receipts/receiptsRoutes_spec.js

+28
Original file line numberDiff line numberDiff line change
@@ -206,6 +206,34 @@ describe('receipts routes', () => {
206206
});
207207
});
208208

209+
it('forwards fromHeight', () => {
210+
// Arrange:
211+
const req = { params: { fromHeight: '123' } };
212+
213+
// Act:
214+
return mockServer.callRoute(route, req).then(() => {
215+
// Assert:
216+
expect(dbTransactionStatementsFake.calledOnce).to.equal(true);
217+
expect(dbTransactionStatementsFake.firstCall.args[0].fromHeight).to.deep.equal([123, 0]);
218+
219+
expect(mockServer.next.calledOnce).to.equal(true);
220+
});
221+
});
222+
223+
it('forwards toHeight', () => {
224+
// Arrange:
225+
const req = { params: { toHeight: '123' } };
226+
227+
// Act:
228+
return mockServer.callRoute(route, req).then(() => {
229+
// Assert:
230+
expect(dbTransactionStatementsFake.calledOnce).to.equal(true);
231+
expect(dbTransactionStatementsFake.firstCall.args[0].toHeight).to.deep.equal([123, 0]);
232+
233+
expect(mockServer.next.calledOnce).to.equal(true);
234+
});
235+
});
236+
209237
describe('forwards receiptType', () => {
210238
it('one element', () => {
211239
// Arrange:

0 commit comments

Comments
 (0)