Skip to content

Commit ef44e02

Browse files
authored
Merge pull request #365 from NEMStudios/task/g352_aggregate_bonded_announcing
Task/g352 aggregate bonded announcing
2 parents 847006a + 0d21737 commit ef44e02

File tree

6 files changed

+385
-10
lines changed

6 files changed

+385
-10
lines changed
Lines changed: 292 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,292 @@
1+
/*
2+
* Copyright 2019 NEM
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
import { expect } from 'chai';
18+
import { Listener } from '../../src/infrastructure/Listener';
19+
import { NamespaceHttp } from '../../src/infrastructure/NamespaceHttp';
20+
import { TransactionHttp } from '../../src/infrastructure/TransactionHttp';
21+
import { Account } from '../../src/model/account/Account';
22+
import { Address } from '../../src/model/account/Address';
23+
import { NetworkType } from '../../src/model/blockchain/NetworkType';
24+
import { PlainMessage } from '../../src/model/message/PlainMessage';
25+
import { Mosaic } from '../../src/model/mosaic/Mosaic';
26+
import { MosaicId } from '../../src/model/mosaic/MosaicId';
27+
import { NetworkCurrencyMosaic } from '../../src/model/mosaic/NetworkCurrencyMosaic';
28+
import { NamespaceId } from '../../src/model/namespace/NamespaceId';
29+
import { AggregateTransaction } from '../../src/model/transaction/AggregateTransaction';
30+
import { Deadline } from '../../src/model/transaction/Deadline';
31+
import { LockFundsTransaction } from '../../src/model/transaction/LockFundsTransaction';
32+
import { MultisigAccountModificationTransaction } from '../../src/model/transaction/MultisigAccountModificationTransaction';
33+
import { TransactionType } from '../../src/model/transaction/TransactionType';
34+
import { TransferTransaction } from '../../src/model/transaction/TransferTransaction';
35+
import { UInt64 } from '../../src/model/UInt64';
36+
import { TransactionService } from '../../src/service/TransactionService';
37+
import { TransactionUtils } from '../infrastructure/TransactionUtils';
38+
39+
describe('TransactionService', () => {
40+
let account: Account;
41+
let account2: Account;
42+
let multisigAccount: Account;
43+
let cosignAccount1: Account;
44+
let cosignAccount2: Account;
45+
let cosignAccount3: Account;
46+
let networkCurrencyMosaicId: MosaicId;
47+
let url: string;
48+
let generationHash: string;
49+
let transactionHttp: TransactionHttp;
50+
let transactionService: TransactionService;
51+
let namespaceHttp: NamespaceHttp;
52+
let config;
53+
54+
before((done) => {
55+
const path = require('path');
56+
require('fs').readFile(path.resolve(__dirname, '../conf/network.conf'), (err, data) => {
57+
if (err) {
58+
throw err;
59+
}
60+
const json = JSON.parse(data);
61+
config = json;
62+
account = Account.createFromPrivateKey(json.testAccount.privateKey, NetworkType.MIJIN_TEST);
63+
account2 = Account.createFromPrivateKey(json.testAccount2.privateKey, NetworkType.MIJIN_TEST);
64+
multisigAccount = Account.createFromPrivateKey(json.multisigAccount.privateKey, NetworkType.MIJIN_TEST);
65+
cosignAccount1 = Account.createFromPrivateKey(json.cosignatoryAccount.privateKey, NetworkType.MIJIN_TEST);
66+
cosignAccount2 = Account.createFromPrivateKey(json.cosignatory2Account.privateKey, NetworkType.MIJIN_TEST);
67+
cosignAccount3 = Account.createFromPrivateKey(json.cosignatory3Account.privateKey, NetworkType.MIJIN_TEST);
68+
url = json.apiUrl;
69+
generationHash = json.generationHash;
70+
transactionHttp = new TransactionHttp(url);
71+
namespaceHttp = new NamespaceHttp(url);
72+
transactionService = new TransactionService(url);
73+
done();
74+
});
75+
});
76+
77+
/**
78+
* =========================
79+
* Setup test data
80+
* =========================
81+
*/
82+
describe('Get network currency mosaic id', () => {
83+
it('get mosaicId', (done) => {
84+
namespaceHttp.getLinkedMosaicId(new NamespaceId('cat.currency')).subscribe((networkMosaicId) => {
85+
networkCurrencyMosaicId = networkMosaicId;
86+
done();
87+
});
88+
});
89+
});
90+
91+
describe('Setup test multisig account', () => {
92+
let listener: Listener;
93+
before (() => {
94+
listener = new Listener(config.apiUrl);
95+
return listener.open();
96+
});
97+
after(() => {
98+
return listener.close();
99+
});
100+
it('Announce MultisigAccountModificationTransaction', (done) => {
101+
const modifyMultisigAccountTransaction = MultisigAccountModificationTransaction.create(
102+
Deadline.create(),
103+
2,
104+
1,
105+
[
106+
cosignAccount1.publicAccount,
107+
cosignAccount2.publicAccount,
108+
cosignAccount3.publicAccount,
109+
],
110+
[],
111+
NetworkType.MIJIN_TEST,
112+
);
113+
114+
const aggregateTransaction = AggregateTransaction.createComplete(Deadline.create(),
115+
[modifyMultisigAccountTransaction.toAggregate(multisigAccount.publicAccount)],
116+
NetworkType.MIJIN_TEST,
117+
[]);
118+
const signedTransaction = aggregateTransaction
119+
.signTransactionWithCosignatories(multisigAccount, [cosignAccount1, cosignAccount2, cosignAccount3], generationHash);
120+
121+
listener.confirmed(multisigAccount.address).subscribe(() => {
122+
done();
123+
});
124+
listener.status(multisigAccount.address).subscribe((error) => {
125+
console.log('Error:', error);
126+
done();
127+
});
128+
transactionHttp.announce(signedTransaction);
129+
});
130+
});
131+
132+
/**
133+
* =========================
134+
* Test
135+
* =========================
136+
*/
137+
138+
describe('should announce transaction', () => {
139+
let listener: Listener;
140+
before (() => {
141+
listener = new Listener(config.apiUrl);
142+
return listener.open();
143+
});
144+
after(() => {
145+
return listener.close();
146+
});
147+
it('announce', (done) => {
148+
const transferTransaction = TransferTransaction.create(
149+
Deadline.create(),
150+
account2.address,
151+
[
152+
NetworkCurrencyMosaic.createAbsolute(1),
153+
],
154+
PlainMessage.create('test-message'),
155+
NetworkType.MIJIN_TEST,
156+
);
157+
const signedTransaction = transferTransaction.signWith(account, generationHash);
158+
transactionService.announce(signedTransaction, listener).subscribe((tx: TransferTransaction) => {
159+
expect(tx.signer!.publicKey).to.be.equal(account.publicKey);
160+
expect((tx.recipientAddress as Address).equals(account2.address)).to.be.true;
161+
expect(tx.message.payload).to.be.equal('test-message');
162+
done();
163+
});
164+
});
165+
});
166+
167+
describe('should announce aggregate bonded with hashlock', () => {
168+
let listener: Listener;
169+
before (() => {
170+
listener = new Listener(config.apiUrl);
171+
return listener.open();
172+
});
173+
after(() => {
174+
return listener.close();
175+
});
176+
it('announce', (done) => {
177+
const signedAggregatedTransaction =
178+
TransactionUtils.createSignedAggregatedBondTransaction(multisigAccount, account, account2.address, generationHash);
179+
const lockFundsTransaction = LockFundsTransaction.create(
180+
Deadline.create(),
181+
new Mosaic(networkCurrencyMosaicId, UInt64.fromUint(10 * Math.pow(10, NetworkCurrencyMosaic.DIVISIBILITY))),
182+
UInt64.fromUint(1000),
183+
signedAggregatedTransaction,
184+
NetworkType.MIJIN_TEST,
185+
);
186+
const signedLockFundsTransaction = lockFundsTransaction.signWith(account, generationHash);
187+
transactionService
188+
.announceHashLockAggregateBonded(signedLockFundsTransaction, signedAggregatedTransaction, listener).subscribe((tx) => {
189+
expect(tx.signer!.publicKey).to.be.equal(account.publicKey);
190+
expect(tx.type).to.be.equal(TransactionType.AGGREGATE_BONDED);
191+
done();
192+
});
193+
});
194+
});
195+
196+
describe('should announce aggregate bonded transaction', () => {
197+
let listener: Listener;
198+
before (() => {
199+
listener = new Listener(config.apiUrl);
200+
return listener.open();
201+
});
202+
after(() => {
203+
return listener.close();
204+
});
205+
it('announce', (done) => {
206+
const signedAggregatedTransaction =
207+
TransactionUtils.createSignedAggregatedBondTransaction(multisigAccount, account, account2.address, generationHash);
208+
const lockFundsTransaction = LockFundsTransaction.create(
209+
Deadline.create(),
210+
new Mosaic(networkCurrencyMosaicId, UInt64.fromUint(10 * Math.pow(10, NetworkCurrencyMosaic.DIVISIBILITY))),
211+
UInt64.fromUint(1000),
212+
signedAggregatedTransaction,
213+
NetworkType.MIJIN_TEST,
214+
);
215+
const signedLockFundsTransaction = lockFundsTransaction.signWith(account, generationHash);
216+
transactionService.announce(signedLockFundsTransaction, listener).subscribe(() => {
217+
transactionService.announceAggregateBonded(signedAggregatedTransaction, listener).subscribe((tx) => {
218+
expect(tx.signer!.publicKey).to.be.equal(account.publicKey);
219+
expect(tx.type).to.be.equal(TransactionType.AGGREGATE_BONDED);
220+
done();
221+
});
222+
});
223+
});
224+
});
225+
226+
/**
227+
* =========================
228+
* House Keeping
229+
* =========================
230+
*/
231+
232+
describe('Restore test multisig Accounts', () => {
233+
let listener: Listener;
234+
before (() => {
235+
listener = new Listener(config.apiUrl);
236+
return listener.open();
237+
});
238+
after(() => {
239+
return listener.close();
240+
});
241+
it('Announce MultisigAccountModificationTransaction', (done) => {
242+
const removeCosigner1 = MultisigAccountModificationTransaction.create(
243+
Deadline.create(),
244+
-1,
245+
0,
246+
[],
247+
[ cosignAccount1.publicAccount,
248+
],
249+
NetworkType.MIJIN_TEST,
250+
);
251+
const removeCosigner2 = MultisigAccountModificationTransaction.create(
252+
Deadline.create(),
253+
0,
254+
0,
255+
[],
256+
[
257+
cosignAccount2.publicAccount,
258+
],
259+
NetworkType.MIJIN_TEST,
260+
);
261+
262+
const removeCosigner3 = MultisigAccountModificationTransaction.create(
263+
Deadline.create(),
264+
-1,
265+
-1,
266+
[],
267+
[
268+
cosignAccount3.publicAccount,
269+
],
270+
NetworkType.MIJIN_TEST,
271+
);
272+
273+
const aggregateTransaction = AggregateTransaction.createComplete(Deadline.create(),
274+
[removeCosigner1.toAggregate(multisigAccount.publicAccount),
275+
removeCosigner2.toAggregate(multisigAccount.publicAccount),
276+
removeCosigner3.toAggregate(multisigAccount.publicAccount)],
277+
NetworkType.MIJIN_TEST,
278+
[]);
279+
const signedTransaction = aggregateTransaction
280+
.signTransactionWithCosignatories(cosignAccount1, [cosignAccount2, cosignAccount3], generationHash);
281+
282+
listener.confirmed(cosignAccount1.address).subscribe(() => {
283+
done();
284+
});
285+
listener.status(cosignAccount1.address).subscribe((error) => {
286+
console.log('Error:', error);
287+
done();
288+
});
289+
transactionHttp.announce(signedTransaction);
290+
});
291+
});
292+
});

src/infrastructure/Listener.ts

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -239,7 +239,7 @@ export class Listener {
239239
* it emits a new Transaction in the event stream.
240240
*
241241
* @param address address we listen when a transaction is in confirmed state
242-
* @param transactionHash transaction hash for the filter
242+
* @param transactionHash transactionHash for filtering multiple transactions
243243
* @return an observable stream of Transaction with state confirmed
244244
*/
245245
public confirmed(address: Address, transactionHash?: string): Observable<Transaction> {
@@ -249,7 +249,7 @@ export class Listener {
249249
filter((_) => _.message instanceof Transaction),
250250
map((_) => _.message as Transaction),
251251
filter((_) => this.transactionFromAddress(_, address)),
252-
filter((_) => _.transactionInfo!.hash === transactionHash || transactionHash === undefined),
252+
filter((_) => transactionHash === undefined || _.transactionInfo!.hash === transactionHash),
253253
);
254254
}
255255

@@ -292,7 +292,7 @@ export class Listener {
292292
* it emits a new {@link AggregateTransaction} in the event stream.
293293
*
294294
* @param address address we listen when a transaction with missing signatures state
295-
* @param transactionHash transaction hash for the filter
295+
* @param transactionHash transactionHash for filtering multiple transactions
296296
* @return an observable stream of AggregateTransaction with missing signatures state
297297
*/
298298
public aggregateBondedAdded(address: Address, transactionHash?: string): Observable<AggregateTransaction> {
@@ -302,7 +302,7 @@ export class Listener {
302302
filter((_) => _.message instanceof AggregateTransaction),
303303
map((_) => _.message as AggregateTransaction),
304304
filter((_) => this.transactionFromAddress(_, address)),
305-
filter((_) => _.transactionInfo!.hash === transactionHash || transactionHash === undefined),
305+
filter((_) => transactionHash === undefined || _.transactionInfo!.hash === transactionHash),
306306
);
307307
}
308308

src/model/transaction/SignedTransaction.ts

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,8 @@
1414
* limitations under the License.
1515
*/
1616

17+
import { Address } from '../account/Address';
18+
import { PublicAccount } from '../account/PublicAccount';
1719
import {NetworkType} from '../blockchain/NetworkType';
1820

1921
/**
@@ -65,4 +67,12 @@ export class SignedTransaction {
6567
networkType: this.networkType,
6668
};
6769
}
70+
71+
/**
72+
* Return signer's address
73+
* @returns {Address}
74+
*/
75+
getSignerAddress(): Address {
76+
return PublicAccount.createFromPublicKey(this.signerPublicKey, this.networkType).address;
77+
}
6878
}

0 commit comments

Comments
 (0)