Skip to content

Commit dff0235

Browse files
author
Grégory Saive
authored
Merge pull request #131 from rg911/task/g112_sign_with_cosignatories
Added #112 sign with cosignatories
2 parents da9f0fe + 84a061b commit dff0235

File tree

4 files changed

+131
-6
lines changed

4 files changed

+131
-6
lines changed

src/model/transaction/AggregateTransaction.ts

Lines changed: 26 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ import { PublicAccount } from '../account/PublicAccount';
2222
import { NetworkType } from '../blockchain/NetworkType';
2323
import { UInt64 } from '../UInt64';
2424
import { AggregateTransactionCosignature } from './AggregateTransactionCosignature';
25+
import { CosignatureSignedTransaction } from './CosignatureSignedTransaction';
2526
import { Deadline } from './Deadline';
2627
import { InnerTransaction } from './InnerTransaction';
2728
import { SignedTransaction } from './SignedTransaction';
@@ -96,7 +97,7 @@ export class AggregateTransaction extends Transaction {
9697
* @param {InnerTransaction[]} innerTransactions
9798
* @param {NetworkType} networkType
9899
* @param {AggregateTransactionCosignature[]} cosignatures
99-
* @param {UInt64} maxFee - (Optional) Max fee defined by the sender
100+
* @param {UInt64} maxFee - (Optional) Max fee defined by the sender
100101
* @return {AggregateTransaction}
101102
*/
102103
public static createBonded(deadline: Deadline,
@@ -149,6 +150,29 @@ export class AggregateTransaction extends Transaction {
149150
this.type, this.networkType);
150151
}
151152

153+
/**
154+
* @internal
155+
* Sign transaction with cosignatories collected from cosigned transactions and creating a new SignedTransaction
156+
* For off chain Aggregated Complete Transaction co-signing.
157+
* @param initiatorAccount - Initiator account
158+
* @param {CosignatureSignedTransaction[]} cosignatureSignedTransactions - Array of cosigned transaction
159+
* @param generationHash - Network generation hash hex
160+
* @param {SignSchema} signSchema The Sign Schema. (KECCAK_REVERSED_KEY / SHA3)
161+
* @return {SignedTransaction}
162+
*/
163+
public signTransactionGivenSignatures(initiatorAccount: Account,
164+
cosignatureSignedTransactions: CosignatureSignedTransaction[],
165+
generationHash: string,
166+
signSchema: SignSchema= SignSchema.SHA3) {
167+
const aggregateTransaction = this.buildTransaction();
168+
const signedTransactionRaw = aggregateTransaction.signTransactionGivenSignatures(initiatorAccount,
169+
cosignatureSignedTransactions,
170+
generationHash,
171+
signSchema);
172+
return new SignedTransaction(signedTransactionRaw.payload, signedTransactionRaw.hash, initiatorAccount.publicKey,
173+
this.type, this.networkType);
174+
}
175+
152176
/**
153177
* Check if account has signed transaction
154178
* @param publicAccount - Signer public account
@@ -179,4 +203,4 @@ export class AggregateTransaction extends Transaction {
179203

180204
return byteSize + byteTransactionsSize + byteTransactions;
181205
}
182-
}
206+
}

src/model/transaction/CosignatureTransaction.ts

Lines changed: 22 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ import {CosignatureTransaction as CosignaturetransactionLibrary} from '../../inf
1919
import {Account} from '../account/Account';
2020
import {AggregateTransaction} from './AggregateTransaction';
2121
import {CosignatureSignedTransaction} from './CosignatureSignedTransaction';
22+
import { VerifiableTransaction } from '../../infrastructure/builders/VerifiableTransaction';
2223

2324
/**
2425
* Cosignature transaction is used to sign an aggregate transactions with missing cosignatures.
@@ -46,6 +47,26 @@ export class CosignatureTransaction {
4647
return new CosignatureTransaction(transactionToCosign);
4748
}
4849

50+
/**
51+
* Co-sign transaction with transaction payload (off chain)
52+
* Creating a new CosignatureSignedTransaction
53+
* @param account - The signing account
54+
* @param payload - off transaction payload (aggregated transaction is unannounced)
55+
* @param gernationHash - Network generation hash
56+
* @returns {CosignatureSignedTransaction}
57+
*/
58+
public static signTransactionPayload(account: Account, payload: string, gernationHash: string): CosignatureSignedTransaction {
59+
/**
60+
* For aggregated complete transaction, cosignatories are gathered off chain announced.
61+
*/
62+
const transactionHash = VerifiableTransaction.createTransactionHash(payload, gernationHash);
63+
const aggregateSignatureTransaction = new CosignaturetransactionLibrary(transactionHash);
64+
const signedTransactionRaw = aggregateSignatureTransaction.signCosignatoriesTransaction(account);
65+
return new CosignatureSignedTransaction(signedTransactionRaw.parentHash,
66+
signedTransactionRaw.signature,
67+
signedTransactionRaw.signer);
68+
}
69+
4970
/**
5071
* @internal
5172
* Serialize and sign transaction creating a new SignedTransaction
@@ -60,4 +81,4 @@ export class CosignatureTransaction {
6081
signedTransactionRaw.signature,
6182
signedTransactionRaw.signer);
6283
}
63-
}
84+
}

test/model/transaction/AggregateTransaction.spec.ts

Lines changed: 65 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616

1717
import {expect} from 'chai';
1818
import {ChronoUnit} from 'js-joda';
19+
import { TransactionMapping } from '../../../src/core/utils/TransactionMapping';
1920
import {CreateTransactionFromDTO} from '../../../src/infrastructure/transaction/CreateTransactionFromDTO';
2021
import {Account} from '../../../src/model/account/Account';
2122
import {Address} from '../../../src/model/account/Address';
@@ -27,6 +28,8 @@ import {MosaicProperties} from '../../../src/model/mosaic/MosaicProperties';
2728
import {MosaicSupplyType} from '../../../src/model/mosaic/MosaicSupplyType';
2829
import { NetworkCurrencyMosaic } from '../../../src/model/mosaic/NetworkCurrencyMosaic';
2930
import {AggregateTransaction} from '../../../src/model/transaction/AggregateTransaction';
31+
import { CosignatureSignedTransaction } from '../../../src/model/transaction/CosignatureSignedTransaction';
32+
import { CosignatureTransaction } from '../../../src/model/transaction/CosignatureTransaction';
3033
import {Deadline} from '../../../src/model/transaction/Deadline';
3134
import {ModifyMultisigAccountTransaction} from '../../../src/model/transaction/ModifyMultisigAccountTransaction';
3235
import {MosaicDefinitionTransaction} from '../../../src/model/transaction/MosaicDefinitionTransaction';
@@ -35,6 +38,7 @@ import {MultisigCosignatoryModification} from '../../../src/model/transaction/Mu
3538
import {MultisigCosignatoryModificationType} from '../../../src/model/transaction/MultisigCosignatoryModificationType';
3639
import {PlainMessage} from '../../../src/model/transaction/PlainMessage';
3740
import {RegisterNamespaceTransaction} from '../../../src/model/transaction/RegisterNamespaceTransaction';
41+
import { TransactionType } from '../../../src/model/transaction/TransactionType';
3842
import {TransferTransaction} from '../../../src/model/transaction/TransferTransaction';
3943
import {UInt64} from '../../../src/model/UInt64';
4044
import {Cosignatory2Account, CosignatoryAccount, MultisigAccount, TestingAccount} from '../../conf/conf.spec';
@@ -80,7 +84,7 @@ describe('AggregateTransaction', () => {
8084
[transferTransaction.toAggregate(account.publicAccount)],
8185
NetworkType.MIJIN_TEST,
8286
[],
83-
new UInt64([1, 0])
87+
new UInt64([1, 0]),
8488
);
8589

8690
expect(aggregateTransaction.maxFee.higher).to.be.equal(0);
@@ -382,6 +386,65 @@ describe('AggregateTransaction', () => {
382386
}).to.throw(Error, 'Inner transaction cannot be an aggregated transaction.');
383387
});
384388

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

test/model/transaction/CosignatureTransaction.spec.ts

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,13 +17,17 @@
1717
import {expect} from 'chai';
1818
import {CreateTransactionFromDTO} from '../../../src/infrastructure/transaction/CreateTransactionFromDTO';
1919
import {Account} from '../../../src/model/account/Account';
20+
import { NetworkType } from '../../../src/model/blockchain/NetworkType';
2021
import {AggregateTransaction} from '../../../src/model/transaction/AggregateTransaction';
2122
import {CosignatureTransaction} from '../../../src/model/transaction/CosignatureTransaction';
23+
import { Deadline } from '../../../src/model/transaction/Deadline';
24+
import { PlainMessage } from '../../../src/model/transaction/PlainMessage';
25+
import { TransferTransaction } from '../../../src/model/transaction/TransferTransaction';
2226
import {TestingAccount} from '../../conf/conf.spec';
2327

2428
describe('CosignatureTransaction', () => {
2529
let account: Account;
26-
30+
const generationHash = '57F7DA205008026C776CB6AED843393F04CD458E0AA2D9F1D5F31A402072B2D6';
2731
before(() => {
2832
account = TestingAccount;
2933
});
@@ -112,4 +116,17 @@ describe('CosignatureTransaction', () => {
112116
'93D2F8522C8DEAC74BEFBCB61AF6414ADF27B2176D6A24FEF612AA6DB2F562176A11C46BA6D5E05430042CB5705');
113117
expect(cosignatureSignedTransaction.signer).to.be.equal(account.publicKey);
114118
});
119+
120+
it('should sign a transaction with transaction payload', () => {
121+
const txPayload = TransferTransaction.create(Deadline.create(),
122+
account.address,
123+
[],
124+
PlainMessage.create('a to b'),
125+
NetworkType.MIJIN_TEST).serialize();
126+
127+
const signedTx = CosignatureTransaction.signTransactionPayload(account, txPayload, generationHash);
128+
129+
expect(signedTx.signer).to.be.equal('C2F93346E27CE6AD1A9F8F5E3066F8326593A406BDF357ACB041E2F9AB402EFE');
130+
expect(signedTx.signer).to.be.equal(account.publicKey);
131+
});
115132
});

0 commit comments

Comments
 (0)