mirror of
https://github.com/IT4Change/gradido.git
synced 2026-02-06 09:56:05 +00:00
test paring transaction validation
This commit is contained in:
parent
8a135bd983
commit
8445c0bec2
323
dlt-connector/src/data/Transaction.logic.test.ts
Normal file
323
dlt-connector/src/data/Transaction.logic.test.ts
Normal file
@ -0,0 +1,323 @@
|
||||
import { Community } from '@entity/Community'
|
||||
import { Transaction } from '@entity/Transaction'
|
||||
import { Decimal } from 'decimal.js-light'
|
||||
|
||||
import { logger } from '@/logging/logger'
|
||||
|
||||
import { CommunityRoot } from './proto/3_3/CommunityRoot'
|
||||
import { CrossGroupType } from './proto/3_3/enum/CrossGroupType'
|
||||
import { GradidoTransfer } from './proto/3_3/GradidoTransfer'
|
||||
import { RegisterAddress } from './proto/3_3/RegisterAddress'
|
||||
import { TransactionBody } from './proto/3_3/TransactionBody'
|
||||
import { TransactionLogic } from './Transaction.logic'
|
||||
import { GradidoCreation } from './proto/3_3/GradidoCreation'
|
||||
import { GradidoDeferredTransfer } from './proto/3_3/GradidoDeferredTransfer'
|
||||
|
||||
let a: Transaction
|
||||
let b: Transaction
|
||||
|
||||
describe('data/transaction.logic', () => {
|
||||
describe('isBelongTogether', () => {
|
||||
beforeEach(() => {
|
||||
const now = new Date()
|
||||
let body = new TransactionBody()
|
||||
body.type = CrossGroupType.OUTBOUND
|
||||
body.transfer = new GradidoTransfer()
|
||||
body.otherGroup = 'recipient group'
|
||||
|
||||
a = new Transaction()
|
||||
a.community = new Community()
|
||||
a.communityId = 1
|
||||
a.otherCommunityId = 2
|
||||
a.id = 1
|
||||
a.signingAccountId = 1
|
||||
a.recipientAccountId = 2
|
||||
a.createdAt = now
|
||||
a.amount = new Decimal('100')
|
||||
a.signature = Buffer.alloc(64)
|
||||
a.bodyBytes = Buffer.from(TransactionBody.encode(body).finish())
|
||||
|
||||
body = new TransactionBody()
|
||||
body.type = CrossGroupType.INBOUND
|
||||
body.transfer = new GradidoTransfer()
|
||||
body.otherGroup = 'sending group'
|
||||
|
||||
b = new Transaction()
|
||||
b.community = new Community()
|
||||
b.communityId = 1
|
||||
b.otherCommunityId = 2
|
||||
b.id = 2
|
||||
b.signingAccountId = 1
|
||||
b.recipientAccountId = 2
|
||||
b.createdAt = now
|
||||
b.amount = new Decimal('100')
|
||||
b.signature = Buffer.alloc(64)
|
||||
b.bodyBytes = Buffer.from(TransactionBody.encode(body).finish())
|
||||
})
|
||||
|
||||
const spy = jest.spyOn(logger, 'info')
|
||||
|
||||
it('true', () => {
|
||||
const logic = new TransactionLogic(a)
|
||||
expect(logic.isBelongTogether(b)).toBe(true)
|
||||
})
|
||||
|
||||
it('false because of same id', () => {
|
||||
b.id = 1
|
||||
const logic = new TransactionLogic(a)
|
||||
expect(logic.isBelongTogether(b)).toBe(false)
|
||||
expect(spy).toHaveBeenLastCalledWith('id is the same, it is the same transaction!')
|
||||
})
|
||||
|
||||
it('false because of different signing accounts', () => {
|
||||
b.signingAccountId = 17
|
||||
const logic = new TransactionLogic(a)
|
||||
expect(logic.isBelongTogether(b)).toBe(false)
|
||||
expect(spy).toHaveBeenLastCalledWith(
|
||||
'transaction a and b are not pairs',
|
||||
expect.objectContaining({}),
|
||||
)
|
||||
})
|
||||
|
||||
it('false because of different recipient accounts', () => {
|
||||
b.recipientAccountId = 21
|
||||
const logic = new TransactionLogic(a)
|
||||
expect(logic.isBelongTogether(b)).toBe(false)
|
||||
expect(spy).toHaveBeenLastCalledWith(
|
||||
'transaction a and b are not pairs',
|
||||
expect.objectContaining({}),
|
||||
)
|
||||
})
|
||||
|
||||
it('false because of different community ids', () => {
|
||||
b.communityId = 6
|
||||
const logic = new TransactionLogic(a)
|
||||
expect(logic.isBelongTogether(b)).toBe(false)
|
||||
expect(spy).toHaveBeenLastCalledWith(
|
||||
'transaction a and b are not pairs',
|
||||
expect.objectContaining({}),
|
||||
)
|
||||
})
|
||||
|
||||
it('false because of different other community ids', () => {
|
||||
b.otherCommunityId = 3
|
||||
const logic = new TransactionLogic(a)
|
||||
expect(logic.isBelongTogether(b)).toBe(false)
|
||||
expect(spy).toHaveBeenLastCalledWith(
|
||||
'transaction a and b are not pairs',
|
||||
expect.objectContaining({}),
|
||||
)
|
||||
})
|
||||
|
||||
it('false because of different createdAt', () => {
|
||||
b.createdAt = new Date('2021-01-01T17:12')
|
||||
const logic = new TransactionLogic(a)
|
||||
expect(logic.isBelongTogether(b)).toBe(false)
|
||||
expect(spy).toHaveBeenLastCalledWith(
|
||||
'transaction a and b are not pairs',
|
||||
expect.objectContaining({}),
|
||||
)
|
||||
})
|
||||
|
||||
describe('false because of mismatching cross group type', () => {
|
||||
const body = new TransactionBody()
|
||||
it('a is LOCAL', () => {
|
||||
body.type = CrossGroupType.LOCAL
|
||||
a.bodyBytes = Buffer.from(TransactionBody.encode(body).finish())
|
||||
const logic = new TransactionLogic(a)
|
||||
expect(logic.isBelongTogether(b)).toBe(false)
|
||||
expect(spy).toHaveBeenNthCalledWith(7, 'no one can be LOCAL')
|
||||
expect(spy).toHaveBeenLastCalledWith(
|
||||
"cross group types don't match",
|
||||
expect.objectContaining({}),
|
||||
)
|
||||
})
|
||||
|
||||
it('b is LOCAL', () => {
|
||||
body.type = CrossGroupType.LOCAL
|
||||
b.bodyBytes = Buffer.from(TransactionBody.encode(body).finish())
|
||||
const logic = new TransactionLogic(a)
|
||||
expect(logic.isBelongTogether(b)).toBe(false)
|
||||
expect(spy).toHaveBeenNthCalledWith(9, 'no one can be LOCAL')
|
||||
expect(spy).toHaveBeenLastCalledWith(
|
||||
"cross group types don't match",
|
||||
expect.objectContaining({}),
|
||||
)
|
||||
})
|
||||
|
||||
it('both are INBOUND', () => {
|
||||
body.type = CrossGroupType.INBOUND
|
||||
a.bodyBytes = Buffer.from(TransactionBody.encode(body).finish())
|
||||
b.bodyBytes = Buffer.from(TransactionBody.encode(body).finish())
|
||||
const logic = new TransactionLogic(a)
|
||||
expect(logic.isBelongTogether(b)).toBe(false)
|
||||
expect(spy).toHaveBeenLastCalledWith(
|
||||
"cross group types don't match",
|
||||
expect.objectContaining({}),
|
||||
)
|
||||
})
|
||||
|
||||
it('both are OUTBOUND', () => {
|
||||
body.type = CrossGroupType.OUTBOUND
|
||||
a.bodyBytes = Buffer.from(TransactionBody.encode(body).finish())
|
||||
b.bodyBytes = Buffer.from(TransactionBody.encode(body).finish())
|
||||
const logic = new TransactionLogic(a)
|
||||
expect(logic.isBelongTogether(b)).toBe(false)
|
||||
expect(spy).toHaveBeenLastCalledWith(
|
||||
"cross group types don't match",
|
||||
expect.objectContaining({}),
|
||||
)
|
||||
})
|
||||
|
||||
it('a is CROSS', () => {
|
||||
body.type = CrossGroupType.CROSS
|
||||
a.bodyBytes = Buffer.from(TransactionBody.encode(body).finish())
|
||||
const logic = new TransactionLogic(a)
|
||||
expect(logic.isBelongTogether(b)).toBe(false)
|
||||
expect(spy).toHaveBeenLastCalledWith(
|
||||
"cross group types don't match",
|
||||
expect.objectContaining({}),
|
||||
)
|
||||
})
|
||||
|
||||
it('b is CROSS', () => {
|
||||
body.type = CrossGroupType.CROSS
|
||||
b.bodyBytes = Buffer.from(TransactionBody.encode(body).finish())
|
||||
const logic = new TransactionLogic(a)
|
||||
expect(logic.isBelongTogether(b)).toBe(false)
|
||||
expect(spy).toHaveBeenLastCalledWith(
|
||||
"cross group types don't match",
|
||||
expect.objectContaining({}),
|
||||
)
|
||||
})
|
||||
|
||||
it('true with a as INBOUND and b as OUTBOUND', () => {
|
||||
let body = TransactionBody.fromBodyBytes(a.bodyBytes)
|
||||
body.type = CrossGroupType.INBOUND
|
||||
a.bodyBytes = Buffer.from(TransactionBody.encode(body).finish())
|
||||
body = TransactionBody.fromBodyBytes(b.bodyBytes)
|
||||
body.type = CrossGroupType.OUTBOUND
|
||||
b.bodyBytes = Buffer.from(TransactionBody.encode(body).finish())
|
||||
const logic = new TransactionLogic(a)
|
||||
expect(logic.isBelongTogether(b)).toBe(true)
|
||||
})
|
||||
})
|
||||
|
||||
describe('false because of transaction type not suitable for cross group transactions', () => {
|
||||
const body = new TransactionBody()
|
||||
body.type = CrossGroupType.OUTBOUND
|
||||
it('without transaction type (broken TransactionBody)', () => {
|
||||
a.bodyBytes = Buffer.from(TransactionBody.encode(body).finish())
|
||||
const logic = new TransactionLogic(a)
|
||||
expect(() => logic.isBelongTogether(b)).toThrowError("couldn't determine transaction type")
|
||||
})
|
||||
|
||||
it('not the same transaction types', () => {
|
||||
const body = new TransactionBody()
|
||||
body.type = CrossGroupType.OUTBOUND
|
||||
body.registerAddress = new RegisterAddress()
|
||||
a.bodyBytes = Buffer.from(TransactionBody.encode(body).finish())
|
||||
const logic = new TransactionLogic(a)
|
||||
expect(logic.isBelongTogether(b)).toBe(false)
|
||||
expect(spy).toHaveBeenLastCalledWith(
|
||||
"transaction types don't match",
|
||||
expect.objectContaining({}),
|
||||
)
|
||||
})
|
||||
|
||||
it('community root cannot be a cross group transaction', () => {
|
||||
let body = new TransactionBody()
|
||||
body.type = CrossGroupType.OUTBOUND
|
||||
body.communityRoot = new CommunityRoot()
|
||||
a.bodyBytes = Buffer.from(TransactionBody.encode(body).finish())
|
||||
body = new TransactionBody()
|
||||
body.type = CrossGroupType.INBOUND
|
||||
body.communityRoot = new CommunityRoot()
|
||||
b.bodyBytes = Buffer.from(TransactionBody.encode(body).finish())
|
||||
const logic = new TransactionLogic(a)
|
||||
expect(logic.isBelongTogether(b)).toBe(false)
|
||||
expect(spy).toHaveBeenLastCalledWith(
|
||||
"TransactionType COMMUNITY_ROOT couldn't be a CrossGroup Transaction",
|
||||
)
|
||||
})
|
||||
|
||||
it('Gradido Creation cannot be a cross group transaction', () => {
|
||||
let body = new TransactionBody()
|
||||
body.type = CrossGroupType.OUTBOUND
|
||||
body.creation = new GradidoCreation()
|
||||
a.bodyBytes = Buffer.from(TransactionBody.encode(body).finish())
|
||||
body = new TransactionBody()
|
||||
body.type = CrossGroupType.INBOUND
|
||||
body.creation = new GradidoCreation()
|
||||
b.bodyBytes = Buffer.from(TransactionBody.encode(body).finish())
|
||||
const logic = new TransactionLogic(a)
|
||||
expect(logic.isBelongTogether(b)).toBe(false)
|
||||
expect(spy).toHaveBeenLastCalledWith(
|
||||
"TransactionType GRADIDO_CREATION couldn't be a CrossGroup Transaction",
|
||||
)
|
||||
})
|
||||
|
||||
it('Deferred Transfer cannot be a cross group transaction', () => {
|
||||
let body = new TransactionBody()
|
||||
body.type = CrossGroupType.OUTBOUND
|
||||
body.deferredTransfer = new GradidoDeferredTransfer()
|
||||
a.bodyBytes = Buffer.from(TransactionBody.encode(body).finish())
|
||||
body = new TransactionBody()
|
||||
body.type = CrossGroupType.INBOUND
|
||||
body.deferredTransfer = new GradidoDeferredTransfer()
|
||||
b.bodyBytes = Buffer.from(TransactionBody.encode(body).finish())
|
||||
const logic = new TransactionLogic(a)
|
||||
expect(logic.isBelongTogether(b)).toBe(false)
|
||||
expect(spy).toHaveBeenLastCalledWith(
|
||||
"TransactionType GRADIDO_DEFERRED_TRANSFER couldn't be a CrossGroup Transaction",
|
||||
)
|
||||
})
|
||||
})
|
||||
|
||||
describe('false because of wrong amount', () => {
|
||||
it('amount missing on a', () => {
|
||||
a.amount = undefined
|
||||
const logic = new TransactionLogic(a)
|
||||
expect(logic.isBelongTogether(b)).toBe(false)
|
||||
expect(spy).toHaveBeenLastCalledWith('missing amount')
|
||||
})
|
||||
|
||||
it('amount missing on b', () => {
|
||||
b.amount = undefined
|
||||
const logic = new TransactionLogic(a)
|
||||
expect(logic.isBelongTogether(b)).toBe(false)
|
||||
expect(spy).toHaveBeenLastCalledWith('missing amount')
|
||||
})
|
||||
|
||||
it('amount not the same', () => {
|
||||
a.amount = new Decimal('101')
|
||||
const logic = new TransactionLogic(a)
|
||||
expect(logic.isBelongTogether(b)).toBe(false)
|
||||
expect(spy).toHaveBeenLastCalledWith('amounts mismatch', expect.objectContaining({}))
|
||||
})
|
||||
})
|
||||
|
||||
it('false because otherGroup are the same', () => {
|
||||
const body = new TransactionBody()
|
||||
body.type = CrossGroupType.OUTBOUND
|
||||
body.transfer = new GradidoTransfer()
|
||||
body.otherGroup = 'sending group'
|
||||
a.bodyBytes = Buffer.from(TransactionBody.encode(body).finish())
|
||||
const logic = new TransactionLogic(a)
|
||||
expect(logic.isBelongTogether(b)).toBe(false)
|
||||
expect(spy).toHaveBeenLastCalledWith('otherGroups are the same', expect.objectContaining({}))
|
||||
})
|
||||
|
||||
it('false because of different memos', () => {
|
||||
const body = new TransactionBody()
|
||||
body.type = CrossGroupType.OUTBOUND
|
||||
body.transfer = new GradidoTransfer()
|
||||
body.otherGroup = 'recipient group'
|
||||
body.memo = 'changed memo'
|
||||
a.bodyBytes = Buffer.from(TransactionBody.encode(body).finish())
|
||||
const logic = new TransactionLogic(a)
|
||||
expect(logic.isBelongTogether(b)).toBe(false)
|
||||
expect(spy).toHaveBeenLastCalledWith('memo differ', expect.objectContaining({}))
|
||||
})
|
||||
})
|
||||
})
|
||||
@ -83,30 +83,26 @@ export class TransactionLogic {
|
||||
this.self.recipientAccountId !== otherTransaction.recipientAccountId ||
|
||||
this.self.communityId !== otherTransaction.communityId ||
|
||||
this.self.otherCommunityId !== otherTransaction.otherCommunityId ||
|
||||
this.self.accountBalanceOnCreation !== otherTransaction.accountBalanceOnCreation ||
|
||||
this.self.createdAt.getTime() !== otherTransaction.createdAt.getTime()
|
||||
) {
|
||||
logger.debug('transaction a and b are not pairs', {
|
||||
a: new TransactionLoggingView(this.self),
|
||||
b: new TransactionLoggingView(otherTransaction),
|
||||
logger.info('transaction a and b are not pairs', {
|
||||
a: new TransactionLoggingView(this.self).toJSON(),
|
||||
b: new TransactionLoggingView(otherTransaction).toJSON(),
|
||||
})
|
||||
return false
|
||||
}
|
||||
const body = this.getBody()
|
||||
const otherBody = TransactionBody.fromBodyBytes(otherTransaction.bodyBytes)
|
||||
/**
|
||||
* both can be Cross
|
||||
* both must be Cross or
|
||||
* one can be OUTBOUND and one can be INBOUND
|
||||
* no one can be LOCAL
|
||||
*/
|
||||
if (
|
||||
(body.type === otherBody.type && body.type !== CrossGroupType.CROSS) ||
|
||||
body.type === CrossGroupType.LOCAL ||
|
||||
otherBody.type === CrossGroupType.LOCAL
|
||||
) {
|
||||
|
||||
if (!this.validCrossGroupTypes(body.type, otherBody.type)) {
|
||||
logger.info("cross group types don't match", {
|
||||
a: new TransactionBodyLoggingView(body),
|
||||
b: new TransactionBodyLoggingView(otherBody),
|
||||
a: new TransactionBodyLoggingView(body).toJSON(),
|
||||
b: new TransactionBodyLoggingView(otherBody).toJSON(),
|
||||
})
|
||||
return false
|
||||
}
|
||||
@ -114,14 +110,14 @@ export class TransactionLogic {
|
||||
const otherType = otherBody.getTransactionType()
|
||||
if (!type || !otherType) {
|
||||
throw new LogError("couldn't determine transaction type", {
|
||||
a: new TransactionBodyLoggingView(body),
|
||||
b: new TransactionBodyLoggingView(otherBody),
|
||||
a: new TransactionBodyLoggingView(body).toJSON(),
|
||||
b: new TransactionBodyLoggingView(otherBody).toJSON(),
|
||||
})
|
||||
}
|
||||
if (type !== otherType) {
|
||||
logger.info("transaction types don't match", {
|
||||
a: new TransactionBodyLoggingView(body),
|
||||
b: new TransactionBodyLoggingView(otherBody),
|
||||
a: new TransactionBodyLoggingView(body).toJSON(),
|
||||
b: new TransactionBodyLoggingView(otherBody).toJSON(),
|
||||
})
|
||||
return false
|
||||
}
|
||||
@ -132,7 +128,7 @@ export class TransactionLogic {
|
||||
TransactionType.GRADIDO_DEFERRED_TRANSFER,
|
||||
].includes(type)
|
||||
) {
|
||||
logger.info(`TransactionType ${type} couldn't be a CrossGroup Transaction`)
|
||||
logger.info(`TransactionType ${TransactionType[type]} couldn't be a CrossGroup Transaction`)
|
||||
return false
|
||||
}
|
||||
if (
|
||||
@ -156,21 +152,45 @@ export class TransactionLogic {
|
||||
}
|
||||
if (body.otherGroup === otherBody.otherGroup) {
|
||||
logger.info('otherGroups are the same', {
|
||||
a: new TransactionBodyLoggingView(body),
|
||||
b: new TransactionBodyLoggingView(otherBody),
|
||||
a: new TransactionBodyLoggingView(body).toJSON(),
|
||||
b: new TransactionBodyLoggingView(otherBody).toJSON(),
|
||||
})
|
||||
return false
|
||||
}
|
||||
if (body.memo !== otherBody.memo) {
|
||||
logger.info('memo differ', {
|
||||
a: new TransactionBodyLoggingView(body),
|
||||
b: new TransactionBodyLoggingView(otherBody),
|
||||
a: new TransactionBodyLoggingView(body).toJSON(),
|
||||
b: new TransactionBodyLoggingView(otherBody).toJSON(),
|
||||
})
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
/**
|
||||
* both must be CROSS or
|
||||
* one can be OUTBOUND and one can be INBOUND
|
||||
* no one can be LOCAL
|
||||
* @return true if crossGroupTypes are valid
|
||||
*/
|
||||
protected validCrossGroupTypes(a: CrossGroupType, b: CrossGroupType): boolean {
|
||||
logger.debug('compare ', {
|
||||
a: CrossGroupType[a],
|
||||
b: CrossGroupType[b],
|
||||
})
|
||||
if (a === CrossGroupType.LOCAL || b === CrossGroupType.LOCAL) {
|
||||
logger.info('no one can be LOCAL')
|
||||
return false
|
||||
}
|
||||
if (
|
||||
(a === CrossGroupType.INBOUND && b === CrossGroupType.OUTBOUND) ||
|
||||
(a === CrossGroupType.OUTBOUND && b === CrossGroupType.INBOUND)
|
||||
) {
|
||||
return true // One can be INBOUND and one can be OUTBOUND
|
||||
}
|
||||
return a === CrossGroupType.CROSS && b === CrossGroupType.CROSS
|
||||
}
|
||||
|
||||
public getBody(): TransactionBody {
|
||||
if (!this.transactionBody) {
|
||||
this.transactionBody = TransactionBody.fromBodyBytes(this.self.bodyBytes)
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user