mirror of
https://github.com/IT4Change/gradido.git
synced 2026-02-06 09:56:05 +00:00
add lint rule for sorted import like in backend, sort imports, refactor enums, remove KeyManager
This commit is contained in:
parent
7add77903a
commit
0567a3ddf0
@ -77,30 +77,30 @@ module.exports = {
|
||||
// 'import/no-named-default': 'error',
|
||||
// 'import/no-namespace': 'error',
|
||||
// 'import/no-unassigned-import': 'error',
|
||||
// 'import/order': [
|
||||
// 'error',
|
||||
// {
|
||||
// groups: ['builtin', 'external', 'internal', 'parent', 'sibling', 'index', 'object', 'type'],
|
||||
// 'newlines-between': 'always',
|
||||
// pathGroups: [
|
||||
// {
|
||||
// pattern: '@?*/**',
|
||||
// group: 'external',
|
||||
// position: 'after',
|
||||
// },
|
||||
// {
|
||||
// pattern: '@/**',
|
||||
// group: 'external',
|
||||
// position: 'after',
|
||||
// },
|
||||
// ],
|
||||
// alphabetize: {
|
||||
// order: 'asc' /* sort in ascending order. Options: ['ignore', 'asc', 'desc'] */,
|
||||
// caseInsensitive: true /* ignore case. Options: [true, false] */,
|
||||
// },
|
||||
// distinctGroup: true,
|
||||
// },
|
||||
// ],
|
||||
'import/order': [
|
||||
'error',
|
||||
{
|
||||
groups: ['builtin', 'external', 'internal', 'parent', 'sibling', 'index', 'object', 'type'],
|
||||
'newlines-between': 'always',
|
||||
pathGroups: [
|
||||
{
|
||||
pattern: '@?*/**',
|
||||
group: 'external',
|
||||
position: 'after',
|
||||
},
|
||||
{
|
||||
pattern: '@/**',
|
||||
group: 'external',
|
||||
position: 'after',
|
||||
},
|
||||
],
|
||||
alphabetize: {
|
||||
order: 'asc' /* sort in ascending order. Options: ['ignore', 'asc', 'desc'] */,
|
||||
caseInsensitive: true /* ignore case. Options: [true, false] */,
|
||||
},
|
||||
distinctGroup: true,
|
||||
},
|
||||
],
|
||||
// 'import/prefer-default-export': 'off',
|
||||
// n
|
||||
'n/handle-callback-err': 'error',
|
||||
|
||||
@ -1,10 +1,10 @@
|
||||
import { KeyManager } from '@/manager/KeyManager'
|
||||
import { KeyPair } from '@/data/KeyPair'
|
||||
import { AddressType } from '@/data/proto/3_3/enum/AddressType'
|
||||
import { hardenDerivationIndex } from '@/utils/derivationHelper'
|
||||
import { Account } from '@entity/Account'
|
||||
import Decimal from 'decimal.js-light'
|
||||
|
||||
import { KeyPair } from '@/data/KeyPair'
|
||||
import { AddressType } from '@/data/proto/3_3/enum/AddressType'
|
||||
import { UserAccountDraft } from '@/graphql/input/UserAccountDraft'
|
||||
import { hardenDerivationIndex } from '@/utils/derivationHelper'
|
||||
import { accountTypeToAddressType } from '@/utils/typeConverter'
|
||||
|
||||
const GMW_ACCOUNT_DERIVATION_INDEX = 1
|
||||
@ -15,14 +15,11 @@ export class AccountFactory {
|
||||
createdAt: Date,
|
||||
derivationIndex: number,
|
||||
type: AddressType,
|
||||
parentKeyPair?: KeyPair,
|
||||
parentKeyPair: KeyPair,
|
||||
): Account {
|
||||
const account = Account.create()
|
||||
account.derivationIndex = derivationIndex
|
||||
account.derive2Pubkey = KeyManager.getInstance().derive(
|
||||
[derivationIndex],
|
||||
parentKeyPair,
|
||||
).publicKey
|
||||
account.derive2Pubkey = parentKeyPair.derive([derivationIndex]).publicKey
|
||||
account.type = type.valueOf()
|
||||
account.createdAt = createdAt
|
||||
account.balanceConfirmedAt = new Decimal(0)
|
||||
@ -33,7 +30,7 @@ export class AccountFactory {
|
||||
|
||||
public static createAccountFromUserAccountDraft(
|
||||
{ createdAt, accountType, user }: UserAccountDraft,
|
||||
parentKeyPair?: KeyPair,
|
||||
parentKeyPair: KeyPair,
|
||||
): Account {
|
||||
return AccountFactory.createAccount(
|
||||
new Date(createdAt),
|
||||
|
||||
@ -1,9 +1,10 @@
|
||||
import { UserIdentifier } from '@/graphql/input/UserIdentifier'
|
||||
import { getDataSource } from '@/typeorm/DataSource'
|
||||
import { Account } from '@entity/Account'
|
||||
import { User } from '@entity/User'
|
||||
import { In } from 'typeorm'
|
||||
|
||||
import { UserIdentifier } from '@/graphql/input/UserIdentifier'
|
||||
import { getDataSource } from '@/typeorm/DataSource'
|
||||
|
||||
export const AccountRepository = getDataSource()
|
||||
.getRepository(Account)
|
||||
.extend({
|
||||
|
||||
@ -1,13 +1,17 @@
|
||||
import 'reflect-metadata'
|
||||
import { TestDB } from '@test/TestDB'
|
||||
import { AccountFactory } from './Account.factory'
|
||||
import { AddressType } from './proto/3_3/enum/AddressType'
|
||||
import { generateKeyPair, generateMnemonic } from '@/utils/cryptoHelper'
|
||||
import { Decimal } from 'decimal.js-light'
|
||||
import { UserAccountDraft } from '@/graphql/input/UserAccountDraft'
|
||||
|
||||
import { TestDB } from '@test/TestDB'
|
||||
|
||||
import { AccountType } from '@/graphql/enum/AccountType'
|
||||
import { UserAccountDraft } from '@/graphql/input/UserAccountDraft'
|
||||
import { UserIdentifier } from '@/graphql/input/UserIdentifier'
|
||||
|
||||
import { AccountFactory } from './Account.factory'
|
||||
import { AccountRepository } from './Account.repository'
|
||||
import { KeyPair } from './KeyPair'
|
||||
import { Mnemonic } from './Mnemonic'
|
||||
import { AddressType } from './proto/3_3/enum/AddressType'
|
||||
import { UserFactory } from './User.factory'
|
||||
import { UserLogic } from './User.logic'
|
||||
|
||||
@ -19,9 +23,9 @@ jest.mock('@typeorm/DataSource', () => ({
|
||||
|
||||
describe('data/Account test factory and repository', () => {
|
||||
const now = new Date()
|
||||
const keyPair1 = generateKeyPair(generateMnemonic('62ef251edc2416f162cd24ab1711982b'))
|
||||
const keyPair2 = generateKeyPair(generateMnemonic('000a0000000002000000000003000070'))
|
||||
const keyPair3 = generateKeyPair(generateMnemonic('00ba541a1000020000000000300bda70'))
|
||||
const keyPair1 = new KeyPair(new Mnemonic('62ef251edc2416f162cd24ab1711982b'))
|
||||
const keyPair2 = new KeyPair(new Mnemonic('000a0000000002000000000003000070'))
|
||||
const keyPair3 = new KeyPair(new Mnemonic('00ba541a1000020000000000300bda70'))
|
||||
const userGradidoID = '6be949ab-8198-4acf-ba63-740089081d61'
|
||||
|
||||
describe('test factory methods', () => {
|
||||
@ -36,6 +40,10 @@ describe('data/Account test factory and repository', () => {
|
||||
const account = AccountFactory.createAccount(now, 1, AddressType.COMMUNITY_HUMAN, keyPair1)
|
||||
expect(account).toMatchObject({
|
||||
derivationIndex: 1,
|
||||
derive2Pubkey: Buffer.from(
|
||||
'cb88043ef4833afc01d6ed9b34e1aa48e79dce5ff97c07090c6600ec05f6d994',
|
||||
'hex',
|
||||
),
|
||||
type: AddressType.COMMUNITY_HUMAN,
|
||||
createdAt: now,
|
||||
balanceCreatedAtDate: now,
|
||||
@ -53,6 +61,10 @@ describe('data/Account test factory and repository', () => {
|
||||
const account = AccountFactory.createAccountFromUserAccountDraft(userAccountDraft, keyPair1)
|
||||
expect(account).toMatchObject({
|
||||
derivationIndex: 1,
|
||||
derive2Pubkey: Buffer.from(
|
||||
'cb88043ef4833afc01d6ed9b34e1aa48e79dce5ff97c07090c6600ec05f6d994',
|
||||
'hex',
|
||||
),
|
||||
type: AddressType.COMMUNITY_HUMAN,
|
||||
createdAt: now,
|
||||
balanceCreatedAtDate: now,
|
||||
@ -65,6 +77,10 @@ describe('data/Account test factory and repository', () => {
|
||||
const account = AccountFactory.createGmwAccount(keyPair1, now)
|
||||
expect(account).toMatchObject({
|
||||
derivationIndex: 2147483649,
|
||||
derive2Pubkey: Buffer.from(
|
||||
'05f0060357bb73bd290283870fc47a10b3764f02ca26938479ed853f46145366',
|
||||
'hex',
|
||||
),
|
||||
type: AddressType.COMMUNITY_GMW,
|
||||
createdAt: now,
|
||||
balanceCreatedAtDate: now,
|
||||
@ -77,6 +93,10 @@ describe('data/Account test factory and repository', () => {
|
||||
const account = AccountFactory.createAufAccount(keyPair1, now)
|
||||
expect(account).toMatchObject({
|
||||
derivationIndex: 2147483650,
|
||||
derive2Pubkey: Buffer.from(
|
||||
'6c749f8693a4a58c948e5ae54df11e2db33d2f98673b56e0cf19c0132614ab59',
|
||||
'hex',
|
||||
),
|
||||
type: AddressType.COMMUNITY_AUF,
|
||||
createdAt: now,
|
||||
balanceCreatedAtDate: now,
|
||||
@ -89,7 +109,6 @@ describe('data/Account test factory and repository', () => {
|
||||
describe('test repository functions', () => {
|
||||
beforeAll(async () => {
|
||||
await con.setupTestDB()
|
||||
|
||||
await Promise.all([
|
||||
AccountFactory.createAufAccount(keyPair1, now).save(),
|
||||
AccountFactory.createGmwAccount(keyPair1, now).save(),
|
||||
|
||||
@ -1,14 +1,16 @@
|
||||
import { Community } from '@entity/Community'
|
||||
import { FindOptionsSelect, In, IsNull, Not } from 'typeorm'
|
||||
|
||||
import { CommunityArg } from '@/graphql/arg/CommunityArg'
|
||||
import { TransactionErrorType } from '@/graphql/enum/TransactionErrorType'
|
||||
import { CommunityDraft } from '@/graphql/input/CommunityDraft'
|
||||
import { UserIdentifier } from '@/graphql/input/UserIdentifier'
|
||||
import { TransactionError } from '@/graphql/model/TransactionError'
|
||||
import { LogError } from '@/server/LogError'
|
||||
import { getDataSource } from '@/typeorm/DataSource'
|
||||
import { iotaTopicFromCommunityUUID } from '@/utils/typeConverter'
|
||||
import { Community } from '@entity/Community'
|
||||
import { FindOptionsSelect, In, IsNull, Not } from 'typeorm'
|
||||
|
||||
import { KeyPair } from './KeyPair'
|
||||
import { LogError } from '@/server/LogError'
|
||||
|
||||
export const CommunityRepository = getDataSource()
|
||||
.getRepository(Community)
|
||||
|
||||
@ -1,38 +1,87 @@
|
||||
// https://www.npmjs.com/package/bip32-ed25519?activeTab=code
|
||||
import { toPublic } from 'bip32-ed25519'
|
||||
import { Community } from '@entity/Community'
|
||||
|
||||
// https://www.npmjs.com/package/bip32-ed25519
|
||||
import { LogError } from '@/server/LogError'
|
||||
|
||||
import { toPublic, derivePrivate, sign, verify, generateFromSeed } from 'bip32-ed25519'
|
||||
|
||||
import { Mnemonic } from './Mnemonic'
|
||||
|
||||
/**
|
||||
* Class Managing Key Pair and also generate, sign and verify signature with it
|
||||
*/
|
||||
export class KeyPair {
|
||||
private _publicKey: Buffer
|
||||
private _chainCode: Buffer
|
||||
private _privateKey: Buffer
|
||||
|
||||
/**
|
||||
* @param input: Buffer = extended private key, returned from bip32-ed25519 generateFromSeed
|
||||
* @param input: Mnemonic = Mnemonic or Passphrase which work as seed for generating algorithms
|
||||
* @param input: Buffer = extended private key, returned from bip32-ed25519 generateFromSeed or from derivePrivate
|
||||
* @param input: Community = community entity with keys loaded from db
|
||||
*
|
||||
*/
|
||||
public constructor(input: Buffer | Community) {
|
||||
if (input instanceof Buffer) {
|
||||
this.privateKey = input.subarray(0, 64)
|
||||
this.chainCode = input.subarray(64, 96)
|
||||
this.publicKey = toPublic(input).subarray(0, 32)
|
||||
public constructor(input: Mnemonic | Buffer | Community) {
|
||||
if (input instanceof Mnemonic) {
|
||||
this.loadFromExtendedPrivateKey(generateFromSeed(input.seed))
|
||||
} else if (input instanceof Buffer) {
|
||||
this.loadFromExtendedPrivateKey(input)
|
||||
} else if (input instanceof Community) {
|
||||
if (!input.rootPrivkey || !input.rootChaincode || !input.rootPubkey) {
|
||||
throw new LogError('missing private key or chaincode or public key in commmunity entity')
|
||||
}
|
||||
this.privateKey = input.rootPrivkey
|
||||
this.publicKey = input.rootPubkey
|
||||
this.chainCode = input.rootChaincode
|
||||
this._privateKey = input.rootPrivkey
|
||||
this._publicKey = input.rootPubkey
|
||||
this._chainCode = input.rootChaincode
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* copy keys to community entity
|
||||
* @param community
|
||||
*/
|
||||
public fillInCommunityKeys(community: Community) {
|
||||
community.rootPubkey = this._publicKey
|
||||
community.rootPrivkey = this._privateKey
|
||||
community.rootChaincode = this._chainCode
|
||||
}
|
||||
|
||||
private loadFromExtendedPrivateKey(extendedPrivateKey: Buffer) {
|
||||
if (extendedPrivateKey.length !== 96) {
|
||||
throw new LogError('invalid extended private key')
|
||||
}
|
||||
this._privateKey = extendedPrivateKey.subarray(0, 64)
|
||||
this._chainCode = extendedPrivateKey.subarray(64, 96)
|
||||
this._publicKey = toPublic(extendedPrivateKey).subarray(0, 32)
|
||||
}
|
||||
|
||||
public getExtendPrivateKey(): Buffer {
|
||||
return Buffer.concat([this.privateKey, this.chainCode])
|
||||
return Buffer.concat([this._privateKey, this._chainCode])
|
||||
}
|
||||
|
||||
public getExtendPublicKey(): Buffer {
|
||||
return Buffer.concat([this.publicKey, this.chainCode])
|
||||
return Buffer.concat([this._publicKey, this._chainCode])
|
||||
}
|
||||
|
||||
publicKey: Buffer
|
||||
chainCode: Buffer
|
||||
privateKey: Buffer
|
||||
public get publicKey(): Buffer {
|
||||
return this._publicKey
|
||||
}
|
||||
|
||||
public derive(path: number[]): KeyPair {
|
||||
const extendedPrivateKey = this.getExtendPrivateKey()
|
||||
return new KeyPair(
|
||||
path.reduce(
|
||||
(extendPrivateKey: Buffer, node: number) => derivePrivate(extendPrivateKey, node),
|
||||
extendedPrivateKey,
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
public sign(message: Buffer): Buffer {
|
||||
return sign(message, this.getExtendPrivateKey())
|
||||
}
|
||||
|
||||
public verify(message: Buffer, signature: Buffer): boolean {
|
||||
return verify(message, signature, this.getExtendPublicKey())
|
||||
}
|
||||
}
|
||||
|
||||
25
dlt-connector/src/data/Mnemonic.ts
Normal file
25
dlt-connector/src/data/Mnemonic.ts
Normal file
@ -0,0 +1,25 @@
|
||||
// https://www.npmjs.com/package/bip39
|
||||
import { entropyToMnemonic, mnemonicToSeedSync } from 'bip39'
|
||||
// eslint-disable-next-line camelcase
|
||||
import { randombytes_buf } from 'sodium-native'
|
||||
|
||||
export class Mnemonic {
|
||||
private _passphrase = ''
|
||||
public constructor(seed?: Buffer | string) {
|
||||
if (seed) {
|
||||
this._passphrase = entropyToMnemonic(seed)
|
||||
return
|
||||
}
|
||||
const entropy = Buffer.alloc(256)
|
||||
randombytes_buf(entropy)
|
||||
this._passphrase = entropyToMnemonic(entropy)
|
||||
}
|
||||
|
||||
public get passphrase(): string {
|
||||
return this._passphrase
|
||||
}
|
||||
|
||||
public get seed(): Buffer {
|
||||
return mnemonicToSeedSync(this._passphrase)
|
||||
}
|
||||
}
|
||||
@ -1,13 +1,15 @@
|
||||
import { GradidoTransaction } from '@/data/proto/3_3/GradidoTransaction'
|
||||
import { TransactionBody } from '@/data/proto/3_3/TransactionBody'
|
||||
import { bodyBytesToTransactionBody, transactionBodyToBodyBytes } from '@/utils/typeConverter'
|
||||
import { Transaction } from '@entity/Transaction'
|
||||
import { AccountRepository } from './Account.repository'
|
||||
import { UserIdentifier } from '@/graphql/input/UserIdentifier'
|
||||
import { CommunityRepository } from './Community.repository'
|
||||
import { LogError } from '@/server/LogError'
|
||||
import { Account } from '@entity/Account'
|
||||
import { Community } from '@entity/Community'
|
||||
import { Transaction } from '@entity/Transaction'
|
||||
|
||||
import { GradidoTransaction } from '@/data/proto/3_3/GradidoTransaction'
|
||||
import { TransactionBody } from '@/data/proto/3_3/TransactionBody'
|
||||
import { UserIdentifier } from '@/graphql/input/UserIdentifier'
|
||||
import { LogError } from '@/server/LogError'
|
||||
import { bodyBytesToTransactionBody, transactionBodyToBodyBytes } from '@/utils/typeConverter'
|
||||
|
||||
import { AccountRepository } from './Account.repository'
|
||||
import { CommunityRepository } from './Community.repository'
|
||||
import { TransactionBodyBuilder } from './proto/TransactionBody.builder'
|
||||
|
||||
export class TransactionBuilder {
|
||||
|
||||
@ -1,7 +1,8 @@
|
||||
import { getDataSource } from '@/typeorm/DataSource'
|
||||
import { Transaction } from '@entity/Transaction'
|
||||
import { IsNull } from 'typeorm'
|
||||
|
||||
import { getDataSource } from '@/typeorm/DataSource'
|
||||
|
||||
// https://www.artima.com/articles/the-dci-architecture-a-new-vision-of-object-oriented-programming
|
||||
export const TransactionRepository = getDataSource()
|
||||
.getRepository(Transaction)
|
||||
|
||||
@ -1,10 +1,12 @@
|
||||
import { UserAccountDraft } from '@/graphql/input/UserAccountDraft'
|
||||
import { User } from '@entity/User'
|
||||
import { UserLogic } from './User.logic'
|
||||
|
||||
import { UserAccountDraft } from '@/graphql/input/UserAccountDraft'
|
||||
|
||||
import { KeyPair } from './KeyPair'
|
||||
import { UserLogic } from './User.logic'
|
||||
|
||||
export class UserFactory {
|
||||
static create(userAccountDraft: UserAccountDraft, parentKeys?: KeyPair): User {
|
||||
static create(userAccountDraft: UserAccountDraft, parentKeys: KeyPair): User {
|
||||
const user = User.create()
|
||||
user.createdAt = new Date(userAccountDraft.createdAt)
|
||||
user.gradidoID = userAccountDraft.user.uuid
|
||||
|
||||
@ -1,9 +1,10 @@
|
||||
import { User } from '@entity/User'
|
||||
import { KeyPair } from './KeyPair'
|
||||
|
||||
import { LogError } from '@/server/LogError'
|
||||
import { uuid4ToBuffer } from '@/utils/typeConverter'
|
||||
import { hardenDerivationIndex } from '@/utils/derivationHelper'
|
||||
import { KeyManager } from '@/manager/KeyManager'
|
||||
import { uuid4ToBuffer } from '@/utils/typeConverter'
|
||||
|
||||
import { KeyPair } from './KeyPair'
|
||||
|
||||
export class UserLogic {
|
||||
// eslint-disable-next-line no-useless-constructor
|
||||
@ -15,7 +16,7 @@ export class UserLogic {
|
||||
* @returns
|
||||
*/
|
||||
|
||||
calculateKeyPair = (parentKeys?: KeyPair): KeyPair => {
|
||||
calculateKeyPair = (parentKeys: KeyPair): KeyPair => {
|
||||
if (!this.user.gradidoID) {
|
||||
throw new LogError('missing GradidoID for user.', { id: this.user.id })
|
||||
}
|
||||
@ -27,7 +28,7 @@ export class UserLogic {
|
||||
parts[i] = hardenDerivationIndex(wholeHex.subarray(i * 4, (i + 1) * 4).readUInt32BE())
|
||||
}
|
||||
// parts: [2206563009, 2629978174, 2324817329, 2405141782]
|
||||
const keyPair = KeyManager.getInstance().derive(parts, parentKeys)
|
||||
const keyPair = parentKeys.derive(parts)
|
||||
if (this.user.derive1Pubkey && this.user.derive1Pubkey.compare(keyPair.publicKey) !== 0) {
|
||||
throw new LogError(
|
||||
'The freshly derived public key does not correspond to the stored public key',
|
||||
|
||||
@ -1,8 +1,9 @@
|
||||
import { UserIdentifier } from '@/graphql/input/UserIdentifier'
|
||||
import { getDataSource } from '@/typeorm/DataSource'
|
||||
import { Account } from '@entity/Account'
|
||||
import { User } from '@entity/User'
|
||||
|
||||
import { UserIdentifier } from '@/graphql/input/UserIdentifier'
|
||||
import { getDataSource } from '@/typeorm/DataSource'
|
||||
|
||||
export const UserRepository = getDataSource()
|
||||
.getRepository(User)
|
||||
.extend({
|
||||
|
||||
@ -1,8 +1,8 @@
|
||||
import { AbstractTransaction } from '../AbstractTransaction'
|
||||
import { TransactionValidationLevel } from '@/graphql/enum/TransactionValidationLevel'
|
||||
import { Field, Message } from 'protobufjs'
|
||||
import { Community } from '@entity/Community'
|
||||
import { Transaction } from '@entity/Transaction'
|
||||
import { Field, Message } from 'protobufjs'
|
||||
|
||||
import { AbstractTransaction } from '../AbstractTransaction'
|
||||
|
||||
// https://www.npmjs.com/package/@apollo/protobufjs
|
||||
// eslint-disable-next-line no-use-before-define
|
||||
@ -30,11 +30,6 @@ export class CommunityRoot extends Message<CommunityRoot> implements AbstractTra
|
||||
@Field.d(3, 'bytes')
|
||||
public aufPubkey: Buffer
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||
public validate(_level: TransactionValidationLevel): boolean {
|
||||
throw new Error('Method not implemented.')
|
||||
}
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-empty-function, @typescript-eslint/no-unused-vars
|
||||
public fillTransactionRecipe(recipe: Transaction): void {}
|
||||
}
|
||||
|
||||
@ -1,7 +1,9 @@
|
||||
import { Field, Message } from 'protobufjs'
|
||||
|
||||
import { base64ToBuffer } from '@/utils/typeConverter'
|
||||
|
||||
import { GradidoTransaction } from './GradidoTransaction'
|
||||
import { TimestampSeconds } from './TimestampSeconds'
|
||||
import { base64ToBuffer } from '@/utils/typeConverter'
|
||||
|
||||
/*
|
||||
id will be set by Node server
|
||||
|
||||
@ -1,9 +1,11 @@
|
||||
import 'reflect-metadata'
|
||||
import { TransactionDraft } from '@/graphql/input/TransactionDraft'
|
||||
import { GradidoCreation } from './GradidoCreation'
|
||||
import { TransactionError } from '@/graphql/model/TransactionError'
|
||||
import { TransactionErrorType } from '@enum/TransactionErrorType'
|
||||
|
||||
import { TransactionDraft } from '@/graphql/input/TransactionDraft'
|
||||
import { TransactionError } from '@/graphql/model/TransactionError'
|
||||
|
||||
import { GradidoCreation } from './GradidoCreation'
|
||||
|
||||
describe('proto/3.3/GradidoCreation', () => {
|
||||
it('test with missing targetDate', () => {
|
||||
const transactionDraft = new TransactionDraft()
|
||||
|
||||
@ -1,15 +1,16 @@
|
||||
import { Account } from '@entity/Account'
|
||||
import { Transaction } from '@entity/Transaction'
|
||||
import { Decimal } from 'decimal.js-light'
|
||||
import { Field, Message } from 'protobufjs'
|
||||
|
||||
import { TransactionErrorType } from '@/graphql/enum/TransactionErrorType'
|
||||
import { TransactionDraft } from '@/graphql/input/TransactionDraft'
|
||||
import { TransactionError } from '@/graphql/model/TransactionError'
|
||||
|
||||
import { AbstractTransaction } from '../AbstractTransaction'
|
||||
|
||||
import { TimestampSeconds } from './TimestampSeconds'
|
||||
import { TransferAmount } from './TransferAmount'
|
||||
import { TransactionDraft } from '@/graphql/input/TransactionDraft'
|
||||
import { TransactionError } from '@/graphql/model/TransactionError'
|
||||
import { TransactionErrorType } from '@/graphql/enum/TransactionErrorType'
|
||||
import { AbstractTransaction } from '../AbstractTransaction'
|
||||
import { TransactionValidationLevel } from '@/graphql/enum/TransactionValidationLevel'
|
||||
import { Transaction } from '@entity/Transaction'
|
||||
import Decimal from 'decimal.js-light'
|
||||
import { Account } from '@entity/Account'
|
||||
|
||||
// need signature from group admin or
|
||||
// percent of group users another than the receiver
|
||||
@ -36,17 +37,16 @@ export class GradidoCreation extends Message<GradidoCreation> implements Abstrac
|
||||
}
|
||||
}
|
||||
|
||||
// recipient: TransferAmount contain
|
||||
// - recipient public key
|
||||
// - amount
|
||||
// - communityId // only set if not the same as recipient community
|
||||
@Field.d(1, TransferAmount)
|
||||
public recipient: TransferAmount
|
||||
|
||||
@Field.d(3, 'TimestampSeconds')
|
||||
public targetDate: TimestampSeconds
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||
public validate(level: TransactionValidationLevel): boolean {
|
||||
throw new Error('Method not implemented.')
|
||||
}
|
||||
|
||||
public fillTransactionRecipe(recipe: Transaction): void {
|
||||
recipe.amount = new Decimal(this.recipient.amount ?? 0)
|
||||
}
|
||||
|
||||
@ -1,11 +1,11 @@
|
||||
import { Transaction } from '@entity/Transaction'
|
||||
import Decimal from 'decimal.js-light'
|
||||
import { Field, Message } from 'protobufjs'
|
||||
|
||||
import { AbstractTransaction } from '../AbstractTransaction'
|
||||
|
||||
import { GradidoTransfer } from './GradidoTransfer'
|
||||
import { TimestampSeconds } from './TimestampSeconds'
|
||||
import { AbstractTransaction } from '../AbstractTransaction'
|
||||
import { TransactionValidationLevel } from '@/graphql/enum/TransactionValidationLevel'
|
||||
import { Transaction } from '@entity/Transaction'
|
||||
import Decimal from 'decimal.js-light'
|
||||
|
||||
// transaction type for chargeable transactions
|
||||
// for transaction for people which haven't a account already
|
||||
@ -36,11 +36,6 @@ export class GradidoDeferredTransfer
|
||||
// split for n recipient
|
||||
// max gradido per recipient? or per transaction with cool down?
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||
public validate(level: TransactionValidationLevel): boolean {
|
||||
throw new Error('Method not implemented.')
|
||||
}
|
||||
|
||||
public fillTransactionRecipe(recipe: Transaction): void {
|
||||
recipe.amount = new Decimal(this.transfer.sender.amount ?? 0)
|
||||
}
|
||||
|
||||
@ -1,10 +1,11 @@
|
||||
import { Field, Message } from 'protobufjs'
|
||||
|
||||
import { SignatureMap } from './SignatureMap'
|
||||
import { TransactionBody } from './TransactionBody'
|
||||
import { SignaturePair } from './SignaturePair'
|
||||
import { LogError } from '@/server/LogError'
|
||||
|
||||
import { SignatureMap } from './SignatureMap'
|
||||
import { SignaturePair } from './SignaturePair'
|
||||
import { TransactionBody } from './TransactionBody'
|
||||
|
||||
// https://www.npmjs.com/package/@apollo/protobufjs
|
||||
// eslint-disable-next-line no-use-before-define
|
||||
export class GradidoTransaction extends Message<GradidoTransaction> {
|
||||
|
||||
@ -1,12 +1,13 @@
|
||||
import { Field, Message } from 'protobufjs'
|
||||
|
||||
import { TransferAmount } from './TransferAmount'
|
||||
import { TransactionDraft } from '@/graphql/input/TransactionDraft'
|
||||
import { AbstractTransaction } from '../AbstractTransaction'
|
||||
import { TransactionValidationLevel } from '@/graphql/enum/TransactionValidationLevel'
|
||||
import { Account } from '@entity/Account'
|
||||
import { Transaction } from '@entity/Transaction'
|
||||
import Decimal from 'decimal.js-light'
|
||||
import { Account } from '@entity/Account'
|
||||
import { Field, Message } from 'protobufjs'
|
||||
|
||||
import { TransactionDraft } from '@/graphql/input/TransactionDraft'
|
||||
|
||||
import { AbstractTransaction } from '../AbstractTransaction'
|
||||
|
||||
import { TransferAmount } from './TransferAmount'
|
||||
|
||||
// https://www.npmjs.com/package/@apollo/protobufjs
|
||||
// eslint-disable-next-line no-use-before-define
|
||||
@ -31,17 +32,17 @@ export class GradidoTransfer extends Message<GradidoTransfer> implements Abstrac
|
||||
}
|
||||
}
|
||||
|
||||
// sender: TransferAmount contain
|
||||
// - sender public key
|
||||
// - amount
|
||||
// - communityId // only set if not the same as sender and recipient community
|
||||
@Field.d(1, TransferAmount)
|
||||
public sender: TransferAmount
|
||||
|
||||
// the recipient public key
|
||||
@Field.d(2, 'bytes')
|
||||
public recipient: Buffer
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||
public validate(level: TransactionValidationLevel): boolean {
|
||||
throw new Error('Method not implemented.')
|
||||
}
|
||||
|
||||
public fillTransactionRecipe(recipe: Transaction): void {
|
||||
recipe.amount = new Decimal(this.sender?.amount ?? 0)
|
||||
}
|
||||
|
||||
@ -1,8 +1,8 @@
|
||||
/* eslint-disable @typescript-eslint/no-unused-vars */
|
||||
import { AbstractTransaction } from '../AbstractTransaction'
|
||||
import { TransactionValidationLevel } from '@/graphql/enum/TransactionValidationLevel'
|
||||
import { Field, Message } from 'protobufjs'
|
||||
import { Transaction } from '@entity/Transaction'
|
||||
import { Field, Message } from 'protobufjs'
|
||||
|
||||
import { AbstractTransaction } from '../AbstractTransaction'
|
||||
|
||||
// connect group together
|
||||
// only CrossGroupType CROSS (in TransactionBody)
|
||||
@ -17,10 +17,6 @@ export class GroupFriendsUpdate extends Message<GroupFriendsUpdate> implements A
|
||||
@Field.d(1, 'bool')
|
||||
public colorFusion: boolean
|
||||
|
||||
public validate(level: TransactionValidationLevel): boolean {
|
||||
throw new Error('Method not implemented.')
|
||||
}
|
||||
|
||||
public fillTransactionRecipe(recipe: Transaction): void {
|
||||
throw new Error('Method not implemented.')
|
||||
}
|
||||
|
||||
@ -1,11 +1,11 @@
|
||||
/* eslint-disable @typescript-eslint/no-empty-function */
|
||||
/* eslint-disable @typescript-eslint/no-unused-vars */
|
||||
import { Transaction } from '@entity/Transaction'
|
||||
import { Field, Message } from 'protobufjs'
|
||||
|
||||
import { AddressType } from '@/data/proto/3_3/enum/AddressType'
|
||||
|
||||
import { AbstractTransaction } from '../AbstractTransaction'
|
||||
import { TransactionValidationLevel } from '@/graphql/enum/TransactionValidationLevel'
|
||||
import { Transaction } from '@entity/Transaction'
|
||||
|
||||
// https://www.npmjs.com/package/@apollo/protobufjs
|
||||
// eslint-disable-next-line no-use-before-define
|
||||
@ -25,9 +25,5 @@ export class RegisterAddress extends Message<RegisterAddress> implements Abstrac
|
||||
@Field.d(5, 'uint32')
|
||||
public derivationIndex?: number
|
||||
|
||||
public validate(level: TransactionValidationLevel): boolean {
|
||||
throw new Error('Method not implemented.')
|
||||
}
|
||||
|
||||
public fillTransactionRecipe(_recipe: Transaction): void {}
|
||||
}
|
||||
|
||||
@ -1,23 +1,24 @@
|
||||
import { Transaction } from '@entity/Transaction'
|
||||
import { Field, Message, OneOf } from 'protobufjs'
|
||||
|
||||
import { CrossGroupType } from './enum/CrossGroupType'
|
||||
import { CommunityDraft } from '@/graphql/input/CommunityDraft'
|
||||
import { TransactionDraft } from '@/graphql/input/TransactionDraft'
|
||||
import { LogError } from '@/server/LogError'
|
||||
import { timestampToDate } from '@/utils/typeConverter'
|
||||
|
||||
import { Timestamp } from './Timestamp'
|
||||
import { GradidoTransfer } from './GradidoTransfer'
|
||||
import { AbstractTransaction } from '../AbstractTransaction'
|
||||
import { determineCrossGroupType, determineOtherGroup } from '../transactionBody.logic'
|
||||
|
||||
import { CommunityRoot } from './CommunityRoot'
|
||||
import { PROTO_TRANSACTION_BODY_VERSION_NUMBER } from './const'
|
||||
import { CrossGroupType } from './enum/CrossGroupType'
|
||||
import { TransactionType } from './enum/TransactionType'
|
||||
import { GradidoCreation } from './GradidoCreation'
|
||||
import { GradidoDeferredTransfer } from './GradidoDeferredTransfer'
|
||||
import { GradidoTransfer } from './GradidoTransfer'
|
||||
import { GroupFriendsUpdate } from './GroupFriendsUpdate'
|
||||
import { RegisterAddress } from './RegisterAddress'
|
||||
import { TransactionDraft } from '@/graphql/input/TransactionDraft'
|
||||
import { determineCrossGroupType, determineOtherGroup } from '../transactionBody.logic'
|
||||
import { CommunityRoot } from './CommunityRoot'
|
||||
import { CommunityDraft } from '@/graphql/input/CommunityDraft'
|
||||
import { TransactionType } from '@/graphql/enum/TransactionType'
|
||||
import { AbstractTransaction } from '../AbstractTransaction'
|
||||
import { Transaction } from '@entity/Transaction'
|
||||
import { timestampToDate } from '@/utils/typeConverter'
|
||||
import { LogError } from '@/server/LogError'
|
||||
import { PROTO_TRANSACTION_BODY_VERSION_NUMBER } from './const'
|
||||
import { Timestamp } from './Timestamp'
|
||||
|
||||
// https://www.npmjs.com/package/@apollo/protobufjs
|
||||
// eslint-disable-next-line no-use-before-define
|
||||
@ -95,6 +96,14 @@ export class TransactionBody extends Message<TransactionBody> {
|
||||
else if (this.communityRoot) return TransactionType.COMMUNITY_ROOT
|
||||
}
|
||||
|
||||
// The `TransactionBody` class utilizes Protobuf's `OneOf` field structure which, according to Protobuf documentation
|
||||
// (https://protobuf.dev/programming-guides/proto3/#oneof), allows only one field within the group to be set at a time.
|
||||
// Therefore, accessing the `getTransactionDetails()` method returns the first initialized value among the defined fields,
|
||||
// each of which should be of type AbstractTransaction. It's important to note that due to the nature of Protobuf's `OneOf`,
|
||||
// only one type from the defined options can be set within the object obtained from Protobuf.
|
||||
//
|
||||
// If multiple fields are set in a single object, the method `getTransactionDetails()` will return the first defined value
|
||||
// based on the order of checks. Developers should handle this behavior according to the expected Protobuf structure.
|
||||
public getTransactionDetails(): AbstractTransaction | undefined {
|
||||
if (this.transfer) return this.transfer
|
||||
if (this.creation) return this.creation
|
||||
|
||||
@ -1,3 +1,8 @@
|
||||
/**
|
||||
* Enum for protobuf
|
||||
* used from RegisterAddress to determine account type
|
||||
* master implementation: https://github.com/gradido/gradido_protocol/blob/master/proto/gradido/register_address.proto
|
||||
*/
|
||||
export enum AddressType {
|
||||
NONE = 0, // if no address was found
|
||||
COMMUNITY_HUMAN = 1, // creation account for human
|
||||
@ -7,13 +12,3 @@ export enum AddressType {
|
||||
SUBACCOUNT = 5, // no creations allowed
|
||||
CRYPTO_ACCOUNT = 6, // user control his keys, no creations
|
||||
}
|
||||
|
||||
export function getAddressTypeEnumValue(typeString: string): AddressType | undefined {
|
||||
// Iterate through all enum values
|
||||
for (const key in AddressType) {
|
||||
if (AddressType[key] === typeString) {
|
||||
return AddressType[key] as unknown as AddressType
|
||||
}
|
||||
}
|
||||
return undefined // If the string is not found
|
||||
}
|
||||
|
||||
@ -1,7 +1,22 @@
|
||||
/**
|
||||
* Enum for protobuf
|
||||
* Determine Cross Group type of Transactions
|
||||
* LOCAL: no cross group transactions, sender and recipient community are the same, only one transaction
|
||||
* INBOUND: cross group transaction, Inbound part. On recipient community chain. Recipient side by Transfer Transactions
|
||||
* OUTBOUND: cross group transaction, Outbound part. On sender community chain. Sender side by Transfer Transactions
|
||||
* CROSS: for cross group transaction which haven't a direction like group friend update
|
||||
* master implementation: https://github.com/gradido/gradido_protocol/blob/master/proto/gradido/transaction_body.proto
|
||||
*
|
||||
* Transaction Handling differ from database focused backend
|
||||
* In Backend for each transfer transaction there are always two entries in db,
|
||||
* on for sender user and one for recipient user despite storing basically the same data two times
|
||||
* In Blockchain Implementation there only two transactions on cross group transactions, one for
|
||||
* the sender community chain, one for the recipient community chain
|
||||
* if the transaction stay in the community there is only one transaction
|
||||
*/
|
||||
export enum CrossGroupType {
|
||||
LOCAL = 0,
|
||||
INBOUND = 1,
|
||||
OUTBOUND = 2,
|
||||
// for cross group transaction which haven't a direction like group friend update
|
||||
// CROSS = 3,
|
||||
CROSS = 3,
|
||||
}
|
||||
|
||||
13
dlt-connector/src/data/proto/3_3/enum/TransactionType.ts
Normal file
13
dlt-connector/src/data/proto/3_3/enum/TransactionType.ts
Normal file
@ -0,0 +1,13 @@
|
||||
/**
|
||||
* based on TransactionBody data oneOf
|
||||
* https://github.com/gradido/gradido_protocol/blob/master/proto/gradido/transaction_body.proto
|
||||
* for storing type in db as number
|
||||
*/
|
||||
export enum TransactionType {
|
||||
GRADIDO_TRANSFER = 1,
|
||||
GRADIDO_CREATION = 2,
|
||||
GROUP_FRIENDS_UPDATE = 3,
|
||||
REGISTER_ADDRESS = 4,
|
||||
GRADIDO_DEFERRED_TRANSFER = 5,
|
||||
COMMUNITY_ROOT = 6,
|
||||
}
|
||||
@ -1,9 +1,5 @@
|
||||
import { TransactionValidationLevel } from '@/graphql/enum/TransactionValidationLevel'
|
||||
import { Transaction } from '@entity/Transaction'
|
||||
|
||||
export abstract class AbstractTransaction {
|
||||
// validate if transaction is valid, maybe expensive because depending on level several transactions will be fetched from db
|
||||
public abstract validate(level: TransactionValidationLevel): boolean
|
||||
|
||||
public abstract fillTransactionRecipe(recipe: Transaction): void
|
||||
}
|
||||
|
||||
@ -1,13 +1,15 @@
|
||||
import { TransactionBody } from './3_3/TransactionBody'
|
||||
import { Account } from '@entity/Account'
|
||||
import { TransactionDraft } from '@/graphql/input/TransactionDraft'
|
||||
import { CommunityDraft } from '@/graphql/input/CommunityDraft'
|
||||
import { Community } from '@entity/Community'
|
||||
|
||||
import { InputTransactionType } from '@/graphql/enum/InputTransactionType'
|
||||
import { CommunityDraft } from '@/graphql/input/CommunityDraft'
|
||||
import { TransactionDraft } from '@/graphql/input/TransactionDraft'
|
||||
import { LogError } from '@/server/LogError'
|
||||
|
||||
import { CommunityRoot } from './3_3/CommunityRoot'
|
||||
import { GradidoCreation } from './3_3/GradidoCreation'
|
||||
import { GradidoTransfer } from './3_3/GradidoTransfer'
|
||||
import { Community } from '@entity/Community'
|
||||
import { CommunityRoot } from './3_3/CommunityRoot'
|
||||
import { LogError } from '@/server/LogError'
|
||||
import { TransactionBody } from './3_3/TransactionBody'
|
||||
|
||||
export class TransactionBodyBuilder {
|
||||
private signingAccount?: Account
|
||||
|
||||
@ -1,8 +1,9 @@
|
||||
import { TransactionDraft } from '@/graphql/input/TransactionDraft'
|
||||
import { CrossGroupType } from './3_3/enum/CrossGroupType'
|
||||
import { InputTransactionType } from '@/graphql/enum/InputTransactionType'
|
||||
import { TransactionError } from '@/graphql/model/TransactionError'
|
||||
import { TransactionErrorType } from '@/graphql/enum/TransactionErrorType'
|
||||
import { TransactionDraft } from '@/graphql/input/TransactionDraft'
|
||||
import { TransactionError } from '@/graphql/model/TransactionError'
|
||||
|
||||
import { CrossGroupType } from './3_3/enum/CrossGroupType'
|
||||
|
||||
export const determineCrossGroupType = ({
|
||||
senderUser,
|
||||
@ -50,5 +51,7 @@ export const determineOtherGroup = (
|
||||
)
|
||||
}
|
||||
return senderUser.communityUuid
|
||||
case CrossGroupType.CROSS:
|
||||
throw new TransactionError(TransactionErrorType.NOT_IMPLEMENTED_YET, 'not implemented yet')
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,7 +1,12 @@
|
||||
import { registerEnumType } from 'type-graphql'
|
||||
|
||||
/**
|
||||
* enum for graphql
|
||||
* describe input account type in UserAccountDraft
|
||||
* should have the same entries like enum AddressType from proto/enum folder
|
||||
*/
|
||||
export enum AccountType {
|
||||
NONE = 'none', // if no address was found
|
||||
NONE = 'NONE', // if no address was found
|
||||
COMMUNITY_HUMAN = 'COMMUNITY_HUMAN', // creation account for human
|
||||
COMMUNITY_GMW = 'COMMUNITY_GMW', // community public budget account
|
||||
COMMUNITY_AUF = 'COMMUNITY_AUF', // community compensation and environment founds account
|
||||
|
||||
@ -1,6 +1,7 @@
|
||||
import { LogError } from '@/server/LogError'
|
||||
import { registerEnumType } from 'type-graphql'
|
||||
|
||||
// enum for graphql but with int because it is the same in backend
|
||||
// for transaction type from backend
|
||||
export enum InputTransactionType {
|
||||
CREATION = 1,
|
||||
SEND = 2,
|
||||
@ -11,14 +12,3 @@ registerEnumType(InputTransactionType, {
|
||||
name: 'InputTransactionType', // this one is mandatory
|
||||
description: 'Type of the transaction', // this one is optional
|
||||
})
|
||||
|
||||
// from ChatGPT
|
||||
export function getTransactionTypeString(id: InputTransactionType): string {
|
||||
const key = Object.keys(InputTransactionType).find(
|
||||
(key) => InputTransactionType[key as keyof typeof InputTransactionType] === id,
|
||||
)
|
||||
if (key === undefined) {
|
||||
throw new LogError('invalid transaction type id: ' + id.toString())
|
||||
}
|
||||
return key
|
||||
}
|
||||
|
||||
@ -1,5 +1,7 @@
|
||||
import { registerEnumType } from 'type-graphql'
|
||||
|
||||
// enum for graphql
|
||||
// error groups for resolver answers
|
||||
export enum TransactionErrorType {
|
||||
NOT_IMPLEMENTED_YET = 'Not Implemented yet',
|
||||
MISSING_PARAMETER = 'Missing parameter',
|
||||
|
||||
@ -1,15 +0,0 @@
|
||||
import { registerEnumType } from 'type-graphql'
|
||||
|
||||
export enum TransactionType {
|
||||
GRADIDO_TRANSFER = 1,
|
||||
GRADIDO_CREATION = 2,
|
||||
GROUP_FRIENDS_UPDATE = 3,
|
||||
REGISTER_ADDRESS = 4,
|
||||
GRADIDO_DEFERRED_TRANSFER = 5,
|
||||
COMMUNITY_ROOT = 6,
|
||||
}
|
||||
|
||||
registerEnumType(TransactionType, {
|
||||
name: 'TransactionType', // this one is mandatory
|
||||
description: 'Type of the transaction', // this one is optional
|
||||
})
|
||||
@ -1,15 +0,0 @@
|
||||
import { registerEnumType } from 'type-graphql'
|
||||
|
||||
export enum TransactionValidationLevel {
|
||||
SINGLE = 1, // check only the transaction
|
||||
SINGLE_PREVIOUS = 2, // check also with previous transaction
|
||||
DATE_RANGE = 3, // check all transaction from within date range by creation automatic the same month
|
||||
PAIRED = 4, // check paired transaction on another group by cross group transactions
|
||||
CONNECTED_GROUP = 5, // check all transactions in the group which connected with this transaction address(es)
|
||||
CONNECTED_BLOCKCHAIN = 6, // check all transactions which connected with this transaction
|
||||
}
|
||||
|
||||
registerEnumType(TransactionValidationLevel, {
|
||||
name: 'TransactionValidationLevel',
|
||||
description: 'Transaction Validation Levels',
|
||||
})
|
||||
@ -2,6 +2,7 @@
|
||||
|
||||
import { IsBoolean, IsUUID } from 'class-validator'
|
||||
import { Field, InputType } from 'type-graphql'
|
||||
|
||||
import { isValidDateString } from '@validator/DateString'
|
||||
|
||||
@InputType()
|
||||
|
||||
@ -1,12 +1,13 @@
|
||||
// https://www.npmjs.com/package/@apollo/protobufjs
|
||||
|
||||
import { IsEnum, IsObject, IsPositive, ValidateNested } from 'class-validator'
|
||||
import { Decimal } from 'decimal.js-light'
|
||||
import { InputTransactionType } from '@enum/InputTransactionType'
|
||||
import { InputType, Field, Int } from 'type-graphql'
|
||||
import { UserIdentifier } from './UserIdentifier'
|
||||
|
||||
import { InputTransactionType } from '@enum/InputTransactionType'
|
||||
import { isValidDateString } from '@validator/DateString'
|
||||
import { IsPositiveDecimal } from '@validator/Decimal'
|
||||
import { IsEnum, IsObject, IsPositive, ValidateNested } from 'class-validator'
|
||||
|
||||
import { UserIdentifier } from './UserIdentifier'
|
||||
|
||||
@InputType()
|
||||
export class TransactionDraft {
|
||||
|
||||
@ -1,11 +1,14 @@
|
||||
// https://www.npmjs.com/package/@apollo/protobufjs
|
||||
|
||||
import { InputType, Field } from 'type-graphql'
|
||||
import { UserIdentifier } from './UserIdentifier'
|
||||
import { isValidDateString } from '@validator/DateString'
|
||||
import { IsEnum, IsObject, ValidateNested } from 'class-validator'
|
||||
import { InputType, Field } from 'type-graphql'
|
||||
|
||||
import { isValidDateString } from '@validator/DateString'
|
||||
|
||||
import { AccountType } from '@/graphql/enum/AccountType'
|
||||
|
||||
import { UserIdentifier } from './UserIdentifier'
|
||||
|
||||
@InputType()
|
||||
export class UserAccountDraft {
|
||||
@Field(() => UserIdentifier)
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
import { ObjectType, Field, Int } from 'type-graphql'
|
||||
import { Community as CommunityEntity } from '@entity/Community'
|
||||
import { ObjectType, Field, Int } from 'type-graphql'
|
||||
|
||||
@ObjectType()
|
||||
export class Community {
|
||||
|
||||
@ -1,4 +1,5 @@
|
||||
import { ObjectType, Field } from 'type-graphql'
|
||||
|
||||
import { TransactionErrorType } from '../enum/TransactionErrorType'
|
||||
|
||||
@ObjectType()
|
||||
|
||||
@ -1,13 +1,20 @@
|
||||
import { Field, Int, ObjectType } from 'type-graphql'
|
||||
import { TransactionType } from '@enum/TransactionType'
|
||||
import { Transaction } from '@entity/Transaction'
|
||||
import { Field, Int, ObjectType } from 'type-graphql'
|
||||
|
||||
import { TransactionType } from '@/data/proto/3_3/enum/TransactionType'
|
||||
import { LogError } from '@/server/LogError'
|
||||
import { getEnumValue } from '@/utils/typeConverter'
|
||||
|
||||
@ObjectType()
|
||||
export class TransactionRecipe {
|
||||
public constructor({ id, createdAt, type, community }: Transaction) {
|
||||
const transactionType = getEnumValue(TransactionType, type)
|
||||
if (!transactionType) {
|
||||
throw new LogError('invalid transaction, type is missing')
|
||||
}
|
||||
this.id = id
|
||||
this.createdAt = createdAt.toString()
|
||||
this.type = type
|
||||
this.type = transactionType.toString()
|
||||
this.topic = community.iotaTopic
|
||||
}
|
||||
|
||||
@ -17,8 +24,8 @@ export class TransactionRecipe {
|
||||
@Field(() => String)
|
||||
createdAt: string
|
||||
|
||||
@Field(() => TransactionType)
|
||||
type: TransactionType
|
||||
@Field(() => String)
|
||||
type: string
|
||||
|
||||
@Field(() => String)
|
||||
topic: string
|
||||
|
||||
@ -1,4 +1,5 @@
|
||||
import { ObjectType, Field } from 'type-graphql'
|
||||
|
||||
import { TransactionError } from './TransactionError'
|
||||
import { TransactionRecipe } from './TransactionRecipe'
|
||||
|
||||
|
||||
@ -1,10 +1,14 @@
|
||||
import 'reflect-metadata'
|
||||
import { ApolloServer } from '@apollo/server'
|
||||
// must be imported before createApolloTestServer so that TestDB was created before createApolloTestServer imports repositories
|
||||
import { TestDB } from '@test/TestDB'
|
||||
import { createApolloTestServer } from '@test/ApolloServerMock'
|
||||
import assert from 'assert'
|
||||
|
||||
import { ApolloServer } from '@apollo/server'
|
||||
|
||||
// must be imported before createApolloTestServer so that TestDB was created before createApolloTestServer imports repositories
|
||||
// eslint-disable-next-line import/order
|
||||
import { TestDB } from '@test/TestDB'
|
||||
import { TransactionResult } from '@model/TransactionResult'
|
||||
import { createApolloTestServer } from '@test/ApolloServerMock'
|
||||
|
||||
import { CONFIG } from '@/config'
|
||||
|
||||
CONFIG.IOTA_HOME_COMMUNITY_SEED = 'aabbccddeeff00112233445566778899aabbccddeeff00112233445566778899'
|
||||
|
||||
@ -1,17 +1,17 @@
|
||||
import { Resolver, Query, Arg, Mutation, Args } from 'type-graphql'
|
||||
|
||||
import { CommunityDraft } from '@input/CommunityDraft'
|
||||
|
||||
import { TransactionResult } from '@model/TransactionResult'
|
||||
import { TransactionError } from '@model/TransactionError'
|
||||
import { TransactionErrorType } from '@enum/TransactionErrorType'
|
||||
import { iotaTopicFromCommunityUUID } from '@/utils/typeConverter'
|
||||
import { Community } from '@model/Community'
|
||||
import { CommunityArg } from '@arg/CommunityArg'
|
||||
import { LogError } from '@/server/LogError'
|
||||
import { logger } from '@/server/logger'
|
||||
import { TransactionErrorType } from '@enum/TransactionErrorType'
|
||||
import { CommunityDraft } from '@input/CommunityDraft'
|
||||
import { Community } from '@model/Community'
|
||||
import { TransactionError } from '@model/TransactionError'
|
||||
import { TransactionResult } from '@model/TransactionResult'
|
||||
|
||||
import { CommunityRepository } from '@/data/Community.repository'
|
||||
import { AddCommunityContext } from '@/interactions/backendToDb/community/AddCommunity.context'
|
||||
import { LogError } from '@/server/LogError'
|
||||
import { logger } from '@/server/logger'
|
||||
import { iotaTopicFromCommunityUUID } from '@/utils/typeConverter'
|
||||
|
||||
@Resolver()
|
||||
export class CommunityResolver {
|
||||
|
||||
@ -1,22 +1,28 @@
|
||||
import 'reflect-metadata'
|
||||
import { ApolloServer } from '@apollo/server'
|
||||
// must be imported before createApolloTestServer so that TestDB was created before createApolloTestServer imports repositories
|
||||
import { TestDB } from '@test/TestDB'
|
||||
import { createApolloTestServer } from '@test/ApolloServerMock'
|
||||
import assert from 'assert'
|
||||
import { TransactionResult } from '@model/TransactionResult'
|
||||
import { AccountFactory } from '@/data/Account.factory'
|
||||
import { CONFIG } from '@/config'
|
||||
import { UserFactory } from '@/data/User.factory'
|
||||
import { UserAccountDraft } from '../input/UserAccountDraft'
|
||||
import { UserLogic } from '@/data/User.logic'
|
||||
import { AccountType } from '@enum/AccountType'
|
||||
import { UserIdentifier } from '../input/UserIdentifier'
|
||||
import { CommunityDraft } from '../input/CommunityDraft'
|
||||
import { AddCommunityContext } from '@/interactions/backendToDb/community/AddCommunity.context'
|
||||
import { InputTransactionType, getTransactionTypeString } from '../enum/InputTransactionType'
|
||||
|
||||
CONFIG.IOTA_HOME_COMMUNITY_SEED = 'aabbccddeeff00112233445566778899aabbccddeeff00112233445566778899'
|
||||
import { ApolloServer } from '@apollo/server'
|
||||
|
||||
// must be imported before createApolloTestServer so that TestDB was created before createApolloTestServer imports repositories
|
||||
// eslint-disable-next-line import/order
|
||||
import { TestDB } from '@test/TestDB'
|
||||
import { AccountType } from '@enum/AccountType'
|
||||
import { TransactionResult } from '@model/TransactionResult'
|
||||
import { createApolloTestServer } from '@test/ApolloServerMock'
|
||||
|
||||
import { CONFIG } from '@/config'
|
||||
import { AccountFactory } from '@/data/Account.factory'
|
||||
import { KeyPair } from '@/data/KeyPair'
|
||||
import { Mnemonic } from '@/data/Mnemonic'
|
||||
import { UserFactory } from '@/data/User.factory'
|
||||
import { UserLogic } from '@/data/User.logic'
|
||||
import { AddCommunityContext } from '@/interactions/backendToDb/community/AddCommunity.context'
|
||||
import { getEnumValue } from '@/utils/typeConverter'
|
||||
|
||||
import { InputTransactionType } from '../enum/InputTransactionType'
|
||||
import { CommunityDraft } from '../input/CommunityDraft'
|
||||
import { UserAccountDraft } from '../input/UserAccountDraft'
|
||||
import { UserIdentifier } from '../input/UserIdentifier'
|
||||
|
||||
let apolloTestServer: ApolloServer
|
||||
|
||||
@ -32,7 +38,9 @@ jest.mock('@typeorm/DataSource', () => ({
|
||||
getDataSource: jest.fn(() => TestDB.instance.dbConnect),
|
||||
}))
|
||||
|
||||
CONFIG.IOTA_HOME_COMMUNITY_SEED = 'aabbccddeeff00112233445566778899aabbccddeeff00112233445566778899'
|
||||
const communityUUID = '3d813cbb-37fb-42ba-91df-831e1593ac29'
|
||||
const communityKeyPair = new KeyPair(new Mnemonic(CONFIG.IOTA_HOME_COMMUNITY_SEED))
|
||||
|
||||
const createUserStoreAccount = async (uuid: string): Promise<UserIdentifier> => {
|
||||
const userAccountDraft = new UserAccountDraft()
|
||||
@ -41,11 +49,11 @@ const createUserStoreAccount = async (uuid: string): Promise<UserIdentifier> =>
|
||||
userAccountDraft.user = new UserIdentifier()
|
||||
userAccountDraft.user.uuid = uuid
|
||||
userAccountDraft.user.communityUuid = communityUUID
|
||||
const user = UserFactory.create(userAccountDraft)
|
||||
const user = UserFactory.create(userAccountDraft, communityKeyPair)
|
||||
const userLogic = new UserLogic(user)
|
||||
const account = AccountFactory.createAccountFromUserAccountDraft(
|
||||
userAccountDraft,
|
||||
userLogic.calculateKeyPair(),
|
||||
userLogic.calculateKeyPair(communityKeyPair),
|
||||
)
|
||||
account.user = user
|
||||
// user is set to cascade: ['insert'] will be saved together with account
|
||||
@ -82,7 +90,7 @@ describe('Transaction Resolver Test', () => {
|
||||
input: {
|
||||
senderUser,
|
||||
recipientUser,
|
||||
type: getTransactionTypeString(InputTransactionType.SEND),
|
||||
type: getEnumValue(InputTransactionType, InputTransactionType.SEND),
|
||||
amount: '10',
|
||||
createdAt: '2012-04-17T17:12:00Z',
|
||||
backendTransactionId: 1,
|
||||
@ -130,7 +138,7 @@ describe('Transaction Resolver Test', () => {
|
||||
input: {
|
||||
senderUser,
|
||||
recipientUser,
|
||||
type: getTransactionTypeString(InputTransactionType.SEND),
|
||||
type: getEnumValue(InputTransactionType, InputTransactionType.SEND),
|
||||
amount: 'no number',
|
||||
createdAt: '2012-04-17T17:12:00Z',
|
||||
backendTransactionId: 1,
|
||||
@ -156,7 +164,7 @@ describe('Transaction Resolver Test', () => {
|
||||
input: {
|
||||
senderUser,
|
||||
recipientUser,
|
||||
type: getTransactionTypeString(InputTransactionType.SEND),
|
||||
type: getEnumValue(InputTransactionType, InputTransactionType.SEND),
|
||||
amount: '10',
|
||||
createdAt: 'not valid',
|
||||
backendTransactionId: 1,
|
||||
@ -192,7 +200,7 @@ describe('Transaction Resolver Test', () => {
|
||||
input: {
|
||||
senderUser,
|
||||
recipientUser,
|
||||
type: getTransactionTypeString(InputTransactionType.CREATION),
|
||||
type: getEnumValue(InputTransactionType, InputTransactionType.CREATION),
|
||||
amount: '10',
|
||||
createdAt: '2012-04-17T17:12:00Z',
|
||||
backendTransactionId: 1,
|
||||
|
||||
@ -1,11 +1,14 @@
|
||||
import { Resolver, Arg, Mutation } from 'type-graphql'
|
||||
|
||||
import { TransactionDraft } from '@input/TransactionDraft'
|
||||
import { TransactionResult } from '../model/TransactionResult'
|
||||
import { TransactionError } from '../model/TransactionError'
|
||||
import { CreateTransactionRecipeContext } from '@/interactions/backendToDb/transaction/CreateTransationRecipe.context'
|
||||
import { TransactionRecipe } from '../model/TransactionRecipe'
|
||||
|
||||
import { TransactionRepository } from '@/data/Transaction.repository'
|
||||
import { CreateTransactionRecipeContext } from '@/interactions/backendToDb/transaction/CreateTransationRecipe.context'
|
||||
|
||||
import { TransactionErrorType } from '../enum/TransactionErrorType'
|
||||
import { TransactionError } from '../model/TransactionError'
|
||||
import { TransactionRecipe } from '../model/TransactionRecipe'
|
||||
import { TransactionResult } from '../model/TransactionResult'
|
||||
|
||||
@Resolver()
|
||||
export class TransactionResolver {
|
||||
|
||||
@ -2,9 +2,9 @@ import { Decimal } from 'decimal.js-light'
|
||||
import { GraphQLSchema } from 'graphql'
|
||||
import { buildSchema } from 'type-graphql'
|
||||
|
||||
import { DecimalScalar } from './scalar/Decimal'
|
||||
import { TransactionResolver } from './resolver/TransactionsResolver'
|
||||
import { CommunityResolver } from './resolver/CommunityResolver'
|
||||
import { TransactionResolver } from './resolver/TransactionsResolver'
|
||||
import { DecimalScalar } from './scalar/Decimal'
|
||||
|
||||
export const schema = async (): Promise<GraphQLSchema> => {
|
||||
return buildSchema({
|
||||
|
||||
@ -1,14 +1,13 @@
|
||||
/* eslint-disable @typescript-eslint/no-explicit-any */
|
||||
import { CONFIG } from '@/config'
|
||||
|
||||
import createServer from './server/createServer'
|
||||
import { KeyManager } from './manager/KeyManager'
|
||||
|
||||
async function main() {
|
||||
// eslint-disable-next-line no-console
|
||||
console.log(`DLT_CONNECTOR_PORT=${CONFIG.DLT_CONNECTOR_PORT}`)
|
||||
const { app } = await createServer()
|
||||
|
||||
await KeyManager.getInstance().init()
|
||||
app.listen(CONFIG.DLT_CONNECTOR_PORT, () => {
|
||||
// eslint-disable-next-line no-console
|
||||
console.log(`Server is running at http://localhost:${CONFIG.DLT_CONNECTOR_PORT}`)
|
||||
|
||||
@ -1,8 +1,9 @@
|
||||
import { CommunityDraft } from '@/graphql/input/CommunityDraft'
|
||||
import { iotaTopicFromCommunityUUID } from '@/utils/typeConverter'
|
||||
|
||||
import { CommunityRole } from './Community.role'
|
||||
import { ForeignCommunityRole } from './ForeignCommunity.role'
|
||||
import { HomeCommunityRole } from './HomeCommunity.role'
|
||||
import { iotaTopicFromCommunityUUID } from '@/utils/typeConverter'
|
||||
import { CommunityRole } from './Community.role'
|
||||
|
||||
/**
|
||||
* @DCI-Context
|
||||
|
||||
@ -1,8 +1,9 @@
|
||||
import { Community } from '@entity/Community'
|
||||
|
||||
import { TransactionErrorType } from '@/graphql/enum/TransactionErrorType'
|
||||
import { CommunityDraft } from '@/graphql/input/CommunityDraft'
|
||||
import { TransactionError } from '@/graphql/model/TransactionError'
|
||||
import { logger } from '@/server/logger'
|
||||
import { Community } from '@entity/Community'
|
||||
|
||||
export abstract class CommunityRole {
|
||||
protected self: Community
|
||||
|
||||
@ -1,27 +1,28 @@
|
||||
import { CommunityDraft } from '@/graphql/input/CommunityDraft'
|
||||
import { Community } from '@entity/Community'
|
||||
import { CommunityRole } from './Community.role'
|
||||
import { Transaction } from '@entity/Transaction'
|
||||
import { KeyManager } from '@/manager/KeyManager'
|
||||
|
||||
import { CONFIG } from '@/config'
|
||||
import { AccountFactory } from '@/data/Account.factory'
|
||||
import { CreateTransactionRecipeContext } from '../transaction/CreateTransationRecipe.context'
|
||||
import { logger } from '@/server/logger'
|
||||
import { TransactionError } from '@/graphql/model/TransactionError'
|
||||
import { KeyPair } from '@/data/KeyPair'
|
||||
import { Mnemonic } from '@/data/Mnemonic'
|
||||
import { TransactionErrorType } from '@/graphql/enum/TransactionErrorType'
|
||||
import { CommunityDraft } from '@/graphql/input/CommunityDraft'
|
||||
import { TransactionError } from '@/graphql/model/TransactionError'
|
||||
import { logger } from '@/server/logger'
|
||||
import { getDataSource } from '@/typeorm/DataSource'
|
||||
|
||||
import { CreateTransactionRecipeContext } from '../transaction/CreateTransationRecipe.context'
|
||||
|
||||
import { CommunityRole } from './Community.role'
|
||||
|
||||
export class HomeCommunityRole extends CommunityRole {
|
||||
private transactionRecipe: Transaction
|
||||
|
||||
public async create(communityDraft: CommunityDraft, topic: string): Promise<void> {
|
||||
super.create(communityDraft, topic)
|
||||
// generate key pair for signing transactions and deriving all keys for community
|
||||
const keyPair = KeyManager.generateKeyPair()
|
||||
this.self.rootPubkey = keyPair.publicKey
|
||||
this.self.rootPrivkey = keyPair.privateKey
|
||||
this.self.rootChaincode = keyPair.chainCode
|
||||
// we should only have one home community per server
|
||||
KeyManager.getInstance().setHomeCommunityKeyPair(keyPair)
|
||||
const keyPair = new KeyPair(new Mnemonic(CONFIG.IOTA_HOME_COMMUNITY_SEED ?? undefined))
|
||||
keyPair.fillInCommunityKeys(this.self)
|
||||
|
||||
// create auf account and gmw account
|
||||
this.self.aufAccount = AccountFactory.createAufAccount(keyPair, this.self.createdAt)
|
||||
|
||||
@ -1,9 +1,10 @@
|
||||
import { CommunityDraft } from '@/graphql/input/CommunityDraft'
|
||||
import { TransactionRecipeRole } from './TransactionRecipe.role'
|
||||
import { Community } from '@entity/Community'
|
||||
import { TransactionBodyBuilder } from '@/data/proto/TransactionBody.builder'
|
||||
|
||||
import { KeyPair } from '@/data/KeyPair'
|
||||
import { sign } from '@/utils/cryptoHelper'
|
||||
import { TransactionBodyBuilder } from '@/data/proto/TransactionBody.builder'
|
||||
import { CommunityDraft } from '@/graphql/input/CommunityDraft'
|
||||
|
||||
import { TransactionRecipeRole } from './TransactionRecipe.role'
|
||||
|
||||
export class CommunityRootTransactionRole extends TransactionRecipeRole {
|
||||
public createFromCommunityRoot(
|
||||
@ -18,7 +19,7 @@ export class CommunityRootTransactionRole extends TransactionRecipeRole {
|
||||
this.transactionBuilder.fromTransactionBody(transactionBody).setCommunity(community)
|
||||
const transaction = this.transactionBuilder.getTransaction()
|
||||
// sign
|
||||
this.transactionBuilder.setSignature(sign(transaction.bodyBytes, new KeyPair(community)))
|
||||
this.transactionBuilder.setSignature(new KeyPair(community).sign(transaction.bodyBytes))
|
||||
return this
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,11 +1,13 @@
|
||||
import { CommunityDraft } from '@/graphql/input/CommunityDraft'
|
||||
import { Community } from '@entity/Community'
|
||||
import { TransactionRecipeRole } from './TransactionRecipe.role'
|
||||
import { CommunityRootTransactionRole } from './CommunityRootTransaction.role'
|
||||
import { TransactionDraft } from '@/graphql/input/TransactionDraft'
|
||||
import { Transaction } from '@entity/Transaction'
|
||||
import { TransactionError } from '@/graphql/model/TransactionError'
|
||||
|
||||
import { TransactionErrorType } from '@/graphql/enum/TransactionErrorType'
|
||||
import { CommunityDraft } from '@/graphql/input/CommunityDraft'
|
||||
import { TransactionDraft } from '@/graphql/input/TransactionDraft'
|
||||
import { TransactionError } from '@/graphql/model/TransactionError'
|
||||
|
||||
import { CommunityRootTransactionRole } from './CommunityRootTransaction.role'
|
||||
import { TransactionRecipeRole } from './TransactionRecipe.role'
|
||||
|
||||
/**
|
||||
* @DCI-Context
|
||||
|
||||
@ -1,12 +1,12 @@
|
||||
import { Transaction } from '@entity/Transaction'
|
||||
|
||||
import { KeyPair } from '@/data/KeyPair'
|
||||
import { TransactionBodyBuilder } from '@/data/proto/TransactionBody.builder'
|
||||
import { TransactionBuilder } from '@/data/Transaction.builder'
|
||||
import { UserRepository } from '@/data/User.repository'
|
||||
import { TransactionBodyBuilder } from '@/data/proto/TransactionBody.builder'
|
||||
import { TransactionErrorType } from '@/graphql/enum/TransactionErrorType'
|
||||
import { TransactionDraft } from '@/graphql/input/TransactionDraft'
|
||||
import { TransactionError } from '@/graphql/model/TransactionError'
|
||||
import { sign } from '@/utils/cryptoHelper'
|
||||
import { Transaction } from '@entity/Transaction'
|
||||
|
||||
export class TransactionRecipeRole {
|
||||
protected transactionBuilder: TransactionBuilder
|
||||
@ -52,7 +52,7 @@ export class TransactionRecipeRole {
|
||||
const transaction = this.transactionBuilder.getTransaction()
|
||||
// sign
|
||||
this.transactionBuilder.setSignature(
|
||||
sign(transaction.bodyBytes, new KeyPair(this.transactionBuilder.getCommunity())),
|
||||
new KeyPair(this.transactionBuilder.getCommunity()).sign(transaction.bodyBytes),
|
||||
)
|
||||
return this
|
||||
}
|
||||
|
||||
@ -1,22 +0,0 @@
|
||||
/* eslint-disable camelcase */
|
||||
import { entropyToMnemonic, mnemonicToSeedSync } from 'bip39'
|
||||
import { generateFromSeed, toPublic } from 'bip32-ed25519'
|
||||
|
||||
describe('controller/KeyManager', () => {
|
||||
describe('test crypto lib', () => {
|
||||
it('key length', () => {
|
||||
const mnemonic = entropyToMnemonic(
|
||||
'aabbccddeeff00112233445566778899aabbccddeeff00112233445566778899',
|
||||
)
|
||||
expect(mnemonic).toEqual(
|
||||
'primary taxi danger target useless ancient match hammer fever crisp timber crew produce toy jeans that abandon math mimic master filter design carbon carbon',
|
||||
)
|
||||
const seed = mnemonicToSeedSync(mnemonic)
|
||||
// private key 64 Bytes + 32 Byte ChainCode
|
||||
const extendPrivkey = generateFromSeed(seed)
|
||||
expect(extendPrivkey).toHaveLength(96)
|
||||
// public key 32 Bytes + 32 Bytes ChainCode
|
||||
expect(toPublic(extendPrivkey)).toHaveLength(64)
|
||||
})
|
||||
})
|
||||
})
|
||||
@ -1,118 +0,0 @@
|
||||
// eslint-disable-next-line camelcase
|
||||
import { randombytes_buf } from 'sodium-native'
|
||||
import { CONFIG } from '../config'
|
||||
import { entropyToMnemonic, mnemonicToSeedSync } from 'bip39'
|
||||
// https://www.npmjs.com/package/bip32-ed25519?activeTab=code
|
||||
import { generateFromSeed, derivePrivate, sign as ed25519Sign } from 'bip32-ed25519'
|
||||
import { logger } from '@/server/logger'
|
||||
import { LogError } from '@/server/LogError'
|
||||
import { KeyPair } from '@/data/KeyPair'
|
||||
import { GradidoTransaction } from '@/data/proto/3_3/GradidoTransaction'
|
||||
import { CommunityRepository } from '@/data/Community.repository'
|
||||
import { SignaturePair } from '@/data/proto/3_3/SignaturePair'
|
||||
|
||||
// Source: https://refactoring.guru/design-patterns/singleton/typescript/example
|
||||
// and ../federation/client/FederationClientFactory.ts
|
||||
/**
|
||||
* A Singleton class defines the `getInstance` method that lets clients access
|
||||
* the unique singleton instance.
|
||||
*/
|
||||
// eslint-disable-next-line @typescript-eslint/no-extraneous-class
|
||||
export class KeyManager {
|
||||
// eslint-disable-next-line no-use-before-define
|
||||
private static instance: KeyManager
|
||||
private homeCommunityRootKeys: KeyPair | null = null
|
||||
|
||||
/**
|
||||
* The Singleton's constructor should always be private to prevent direct
|
||||
* construction calls with the `new` operator.
|
||||
*/
|
||||
// eslint-disable-next-line no-useless-constructor, @typescript-eslint/no-empty-function
|
||||
private constructor() {}
|
||||
|
||||
/**
|
||||
* The static method that controls the access to the singleton instance.
|
||||
*
|
||||
* This implementation let you subclass the Singleton class while keeping
|
||||
* just one instance of each subclass around.
|
||||
*/
|
||||
public static getInstance(): KeyManager {
|
||||
if (!KeyManager.instance) {
|
||||
KeyManager.instance = new KeyManager()
|
||||
}
|
||||
return KeyManager.instance
|
||||
}
|
||||
|
||||
public async init(): Promise<boolean> {
|
||||
try {
|
||||
this.homeCommunityRootKeys = await CommunityRepository.loadHomeCommunityKeyPair()
|
||||
return true
|
||||
} catch (error) {
|
||||
logger.error('error by init key manager', error)
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
public static generateKeyPair(): KeyPair {
|
||||
const mnemonic = KeyManager.generateMnemonic(CONFIG.IOTA_HOME_COMMUNITY_SEED ?? undefined)
|
||||
// logger.info('passphrase for key pair: ' + mnemonic)
|
||||
const seed = mnemonicToSeedSync(mnemonic)
|
||||
return new KeyPair(generateFromSeed(seed))
|
||||
}
|
||||
|
||||
public setHomeCommunityKeyPair(keyPair: KeyPair) {
|
||||
this.homeCommunityRootKeys = keyPair
|
||||
}
|
||||
|
||||
public sign(transaction: GradidoTransaction, keys?: KeyPair[]) {
|
||||
let localKeys: KeyPair[] = []
|
||||
|
||||
if (!keys && this.homeCommunityRootKeys) {
|
||||
localKeys.push(this.homeCommunityRootKeys)
|
||||
} else if (keys) {
|
||||
localKeys = keys
|
||||
}
|
||||
if (!localKeys.length) {
|
||||
throw new LogError('no key pair for signing')
|
||||
}
|
||||
localKeys.forEach((keyPair: KeyPair) => {
|
||||
const signature = ed25519Sign(transaction.bodyBytes, keyPair.getExtendPrivateKey())
|
||||
const sigPair = new SignaturePair({ pubKey: keyPair.publicKey, signature })
|
||||
logger.debug('sign transaction', {
|
||||
signature: signature.toString('hex'),
|
||||
publicKey: keyPair.publicKey.toString('hex'),
|
||||
bodyBytes: transaction.bodyBytes.toString('hex'),
|
||||
})
|
||||
transaction.sigMap.sigPair.push(sigPair)
|
||||
})
|
||||
}
|
||||
|
||||
public getHomeCommunityPublicKey(): Buffer | undefined {
|
||||
if (!this.homeCommunityRootKeys) return undefined
|
||||
return this.homeCommunityRootKeys.publicKey
|
||||
}
|
||||
|
||||
public derive(path: number[], parentKeys?: KeyPair): KeyPair {
|
||||
const extendedPrivateKey = parentKeys
|
||||
? parentKeys.getExtendPrivateKey()
|
||||
: this.homeCommunityRootKeys?.getExtendPrivateKey()
|
||||
if (!extendedPrivateKey) {
|
||||
throw new LogError('missing parent or root key pair')
|
||||
}
|
||||
return new KeyPair(
|
||||
path.reduce(
|
||||
(extendPrivateKey: Buffer, node: number) => derivePrivate(extendPrivateKey, node),
|
||||
extendedPrivateKey,
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
static generateMnemonic(seed?: Buffer | string): string {
|
||||
if (seed) {
|
||||
return entropyToMnemonic(seed)
|
||||
}
|
||||
const entropy = Buffer.alloc(256)
|
||||
randombytes_buf(entropy)
|
||||
return entropyToMnemonic(entropy)
|
||||
}
|
||||
}
|
||||
@ -2,16 +2,16 @@ import 'reflect-metadata'
|
||||
|
||||
import { ApolloServer } from '@apollo/server'
|
||||
import { expressMiddleware } from '@apollo/server/express4'
|
||||
import bodyParser from 'body-parser'
|
||||
import cors from 'cors'
|
||||
import express, { Express } from 'express'
|
||||
|
||||
// graphql
|
||||
import { Logger } from 'log4js'
|
||||
|
||||
import { schema } from '@/graphql/schema'
|
||||
import { Connection } from '@/typeorm/DataSource'
|
||||
|
||||
import { logger as dltLogger } from './logger'
|
||||
import { Logger } from 'log4js'
|
||||
import cors from 'cors'
|
||||
import bodyParser from 'body-parser'
|
||||
import { Connection } from '@/typeorm/DataSource'
|
||||
|
||||
type ServerDef = { apollo: ApolloServer; app: Express }
|
||||
|
||||
|
||||
@ -1,7 +1,9 @@
|
||||
import { readFileSync } from 'fs'
|
||||
|
||||
import log4js from 'log4js'
|
||||
|
||||
import { CONFIG } from '@/config'
|
||||
|
||||
import { readFileSync } from 'fs'
|
||||
const options = JSON.parse(readFileSync(CONFIG.LOG4JS_CONFIG, 'utf-8'))
|
||||
|
||||
log4js.configure(options)
|
||||
|
||||
@ -2,11 +2,11 @@
|
||||
// We cannot use our connection here, but must use the external typeorm installation
|
||||
import { DataSource as DBDataSource, FileLogger } from '@dbTools/typeorm'
|
||||
import { entities } from '@entity/index'
|
||||
import { Migration } from '@entity/Migration'
|
||||
|
||||
import { CONFIG } from '@/config'
|
||||
import { logger } from '@/server/logger'
|
||||
import { Migration } from '@entity/Migration'
|
||||
import { LogError } from '@/server/LogError'
|
||||
import { logger } from '@/server/logger'
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-extraneous-class
|
||||
export class Connection {
|
||||
|
||||
@ -1,23 +0,0 @@
|
||||
import { KeyPair } from '@/data/KeyPair'
|
||||
import { sign as ed25519Sign, generateFromSeed } from 'bip32-ed25519'
|
||||
import { entropyToMnemonic, mnemonicToSeedSync } from 'bip39'
|
||||
// eslint-disable-next-line camelcase
|
||||
import { randombytes_buf } from 'sodium-native'
|
||||
|
||||
export const sign = (message: Buffer, keyPair: KeyPair): Buffer => {
|
||||
return ed25519Sign(message, keyPair.getExtendPrivateKey())
|
||||
}
|
||||
|
||||
export const generateKeyPair = (mnemonic: string): KeyPair => {
|
||||
const seedFromMnemonic = mnemonicToSeedSync(mnemonic)
|
||||
return new KeyPair(generateFromSeed(seedFromMnemonic))
|
||||
}
|
||||
|
||||
export const generateMnemonic = (seed?: Buffer | string): string => {
|
||||
if (seed) {
|
||||
return entropyToMnemonic(seed)
|
||||
}
|
||||
const entropy = Buffer.alloc(256)
|
||||
randombytes_buf(entropy)
|
||||
return entropyToMnemonic(entropy)
|
||||
}
|
||||
@ -1,5 +1,6 @@
|
||||
import 'reflect-metadata'
|
||||
import { Timestamp } from '../data/proto/3_3/Timestamp'
|
||||
|
||||
import { hardenDerivationIndex, HARDENED_KEY_BITMASK } from './derivationHelper'
|
||||
import { timestampToDate } from './typeConverter'
|
||||
|
||||
|
||||
@ -1,5 +1,6 @@
|
||||
import 'reflect-metadata'
|
||||
import { Timestamp } from '@/data/proto/3_3/Timestamp'
|
||||
|
||||
import { timestampToDate } from './typeConverter'
|
||||
|
||||
describe('utils/typeConverter', () => {
|
||||
|
||||
@ -1,14 +1,14 @@
|
||||
import { crypto_generichash as cryptoHash } from 'sodium-native'
|
||||
|
||||
import { AddressType } from '@/data/proto/3_3/enum/AddressType'
|
||||
import { Timestamp } from '@/data/proto/3_3/Timestamp'
|
||||
import { TimestampSeconds } from '@/data/proto/3_3/TimestampSeconds'
|
||||
import { TransactionBody } from '@/data/proto/3_3/TransactionBody'
|
||||
import { logger } from '@/server/logger'
|
||||
import { TransactionError } from '@/graphql/model/TransactionError'
|
||||
import { TransactionErrorType } from '@/graphql/enum/TransactionErrorType'
|
||||
import { AccountType } from '@/graphql/enum/AccountType'
|
||||
import { TransactionErrorType } from '@/graphql/enum/TransactionErrorType'
|
||||
import { TransactionError } from '@/graphql/model/TransactionError'
|
||||
import { LogError } from '@/server/LogError'
|
||||
import { AddressType } from '@/data/proto/3_3/enum/AddressType'
|
||||
import { logger } from '@/server/logger'
|
||||
|
||||
export const uuid4ToBuffer = (uuid: string): Buffer => {
|
||||
// Remove dashes from the UUIDv4 string
|
||||
@ -64,44 +64,44 @@ export const transactionBodyToBodyBytes = (transactionBody: TransactionBody): Bu
|
||||
}
|
||||
}
|
||||
|
||||
export const accountTypeToAddressType = (accountType: AccountType): AddressType => {
|
||||
switch (accountType) {
|
||||
case AccountType.NONE:
|
||||
return AddressType.NONE
|
||||
case AccountType.COMMUNITY_HUMAN:
|
||||
return AddressType.COMMUNITY_HUMAN
|
||||
case AccountType.COMMUNITY_GMW:
|
||||
return AddressType.COMMUNITY_GMW
|
||||
case AccountType.COMMUNITY_AUF:
|
||||
return AddressType.COMMUNITY_AUF
|
||||
case AccountType.COMMUNITY_PROJECT:
|
||||
return AddressType.COMMUNITY_PROJECT
|
||||
case AccountType.SUBACCOUNT:
|
||||
return AddressType.SUBACCOUNT
|
||||
case AccountType.CRYPTO_ACCOUNT:
|
||||
return AddressType.CRYPTO_ACCOUNT
|
||||
default:
|
||||
throw new LogError(`Unsupported AccountType: ${accountType}`)
|
||||
export function getEnumValue<T extends Record<string, unknown>>(
|
||||
enumType: T,
|
||||
value: number | string,
|
||||
): T[keyof T] | undefined {
|
||||
if (typeof value === 'number' && typeof enumType === 'object') {
|
||||
return enumType[value as keyof T] as T[keyof T]
|
||||
} else if (typeof value === 'string') {
|
||||
for (const key in enumType) {
|
||||
if (enumType[key as keyof T] === value) {
|
||||
return enumType[key as keyof T] as T[keyof T]
|
||||
}
|
||||
}
|
||||
}
|
||||
return undefined
|
||||
}
|
||||
|
||||
export const addressTypeToAccountType = (addressType: AddressType): AccountType => {
|
||||
switch (addressType) {
|
||||
case AddressType.NONE:
|
||||
return AccountType.NONE
|
||||
case AddressType.COMMUNITY_HUMAN:
|
||||
return AccountType.COMMUNITY_HUMAN
|
||||
case AddressType.COMMUNITY_GMW:
|
||||
return AccountType.COMMUNITY_GMW
|
||||
case AddressType.COMMUNITY_AUF:
|
||||
return AccountType.COMMUNITY_AUF
|
||||
case AddressType.COMMUNITY_PROJECT:
|
||||
return AccountType.COMMUNITY_PROJECT
|
||||
case AddressType.SUBACCOUNT:
|
||||
return AccountType.SUBACCOUNT
|
||||
case AddressType.CRYPTO_ACCOUNT:
|
||||
return AccountType.CRYPTO_ACCOUNT
|
||||
default:
|
||||
throw new LogError(`Unsupported AddressType: ${addressType}`)
|
||||
export const accountTypeToAddressType = (type: AccountType): AddressType => {
|
||||
const typeString: string = AccountType[type]
|
||||
const addressType: AddressType = AddressType[typeString as keyof typeof AddressType]
|
||||
|
||||
if (!addressType) {
|
||||
throw new LogError("couldn't find corresponding AddressType for AccountType", {
|
||||
accountType: type,
|
||||
addressTypes: Object.keys(AddressType),
|
||||
})
|
||||
}
|
||||
return addressType
|
||||
}
|
||||
|
||||
export const addressTypeToAccountType = (type: AddressType): AccountType => {
|
||||
const typeString: string = AddressType[type]
|
||||
const accountType: AccountType = AccountType[typeString as keyof typeof AccountType]
|
||||
|
||||
if (!accountType) {
|
||||
throw new LogError("couldn't find corresponding AccountType for AddressType", {
|
||||
addressTypes: type,
|
||||
accountType: Object.keys(AccountType),
|
||||
})
|
||||
}
|
||||
return accountType
|
||||
}
|
||||
|
||||
@ -1,5 +1,6 @@
|
||||
import { ApolloServer } from '@apollo/server'
|
||||
import { addMocksToSchema } from '@graphql-tools/mock'
|
||||
|
||||
import { schema } from '@/graphql/schema'
|
||||
|
||||
let apolloTestServer: ApolloServer
|
||||
|
||||
@ -1,7 +1,6 @@
|
||||
import { DataSource, FileLogger } from '@dbTools/typeorm'
|
||||
import { createDatabase } from 'typeorm-extension'
|
||||
|
||||
import { entities } from '@entity/index'
|
||||
import { createDatabase } from 'typeorm-extension'
|
||||
|
||||
import { CONFIG } from '@/config'
|
||||
import { LogError } from '@/server/LogError'
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user