Skip to content

Commit e5bc363

Browse files
committed
Added #112 sign with cosignatories
1 parent 0f1928e commit e5bc363

File tree

4 files changed

+124
-1
lines changed

4 files changed

+124
-1
lines changed

src/model/transaction/AggregateTransaction.ts

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ import { PublicAccount } from '../account/PublicAccount';
2121
import { NetworkType } from '../blockchain/NetworkType';
2222
import { UInt64 } from '../UInt64';
2323
import { AggregateTransactionCosignature } from './AggregateTransactionCosignature';
24+
import { CosignatureSignedTransaction } from './CosignatureSignedTransaction';
2425
import { Deadline } from './Deadline';
2526
import { InnerTransaction } from './InnerTransaction';
2627
import { SignedTransaction } from './SignedTransaction';
@@ -143,6 +144,23 @@ export class AggregateTransaction extends Transaction {
143144
this.type, this.networkType);
144145
}
145146

147+
/**
148+
* @internal
149+
* Sign transaction with cosignatories collected from cosigned transactions and creating a new SignedTransaction
150+
* For off chain Aggregated Complete Transaction co-signing.
151+
* @param initiatorAccount - Initiator account
152+
* @param {CosignatureSignedTransaction[]} cosignatureSignedTransaction - Array of cosigned transaction
153+
* @return {SignedTransaction}
154+
*/
155+
public signTransactionWithCosignedTransactions(initiatorAccount: Account,
156+
cosignatureSignedTransaction: CosignatureSignedTransaction[]) {
157+
const aggregateTransaction = this.buildTransaction();
158+
const signedTransactionRaw = aggregateTransaction.signTransactionWithCosignedTransactions(initiatorAccount,
159+
cosignatureSignedTransaction);
160+
return new SignedTransaction(signedTransactionRaw.payload, signedTransactionRaw.hash, initiatorAccount.publicKey,
161+
this.type, this.networkType);
162+
}
163+
146164
/**
147165
* Check if account has signed transaction
148166
* @param publicAccount - Signer public account

src/model/transaction/CosignatureTransaction.ts

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,24 @@ export class CosignatureTransaction {
4545
return new CosignatureTransaction(transactionToCosign);
4646
}
4747

48+
/**
49+
* Co-sign transaction with transaction hash (off chain)
50+
* Creating a new CosignatureSignedTransaction
51+
* @param account - The signing account
52+
* @param transactionHash - off transaction hash (aggregated transaction is unannounced)
53+
* @returns {CosignatureSignedTransaction}
54+
*/
55+
public static signTransactionHashWith(account: Account, transactionHash: string): CosignatureSignedTransaction {
56+
/**
57+
* For aggregated complete transaction, cosignatories are gathered off chain announced.
58+
*/
59+
const aggregateSignatureTransaction = new CosignaturetransactionLibrary(transactionHash);
60+
const signedTransactionRaw = aggregateSignatureTransaction.signCosignatoriesTransaction(account);
61+
return new CosignatureSignedTransaction(signedTransactionRaw.parentHash,
62+
signedTransactionRaw.signature,
63+
signedTransactionRaw.signer);
64+
}
65+
4866
/**
4967
* @internal
5068
* Serialize and sign transaction creating a new SignedTransaction

test/model/transaction/AggregateTransaction.spec.ts

Lines changed: 67 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,8 @@
1616

1717
import {expect} from 'chai';
1818
import {ChronoUnit} from 'js-joda';
19+
import { VerifiableTransaction } from 'nem2-library';
20+
import { TransactionMapping } from '../../../src/core/utils/TransactionMapping';
1921
import {CreateTransactionFromDTO} from '../../../src/infrastructure/transaction/CreateTransactionFromDTO';
2022
import {Account} from '../../../src/model/account/Account';
2123
import {Address} from '../../../src/model/account/Address';
@@ -27,6 +29,8 @@ import {MosaicProperties} from '../../../src/model/mosaic/MosaicProperties';
2729
import {MosaicSupplyType} from '../../../src/model/mosaic/MosaicSupplyType';
2830
import { NetworkCurrencyMosaic } from '../../../src/model/mosaic/NetworkCurrencyMosaic';
2931
import {AggregateTransaction} from '../../../src/model/transaction/AggregateTransaction';
32+
import { CosignatureSignedTransaction } from '../../../src/model/transaction/CosignatureSignedTransaction';
33+
import { CosignatureTransaction } from '../../../src/model/transaction/CosignatureTransaction';
3034
import {Deadline} from '../../../src/model/transaction/Deadline';
3135
import {ModifyMultisigAccountTransaction} from '../../../src/model/transaction/ModifyMultisigAccountTransaction';
3236
import {MosaicDefinitionTransaction} from '../../../src/model/transaction/MosaicDefinitionTransaction';
@@ -35,6 +39,7 @@ import {MultisigCosignatoryModification} from '../../../src/model/transaction/Mu
3539
import {MultisigCosignatoryModificationType} from '../../../src/model/transaction/MultisigCosignatoryModificationType';
3640
import {PlainMessage} from '../../../src/model/transaction/PlainMessage';
3741
import {RegisterNamespaceTransaction} from '../../../src/model/transaction/RegisterNamespaceTransaction';
42+
import { TransactionType } from '../../../src/model/transaction/TransactionType';
3843
import {TransferTransaction} from '../../../src/model/transaction/TransferTransaction';
3944
import {UInt64} from '../../../src/model/UInt64';
4045
import {Cosignatory2Account, CosignatoryAccount, MultisigAccount, TestingAccount} from '../../conf/conf.spec';
@@ -80,7 +85,7 @@ describe('AggregateTransaction', () => {
8085
[transferTransaction.toAggregate(account.publicAccount)],
8186
NetworkType.MIJIN_TEST,
8287
[],
83-
new UInt64([1, 0])
88+
new UInt64([1, 0]),
8489
);
8590

8691
expect(aggregateTransaction.maxFee.higher).to.be.equal(0);
@@ -382,6 +387,67 @@ describe('AggregateTransaction', () => {
382387
}).to.throw(Error, 'Inner transaction cannot be an aggregated transaction.');
383388
});
384389

390+
it('Should create signed transaction with cosignatories - Aggregated Complete', () => {
391+
/**
392+
* https://github.com/nemtech/nem2-sdk-typescript-javascript/issues/112
393+
*/
394+
const accountAlice = TestingAccount;
395+
const accountBob = CosignatoryAccount;
396+
const accountCarol = Cosignatory2Account;
397+
398+
const AtoBTx = TransferTransaction.create(Deadline.create(),
399+
accountBob.address,
400+
[],
401+
PlainMessage.create('a to b'),
402+
NetworkType.MIJIN_TEST);
403+
const BtoATx = TransferTransaction.create(Deadline.create(),
404+
accountAlice.address,
405+
[],
406+
PlainMessage.create('b to a'),
407+
NetworkType.MIJIN_TEST);
408+
const CtoATx = TransferTransaction.create(Deadline.create(),
409+
accountAlice.address,
410+
[],
411+
PlainMessage.create('c to a'),
412+
NetworkType.MIJIN_TEST);
413+
414+
// 01. Alice creates the aggregated tx and serialize it, and generate the hash. Then send to Bob & Carol
415+
const aggregateTransactionPayload = AggregateTransaction.createComplete(
416+
Deadline.create(),
417+
[
418+
AtoBTx.toAggregate(accountAlice.publicAccount),
419+
BtoATx.toAggregate(accountBob.publicAccount),
420+
CtoATx.toAggregate(accountCarol.publicAccount)],
421+
NetworkType.MIJIN_TEST,
422+
[],
423+
).serialize();
424+
425+
const txHash = VerifiableTransaction.createTransactionHash(aggregateTransactionPayload);
426+
427+
// 02.1 Bob cosigns the tx and sends it back to Alice
428+
const signedTxBob = CosignatureTransaction.signTransactionHashWith(accountBob, txHash);
429+
430+
// 02.2 Carol cosigns the tx and sends it back to Alice
431+
const signedTxCarol = CosignatureTransaction.signTransactionHashWith(accountCarol, txHash);
432+
433+
// 03. Alice collects the cosignatures, recreate, sign, and announces the transaction
434+
435+
// First Alice need to append cosignatories to current transaction.
436+
const cosignatureSignedTransactions = [
437+
new CosignatureSignedTransaction(signedTxBob.parentHash, signedTxBob.signature, signedTxBob.signer),
438+
new CosignatureSignedTransaction(signedTxCarol.parentHash, signedTxCarol.signature, signedTxCarol.signer),
439+
];
440+
441+
const recreatedTx = TransactionMapping.createFromPayload(aggregateTransactionPayload) as AggregateTransaction;
442+
443+
const signedTransaction = recreatedTx.signTransactionWithCosignedTransactions(accountAlice, cosignatureSignedTransactions);
444+
445+
expect(signedTransaction.type).to.be.equal(TransactionType.AGGREGATE_COMPLETE);
446+
expect(signedTransaction.signer).to.be.equal(accountAlice.publicKey);
447+
expect(signedTransaction.payload.indexOf(accountBob.publicKey) > -1).to.be.true;
448+
expect(signedTransaction.payload.indexOf(accountCarol.publicKey) > -1).to.be.true;
449+
});
450+
385451
describe('size', () => {
386452
it('should return 282 for AggregateTransaction byte size with TransferTransaction with 1 mosaic and message NEM', () => {
387453
const transaction = TransferTransaction.create(

test/model/transaction/CosignatureTransaction.spec.ts

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,10 +15,15 @@
1515
*/
1616

1717
import {expect} from 'chai';
18+
import { VerifiableTransaction } from 'nem2-library';
1819
import {CreateTransactionFromDTO} from '../../../src/infrastructure/transaction/CreateTransactionFromDTO';
1920
import {Account} from '../../../src/model/account/Account';
21+
import { NetworkType } from '../../../src/model/blockchain/NetworkType';
2022
import {AggregateTransaction} from '../../../src/model/transaction/AggregateTransaction';
2123
import {CosignatureTransaction} from '../../../src/model/transaction/CosignatureTransaction';
24+
import { Deadline } from '../../../src/model/transaction/Deadline';
25+
import { PlainMessage } from '../../../src/model/transaction/PlainMessage';
26+
import { TransferTransaction } from '../../../src/model/transaction/TransferTransaction';
2227
import {TestingAccount} from '../../conf/conf.spec';
2328

2429
describe('CosignatureTransaction', () => {
@@ -112,4 +117,20 @@ describe('CosignatureTransaction', () => {
112117
'93D2F8522C8DEAC74BEFBCB61AF6414ADF27B2176D6A24FEF612AA6DB2F562176A11C46BA6D5E05430042CB5705');
113118
expect(cosignatureSignedTransaction.signer).to.be.equal(account.publicKey);
114119
});
120+
121+
it('should sign a transaction with transaction hash', () => {
122+
const txPayload = TransferTransaction.create(Deadline.create(),
123+
account.address,
124+
[],
125+
PlainMessage.create('a to b'),
126+
NetworkType.MIJIN_TEST).serialize();
127+
128+
const txHash = VerifiableTransaction.createTransactionHash(txPayload);
129+
130+
const signedTx = CosignatureTransaction.signTransactionHashWith(account, txHash);
131+
132+
expect(signedTx.parentHash).to.be.equal(txHash);
133+
expect(signedTx.signer).to.be.equal('C2F93346E27CE6AD1A9F8F5E3066F8326593A406BDF357ACB041E2F9AB402EFE');
134+
expect(signedTx.signer).to.be.equal(account.publicKey);
135+
});
115136
});

0 commit comments

Comments
 (0)