Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
38 commits
Select commit Hold shift + click to select a range
486d378
Added #4 check if an aggregate complete transaction has all cosignato…
rg911 Mar 18, 2019
7d1d2fb
Added #4 check if an aggregate complete transaction has all cosignato…
rg911 Mar 18, 2019
41b0b45
#4
rg911 Apr 1, 2019
e50f160
Merge branch 'task/g4_check_act_has_all_sosignatories' of github.com:…
rg911 Apr 1, 2019
319ba1d
Added #4 check if an aggregate complete transaction has all cosignato…
rg911 Mar 18, 2019
b740dee
#4
rg911 Apr 1, 2019
93e4ad6
Merge branch 'task/g4_check_act_has_all_sosignatories' of github.com:…
rg911 Apr 1, 2019
08c8ce3
Updated license year
rg911 Apr 1, 2019
f32982d
Added #4 check if an aggregate complete transaction has all cosignato…
rg911 Mar 18, 2019
41eb60e
#4
rg911 Apr 1, 2019
478c8a4
Updated license year
rg911 Apr 1, 2019
3b470d1
Merge branch 'task/g4_check_act_has_all_sosignatories' of github.com:…
rg911 Apr 2, 2019
3d1d1f6
Added check in validate cosignatories for
rg911 Apr 3, 2019
7a420e7
Fixed bug in create transaction from payload
rg911 Apr 4, 2019
c37da2d
Added more test cases and fixed a few bugs
rg911 Apr 5, 2019
77b43b3
Merge branch 'master' into task/g4_check_act_has_all_sosignatories
evias Apr 10, 2019
eb2e12f
renamed AggregatedTransactionService
rg911 Apr 10, 2019
5776201
#4
rg911 Apr 1, 2019
90e805f
Updated license year
rg911 Apr 1, 2019
c33a4c3
Added check in validate cosignatories for
rg911 Apr 3, 2019
9c4ae75
Fixed bug in create transaction from payload
rg911 Apr 4, 2019
ffed0a7
Added more test cases and fixed a few bugs
rg911 Apr 5, 2019
461f34b
renamed AggregatedTransactionService
rg911 Apr 10, 2019
91931f0
Merge branch 'task/g4_check_act_has_all_sosignatories' of github.com:…
rg911 Apr 10, 2019
f944d3b
#4
rg911 Apr 1, 2019
516b5e6
Updated license year
rg911 Apr 1, 2019
fb47164
Added check in validate cosignatories for
rg911 Apr 3, 2019
daeb1cb
Fixed bug in create transaction from payload
rg911 Apr 4, 2019
3da7cf7
Added more test cases and fixed a few bugs
rg911 Apr 5, 2019
1cef7f8
renamed AggregatedTransactionService
rg911 Apr 10, 2019
18a5f2a
#4
rg911 Apr 1, 2019
df799f6
Updated license year
rg911 Apr 1, 2019
6a01621
Added check in validate cosignatories for
rg911 Apr 3, 2019
e6136e0
Added more test cases and fixed a few bugs
rg911 Apr 5, 2019
9754295
renamed AggregatedTransactionService
rg911 Apr 10, 2019
a28caf5
Merge branch 'task/g4_check_act_has_all_sosignatories' of github.com:…
rg911 Apr 10, 2019
85439ba
Fixed issue on createTransactionFromJson
rg911 Apr 10, 2019
ab012c8
Merge branch 'master' into task/g4_check_act_has_all_sosignatories
evias Apr 10, 2019
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 7 additions & 7 deletions src/infrastructure/transaction/CreateTransactionFromPayload.ts
Original file line number Diff line number Diff line change
Expand Up @@ -376,8 +376,8 @@ const CreateTransaction = (type: number, transactionData: string, networkType: N
}),
networkType,
consignatureArray ? consignatureArray.map((cosignature) => new AggregateTransactionCosignature(
cosignature.substring(0, 64),
PublicAccount.createFromPublicKey(cosignature.substring(64, 192), networkType),
cosignature.substring(64, 192),
PublicAccount.createFromPublicKey(cosignature.substring(0, 64), networkType),
)) : [],
);
case TransactionType.AGGREGATE_BONDED:
Expand All @@ -400,8 +400,8 @@ const CreateTransaction = (type: number, transactionData: string, networkType: N
}),
networkType,
bondedConsignatureArray ? bondedConsignatureArray.map((cosignature) => new AggregateTransactionCosignature(
cosignature.substring(0, 64),
PublicAccount.createFromPublicKey(cosignature.substring(64, 192), networkType),
cosignature.substring(64, 192),
PublicAccount.createFromPublicKey(cosignature.substring(0, 64), networkType),
)) : [],
);
default:
Expand Down Expand Up @@ -456,10 +456,10 @@ const parseInnerTransactionFromBinary = (innerTransactionBinary: string): string
let innerBinary = innerTransactionBinary;

while (innerBinary.length) {
const payloadSize = parseInt(convert.uint8ToHex(convert.hexToUint8(innerTransactionBinary.substring(0, 8)).reverse()), 16) * 2;
const innerTransaction = innerTransactionBinary.substring(8, 8 + payloadSize);
const payloadSize = parseInt(convert.uint8ToHex(convert.hexToUint8(innerBinary.substring(0, 8)).reverse()), 16) * 2;
const innerTransaction = innerBinary.substring(8, payloadSize);
embeddedTransaction.push(innerTransaction);
innerBinary = innerTransactionBinary.substring(8 + payloadSize);
innerBinary = innerBinary.substring(payloadSize);
}
return embeddedTransaction;
};
Expand Down
2 changes: 1 addition & 1 deletion src/model/transaction/Transaction.ts
Original file line number Diff line number Diff line change
Expand Up @@ -211,7 +211,7 @@ export abstract class Transaction {
const commonTransactionObject = {
type: this.type,
networkType: this.networkType,
version: this.version,
version: this.versionToDTO(),
maxFee: this.maxFee.toDTO(),
deadline: this.deadline.toDTO(),
signature: this.signature ? this.signature : '',
Expand Down
153 changes: 153 additions & 0 deletions src/service/AggregateTransactionService.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,153 @@
/*
* Copyright 2019 NEM
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

import {from as observableFrom , Observable, of as observableOf} from 'rxjs';
import { flatMap, map, mergeMap, toArray} from 'rxjs/operators';
import { TransactionMapping } from '../core/utils/TransactionMapping';
import { AccountHttp } from '../infrastructure/AccountHttp';
import { MultisigAccountGraphInfo } from '../model/account/MultisigAccountGraphInfo';
import { AggregateTransaction } from '../model/transaction/AggregateTransaction';
import { InnerTransaction } from '../model/transaction/InnerTransaction';
import { ModifyMultisigAccountTransaction } from '../model/transaction/ModifyMultisigAccountTransaction';
import { MultisigCosignatoryModificationType } from '../model/transaction/MultisigCosignatoryModificationType';
import { SignedTransaction } from '../model/transaction/SignedTransaction';
import { TransactionType } from '../model/transaction/TransactionType';

/**
* Aggregated Transaction service
*/
export class AggregateTransactionService {

/**
* Constructor
* @param accountHttp
*/
constructor(private readonly accountHttp: AccountHttp) {
}

/**
* Check if an aggregate complete transaction has all cosignatories attached
* @param signedTransaction - The signed aggregate transaction (complete) to be verified
* @returns {Observable<boolean>}
*/
public isComplete(signedTransaction: SignedTransaction): Observable<boolean> {
const aggregateTransaction = TransactionMapping.createFromPayload(signedTransaction.payload) as AggregateTransaction;
/**
* Include both initiator & cosigners
*/
const signers = (aggregateTransaction.cosignatures.map((cosigner) => cosigner.signer.publicKey));
if (signedTransaction.signer) {
signers.push(signedTransaction.signer);
}
return observableFrom(aggregateTransaction.innerTransactions).pipe(
mergeMap((innerTransaction) => this.accountHttp.getMultisigAccountInfo(innerTransaction.signer.address)
.pipe(
/**
* For multisig account, we need to get the graph info in case it has multiple levels
*/
mergeMap((_) => _.minApproval !== 0 && _.minRemoval !== 0 ?
this.accountHttp.getMultisigAccountGraphInfo(_.account.address)
.pipe(
map((graphInfo) => this.validateCosignatories(graphInfo, signers, innerTransaction)),
) : observableOf(signers.find((s) => s === _.account.publicKey ) !== undefined),
),
),
),
toArray(),
).pipe(
flatMap((results) => {
return observableOf(results.every((isComplete) => isComplete));
}),
);
}

/**
* Validate cosignatories against multisig Account(s)
* @param graphInfo - multisig account graph info
* @param cosignatories - array of cosignatories extracted from aggregated transaction
* @param innerTransaction - the inner transaction of the aggregated transaction
* @returns {boolean}
*/
private validateCosignatories(graphInfo: MultisigAccountGraphInfo,
cosignatories: string[],
innerTransaction: InnerTransaction): boolean {
/**
* Validate cosignatories from bottom level to top
*/
const sortedKeys = Array.from(graphInfo.multisigAccounts.keys()).sort((a, b) => b - a);
const cosignatoriesReceived = cosignatories;
let validationResult = false;

let isMultisigRemoval = false;

/**
* Check inner transaction. If remove cosigner from multisig account,
* use minRemoval instead of minApproval for cosignatories validation.
*/
if (innerTransaction.type === TransactionType.MODIFY_MULTISIG_ACCOUNT) {
if ((innerTransaction as ModifyMultisigAccountTransaction).modifications
.find((modification) => modification.type === MultisigCosignatoryModificationType.Remove) !== undefined) {
isMultisigRemoval = true;
}
}

sortedKeys.forEach((key) => {
const multisigInfo = graphInfo.multisigAccounts.get(key);
if (multisigInfo && !validationResult) {
multisigInfo.forEach((multisig) => {
if (multisig.minApproval >= 1 && multisig.minRemoval) { // To make sure it is multisig account
const matchedCosignatories = this.compareArrays(cosignatoriesReceived,
multisig.cosignatories.map((cosig) => cosig.publicKey));

/**
* if minimal signature requirement met at current level, push the multisig account
* into the received signatories array for next level validation.
* Otherwise return validation failed.
*/
if ((matchedCosignatories.length >= multisig.minApproval && !isMultisigRemoval) ||
(matchedCosignatories.length >= multisig.minRemoval && isMultisigRemoval)) {
if (cosignatoriesReceived.indexOf(multisig.account.publicKey) === -1) {
cosignatoriesReceived.push(multisig.account.publicKey);
}
validationResult = true;
} else {
validationResult = false;
}
}
});
}
});

return validationResult;
}

/**
* Compare two string arrays
* @param array1 - base array
* @param array2 - array to be matched
* @returns {string[]} - array of matched elements
*/
private compareArrays(array1: string[], array2: string[]): string[] {
const results: string[] = [];
array1.forEach((a1) => array2.forEach((a2) => {
if (a1 === a2) {
results.push(a1);
}
}));

return results;
}
}
1 change: 1 addition & 0 deletions src/service/service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,3 +16,4 @@

export * from './NamespaceService';
export * from './MosaicService';
export * from './AggregateTransactionService';
Loading