Merge branch 'master' into 2947-refactor-the-existing-sendcoins-resolver-methode-to-distingue-between-local-transaction-and-x-transaction

This commit is contained in:
clauspeterhuebner 2023-09-06 17:06:35 +02:00 committed by GitHub
commit 5aa886121c
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
45 changed files with 15019 additions and 5 deletions

View File

@ -27,6 +27,7 @@ jobs:
frontend
admin
database
dlt-database
release
federation
dht

View File

@ -294,7 +294,7 @@ describe('CommunityResolver', () => {
})
})
describe('with several community entries', () => {
describe('returns 2 filtered communities even with 3 existing entries', () => {
beforeEach(async () => {
await cleanDB()
jest.clearAllMocks()
@ -316,8 +316,8 @@ describe('CommunityResolver', () => {
foreignCom1.url = 'http://stage-2.gradido.net/api'
foreignCom1.publicKey = Buffer.from('publicKey-stage-2_Community')
foreignCom1.privateKey = Buffer.from('privateKey-stage-2_Community')
foreignCom1.communityUuid = 'Stage2-Com-UUID'
foreignCom1.authenticatedAt = new Date()
// foreignCom1.communityUuid = 'Stage2-Com-UUID'
// foreignCom1.authenticatedAt = new Date()
foreignCom1.name = 'Stage-2_Community-name'
foreignCom1.description = 'Stage-2_Community-description'
foreignCom1.creationDate = new Date()
@ -336,7 +336,7 @@ describe('CommunityResolver', () => {
await DbCommunity.insert(foreignCom2)
})
it('returns 3 community entries', async () => {
it('returns 2 community entries', async () => {
await expect(query({ query: communities })).resolves.toMatchObject({
data: {
communities: [
@ -350,6 +350,7 @@ describe('CommunityResolver', () => {
uuid: homeCom1.communityUuid,
authenticatedAt: homeCom1.authenticatedAt?.toISOString(),
},
/*
{
id: expect.any(Number),
foreign: foreignCom1.foreign,
@ -360,6 +361,7 @@ describe('CommunityResolver', () => {
uuid: foreignCom1.communityUuid,
authenticatedAt: foreignCom1.authenticatedAt?.toISOString(),
},
*/
{
id: expect.any(Number),
foreign: foreignCom2.foreign,

View File

@ -1,3 +1,4 @@
import { IsNull, Not } from '@dbTools/typeorm'
import { Community as DbCommunity } from '@entity/Community'
import { FederatedCommunity as DbFederatedCommunity } from '@entity/FederatedCommunity'
import { Resolver, Query, Authorized } from 'type-graphql'
@ -28,6 +29,7 @@ export class CommunityResolver {
@Query(() => [Community])
async communities(): Promise<Community[]> {
const dbCommunities: DbCommunity[] = await DbCommunity.find({
where: { communityUuid: Not(IsNull()) }, //, authenticatedAt: Not(IsNull()) },
order: {
name: 'ASC',
},

6
dlt-database/.env.dist Normal file
View File

@ -0,0 +1,6 @@
DB_HOST=localhost
DB_PORT=3306
DB_USER=root
DB_PASSWORD=
DB_DATABASE=gradido_dlt
MIGRATIONS_TABLE=migrations

View File

@ -0,0 +1,8 @@
CONFIG_VERSION=$DATABASE_CONFIG_VERSION
DB_HOST=localhost
DB_PORT=3306
DB_USER=$DB_USER
DB_PASSWORD=$DB_PASSWORD
DB_DATABASE=gradido_dlt
MIGRATIONS_TABLE=migrations

View File

@ -0,0 +1,3 @@
node_modules
**/*.min.js
build

206
dlt-database/.eslintrc.js Normal file
View File

@ -0,0 +1,206 @@
// eslint-disable-next-line import/no-commonjs, import/unambiguous
module.exports = {
root: true,
env: {
node: true,
},
parser: '@typescript-eslint/parser',
plugins: ['prettier', '@typescript-eslint', 'import', 'n', 'promise'],
extends: [
'standard',
'eslint:recommended',
'plugin:prettier/recommended',
'plugin:import/recommended',
'plugin:import/typescript',
// 'plugin:security/recommended',
'plugin:@eslint-community/eslint-comments/recommended',
],
settings: {
'import/parsers': {
'@typescript-eslint/parser': ['.ts', '.tsx'],
},
'import/resolver': {
typescript: {
project: ['./tsconfig.json'],
},
node: true,
},
},
rules: {
'no-console': 'error',
camelcase: 'error',
'no-debugger': 'error',
'prettier/prettier': [
'error',
{
htmlWhitespaceSensitivity: 'ignore',
},
],
// import
'import/export': 'error',
'import/no-deprecated': 'error',
'import/no-empty-named-blocks': 'error',
// 'import/no-extraneous-dependencies': 'error',
'import/no-mutable-exports': 'error',
'import/no-unused-modules': 'error',
'import/no-named-as-default': 'error',
'import/no-named-as-default-member': 'error',
'import/no-amd': 'error',
'import/no-commonjs': 'error',
'import/no-import-module-exports': 'error',
'import/no-nodejs-modules': 'off',
'import/unambiguous': 'error',
'import/default': 'error',
'import/named': 'error',
'import/namespace': 'error',
'import/no-absolute-path': 'error',
// 'import/no-cycle': 'error',
'import/no-dynamic-require': 'error',
'import/no-internal-modules': 'off',
'import/no-relative-packages': 'error',
// 'import/no-relative-parent-imports': ['error', { ignore: ['@/*'] }],
'import/no-self-import': 'error',
'import/no-unresolved': 'error',
'import/no-useless-path-segments': 'error',
'import/no-webpack-loader-syntax': 'error',
'import/consistent-type-specifier-style': 'error',
'import/exports-last': 'off',
'import/extensions': 'error',
'import/first': 'error',
'import/group-exports': 'off',
'import/newline-after-import': 'error',
'import/no-anonymous-default-export': 'error',
'import/no-default-export': 'error',
'import/no-duplicates': 'error',
'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/prefer-default-export': 'off',
// n
'n/handle-callback-err': 'error',
'n/no-callback-literal': 'error',
'n/no-exports-assign': 'error',
// 'n/no-extraneous-import': 'error',
'n/no-extraneous-require': 'error',
'n/no-hide-core-modules': 'error',
'n/no-missing-import': 'off', // not compatible with typescript
'n/no-missing-require': 'error',
'n/no-new-require': 'error',
'n/no-path-concat': 'error',
// 'n/no-process-exit': 'error',
'n/no-unpublished-bin': 'error',
'n/no-unpublished-import': 'off', // TODO need to exclude seeds
'n/no-unpublished-require': 'error',
'n/no-unsupported-features': ['error', { ignores: ['modules'] }],
'n/no-unsupported-features/es-builtins': 'error',
'n/no-unsupported-features/es-syntax': 'error',
'n/no-unsupported-features/node-builtins': 'error',
'n/process-exit-as-throw': 'error',
'n/shebang': 'error',
'n/callback-return': 'error',
'n/exports-style': 'error',
'n/file-extension-in-import': 'off',
'n/global-require': 'error',
'n/no-mixed-requires': 'error',
'n/no-process-env': 'error',
'n/no-restricted-import': 'error',
'n/no-restricted-require': 'error',
// 'n/no-sync': 'error',
'n/prefer-global/buffer': 'error',
'n/prefer-global/console': 'error',
'n/prefer-global/process': 'error',
'n/prefer-global/text-decoder': 'error',
'n/prefer-global/text-encoder': 'error',
'n/prefer-global/url': 'error',
'n/prefer-global/url-search-params': 'error',
'n/prefer-promises/dns': 'error',
// 'n/prefer-promises/fs': 'error',
// promise
// 'promise/catch-or-return': 'error',
// 'promise/no-return-wrap': 'error',
// 'promise/param-names': 'error',
// 'promise/always-return': 'error',
// 'promise/no-native': 'off',
// 'promise/no-nesting': 'warn',
// 'promise/no-promise-in-callback': 'warn',
// 'promise/no-callback-in-promise': 'warn',
// 'promise/avoid-new': 'warn',
// 'promise/no-new-statics': 'error',
// 'promise/no-return-in-finally': 'warn',
// 'promise/valid-params': 'warn',
// 'promise/prefer-await-to-callbacks': 'error',
// 'promise/no-multiple-resolved': 'error',
// eslint comments
'@eslint-community/eslint-comments/disable-enable-pair': ['error', { allowWholeFile: true }],
'@eslint-community/eslint-comments/no-restricted-disable': 'error',
'@eslint-community/eslint-comments/no-use': 'off',
'@eslint-community/eslint-comments/require-description': 'off',
},
overrides: [
// only for ts files
{
files: ['*.ts', '*.tsx'],
extends: [
// 'plugin:@typescript-eslint/recommended',
// 'plugin:@typescript-eslint/recommended-requiring-type-checking',
// 'plugin:@typescript-eslint/strict',
],
rules: {
// allow explicitly defined dangling promises
// '@typescript-eslint/no-floating-promises': ['error', { ignoreVoid: true }],
'no-void': ['error', { allowAsStatement: true }],
// ignore prefer-regexp-exec rule to allow string.match(regex)
'@typescript-eslint/prefer-regexp-exec': 'off',
// this should not run on ts files: https://github.com/import-js/eslint-plugin-import/issues/2215#issuecomment-911245486
'import/unambiguous': 'off',
// this is not compatible with typeorm, due to joined tables can be null, but are not defined as nullable
'@typescript-eslint/no-unnecessary-condition': 'off',
},
parserOptions: {
tsconfigRootDir: __dirname,
project: ['./tsconfig.json'],
// this is to properly reference the referenced project database without requirement of compiling it
// eslint-disable-next-line camelcase
EXPERIMENTAL_useSourceOfProjectReferenceRedirect: true,
},
},
// we do not have testing on the database
// {
// files: ['*.test.ts'],
// plugins: ['jest'],
// rules: {
// 'jest/no-disabled-tests': 'error',
// 'jest/no-focused-tests': 'error',
// 'jest/no-identical-title': 'error',
// 'jest/prefer-to-have-length': 'error',
// 'jest/valid-expect': 'error',
// '@typescript-eslint/unbound-method': 'off',
// 'jest/unbound-method': 'error',
// },
// },
],
}

27
dlt-database/.gitignore vendored Normal file
View File

@ -0,0 +1,27 @@
.DS_Store
node_modules/
build/
.cache/
npm-debug.log*
yarn-debug.log*
yarn-error.log*
test/unit/coverage
package-lock.json
/.env
/.env.bak
.env.development.local
.env.production.local
# Editor directories and files
.idea
*.suo
*.ntvs*
*.njsproj
*.sln
# coverage folder
coverage/
*~

View File

@ -0,0 +1,9 @@
module.exports = {
semi: false,
printWidth: 100,
singleQuote: true,
trailingComma: "all",
tabWidth: 2,
bracketSpacing: true,
endOfLine: "auto",
};

130
dlt-database/Dockerfile Normal file
View File

@ -0,0 +1,130 @@
##################################################################################
# BASE ###########################################################################
##################################################################################
FROM node:18.7.0-alpine3.16 as base
# ENVs (available in production aswell, can be overwritten by commandline or env file)
## DOCKER_WORKDIR would be a classical ARG, but that is not multi layer persistent - shame
ENV DOCKER_WORKDIR="/app"
## We Cannot do `$(date -u +'%Y-%m-%dT%H:%M:%SZ')` here so we use unix timestamp=0
ENV BUILD_DATE="1970-01-01T00:00:00.00Z"
## We cannot do $(npm run version).${BUILD_NUMBER} here so we default to 0.0.0.0
ENV BUILD_VERSION="0.0.0.0"
## We cannot do `$(git rev-parse --short HEAD)` here so we default to 0000000
ENV BUILD_COMMIT="0000000"
## SET NODE_ENV
ENV NODE_ENV="production"
# Labels
LABEL org.label-schema.build-date="${BUILD_DATE}"
LABEL org.label-schema.name="gradido:database"
LABEL org.label-schema.description="Gradido Database Migration Service"
LABEL org.label-schema.usage="https://github.com/gradido/gradido/blob/master/README.md"
LABEL org.label-schema.url="https://gradido.net"
LABEL org.label-schema.vcs-url="https://github.com/gradido/gradido/tree/master/database"
LABEL org.label-schema.vcs-ref="${BUILD_COMMIT}"
LABEL org.label-schema.vendor="Gradido Community"
LABEL org.label-schema.version="${BUILD_VERSION}"
LABEL org.label-schema.schema-version="1.0"
LABEL maintainer="support@gradido.net"
# Install Additional Software
## install: git
#RUN apk --no-cache add git
## Workdir
RUN mkdir -p ${DOCKER_WORKDIR}
WORKDIR ${DOCKER_WORKDIR}
##################################################################################
# DEVELOPMENT (Connected to the local environment, to reload on demand) ##########
##################################################################################
FROM base as development
# We don't need to copy or build anything since we gonna bind to the
# local filesystem which will need a rebuild anyway
# Run command
# (for development we need to execute npm install since the
# node_modules are on another volume and need updating)
CMD /bin/sh -c "yarn install"
##################################################################################
# BUILD (Does contain all files and is therefore bloated) ########################
##################################################################################
FROM base as build
# Copy everything
COPY . .
# npm install
RUN yarn install --production=false --frozen-lockfile --non-interactive
# npm build
RUN yarn run build
##################################################################################
# TEST UP ########################################################################
##################################################################################
FROM build as test_up
# Run command
CMD /bin/sh -c "yarn install && yarn run dev_up"
##################################################################################
# TEST RESET #####################################################################
##################################################################################
FROM build as test_reset
# Run command
CMD /bin/sh -c "yarn install && yarn run dev_reset"
##################################################################################
# TEST DOWN ######################################################################
##################################################################################
FROM build as test_down
# Run command
CMD /bin/sh -c "yarn install && yarn run dev_down"
##################################################################################
# PRODUCTION (Does contain only "binary"- and static-files to reduce image size) #
##################################################################################
FROM base as production
# Copy "binary"-files from build image
COPY --from=build ${DOCKER_WORKDIR}/build ./build
# We also copy the node_modules express and serve-static for the run script
COPY --from=build ${DOCKER_WORKDIR}/node_modules ./node_modules
# Copy static files
# COPY --from=build ${DOCKER_WORKDIR}/public ./public
# Copy package.json for script definitions (lock file should not be needed)
COPY --from=build ${DOCKER_WORKDIR}/package.json ./package.json
# Copy Mnemonic files
COPY --from=build ${DOCKER_WORKDIR}/src/config/*.txt ./src/config/
# Copy log folder
COPY --from=build ${DOCKER_WORKDIR}/log ./log
# Copy run scripts run/
# COPY --from=build ${DOCKER_WORKDIR}/run ./run
##################################################################################
# PRODUCTION UP ##################################################################
##################################################################################
FROM production as production_up
# Run command
CMD /bin/sh -c "yarn run up"
##################################################################################
# PRODUCTION RESET ###############################################################
##################################################################################
FROM production as production_reset
# Run command
CMD /bin/sh -c "yarn run reset"
##################################################################################
# PRODUCTION DOWN ################################################################
##################################################################################
FROM production as production_down
# Run command
CMD /bin/sh -c "yarn run down"

39
dlt-database/README.md Normal file
View File

@ -0,0 +1,39 @@
# database
## Project setup
```bash
yarn install
```
## Upgrade migrations production
```bash
yarn up
```
## Upgrade migrations development
```bash
yarn dev_up
```
## Downgrade migrations production
```bash
yarn down
```
## Downgrade migrations development
```bash
yarn dev_down
```
## Reset database
```bash
yarn dev_reset
```
Runs all down migrations and after this all up migrations.

View File

@ -0,0 +1,79 @@
import {
Entity,
PrimaryGeneratedColumn,
Column,
CreateDateColumn,
ManyToOne,
JoinColumn,
OneToMany,
ManyToMany,
JoinTable,
} from 'typeorm'
import { User } from './User'
import { Community } from './Community'
import { TransactionRecipe } from './TransactionRecipe'
import { ConfirmedTransaction } from './ConfirmedTransaction'
import { DecimalTransformer } from '../../src/typeorm/DecimalTransformer'
import { Decimal } from 'decimal.js-light'
@Entity('accounts')
export class Account {
@PrimaryGeneratedColumn('increment', { unsigned: true })
id: number
@ManyToOne(() => User, (user) => user.accounts) // Assuming you have a User entity with 'accounts' relation
@JoinColumn({ name: 'user_id' })
user: User
// if user id is null, account belongs to community gmw or auf
@Column({ name: 'user_id', type: 'int', unsigned: true, nullable: true })
userId?: number
@Column({ name: 'derivation_index', type: 'int', unsigned: true })
derivationIndex: number
@Column({ name: 'derive2_pubkey', type: 'binary', length: 32, unique: true })
derive2Pubkey: Buffer
@Column({ type: 'tinyint', unsigned: true })
type: number
@CreateDateColumn({ name: 'created_at', type: 'datetime', default: () => 'CURRENT_TIMESTAMP(3)' })
createdAt: Date
@Column({ name: 'confirmed_at', type: 'datetime', nullable: true })
confirmedAt?: Date
@Column({
type: 'decimal',
precision: 40,
scale: 20,
default: 0,
transformer: DecimalTransformer,
})
balance: Decimal
@Column({
name: 'balance_date',
type: 'datetime',
default: () => 'CURRENT_TIMESTAMP(3)',
})
balanceDate: Date
@ManyToMany(() => Community, (community) => community.communityAccounts)
@JoinTable({
name: 'accounts_communities',
joinColumn: { name: 'account_id', referencedColumnName: 'id' },
inverseJoinColumn: { name: 'community_id', referencedColumnName: 'id' },
})
accountCommunities: Community[]
@OneToMany(() => TransactionRecipe, (recipe) => recipe.signingAccount)
transactionRecipesSigning?: TransactionRecipe[]
@OneToMany(() => TransactionRecipe, (recipe) => recipe.recipientAccount)
transactionRecipesRecipient?: TransactionRecipe[]
@OneToMany(() => ConfirmedTransaction, (transaction) => transaction.account)
confirmedTransactions?: ConfirmedTransaction[]
}

View File

@ -0,0 +1,30 @@
import { Entity, PrimaryGeneratedColumn, Column, ManyToOne, JoinColumn } from 'typeorm'
import { Account } from './Account'
import { Community } from './Community'
@Entity('accounts_communities')
export class AccountCommunity {
@PrimaryGeneratedColumn('increment', { unsigned: true })
id: number
@ManyToOne(() => Account, (account) => account.accountCommunities)
@JoinColumn({ name: 'account_id' })
account: Account
@Column({ name: 'account_id', type: 'int', unsigned: true })
accountId: number
@ManyToOne(() => Community, (community) => community.communityAccounts)
@JoinColumn({ name: 'community_id' })
community: Community
@Column({ name: 'community_id', type: 'int', unsigned: true })
communityId: number
@Column({ name: 'valid_from', type: 'datetime' })
validFrom: Date
@Column({ name: 'valid_to', type: 'datetime', nullable: true })
validTo?: Date
}

View File

@ -0,0 +1,68 @@
import {
Entity,
PrimaryGeneratedColumn,
Column,
CreateDateColumn,
JoinColumn,
OneToOne,
OneToMany,
ManyToMany,
JoinTable,
} from 'typeorm'
import { Account } from './Account'
import { TransactionRecipe } from './TransactionRecipe'
@Entity('communities')
export class Community {
@PrimaryGeneratedColumn('increment', { unsigned: true })
id: number
@Column({ name: 'iota_topic', collation: 'utf8mb4_unicode_ci' })
iotaTopic: string
@Column({ name: 'root_pubkey', type: 'binary', length: 32, unique: true })
rootPubkey: Buffer
@Column({ name: 'root_privkey', type: 'binary', length: 32, nullable: true })
rootPrivkey?: Buffer
@Column({ name: 'root_chaincode', type: 'binary', length: 32, nullable: true })
rootChaincode?: Buffer
@Column({ type: 'tinyint', default: true })
foreign: boolean
@Column({ name: 'gmw_account_id', type: 'int', unsigned: true, nullable: true })
gmwAccountId?: number
@OneToOne(() => Account)
@JoinColumn({ name: 'gmw_account_id' })
gmwAccount?: Account
@Column({ name: 'auf_account_id', type: 'int', unsigned: true, nullable: true })
aufAccountId?: number
@OneToOne(() => Account)
@JoinColumn({ name: 'auf_account_id' })
aufAccount?: Account
@CreateDateColumn({ name: 'created_at', type: 'datetime', default: () => 'CURRENT_TIMESTAMP(3)' })
createdAt: Date
@Column({ name: 'confirmed_at', type: 'datetime', nullable: true })
confirmedAt?: Date
@ManyToMany(() => Account, (account) => account.accountCommunities)
@JoinTable({
name: 'accounts_communities',
joinColumn: { name: 'community_id', referencedColumnName: 'id' },
inverseJoinColumn: { name: 'account_id', referencedColumnName: 'id' },
})
communityAccounts: Account[]
@OneToMany(() => TransactionRecipe, (recipe) => recipe.senderCommunity)
transactionRecipesSender?: TransactionRecipe[]
@OneToMany(() => TransactionRecipe, (recipe) => recipe.recipientCommunity)
transactionRecipesRecipient?: TransactionRecipe[]
}

View File

@ -0,0 +1,49 @@
import { Entity, PrimaryGeneratedColumn, Column, ManyToOne, JoinColumn, OneToOne } from 'typeorm'
import { Decimal } from 'decimal.js-light'
import { DecimalTransformer } from '../../src/typeorm/DecimalTransformer'
import { Account } from './Account'
import { TransactionRecipe } from './TransactionRecipe'
@Entity('confirmed_transactions')
export class ConfirmedTransaction {
@PrimaryGeneratedColumn('increment', { unsigned: true, type: 'bigint' })
id: number
@OneToOne(() => TransactionRecipe, (recipe) => recipe.confirmedTransaction)
@JoinColumn({ name: 'transaction_recipe_id' })
transactionRecipe: TransactionRecipe
@Column({ name: 'transaction_recipe_id', type: 'int', unsigned: true })
transactionRecipeId: number
@Column({ type: 'bigint' })
nr: number
@Column({ type: 'binary', length: 48 })
runningHash: Buffer
@ManyToOne(() => Account, (account) => account.confirmedTransactions)
@JoinColumn({ name: 'account_id' })
account: Account
@Column({ name: 'account_id', type: 'int', unsigned: true })
accountId: number
@Column({
name: 'account_balance',
type: 'decimal',
precision: 40,
scale: 20,
nullable: false,
default: 0,
transformer: DecimalTransformer,
})
accountBalance: Decimal
@Column({ name: 'iota_milestone', type: 'bigint' })
iotaMilestone: number
@Column({ name: 'confirmed_at', type: 'datetime' })
confirmedAt: Date
}

View File

@ -0,0 +1,10 @@
import { Entity, PrimaryGeneratedColumn, Column } from 'typeorm'
@Entity('invalid_transactions')
export class InvalidTransaction {
@PrimaryGeneratedColumn('increment', { unsigned: true, type: 'bigint' })
id: number
@Column({ name: 'iota_message_id', type: 'binary', length: 32 })
iotaMessageId: Buffer
}

View File

@ -0,0 +1,80 @@
import {
Entity,
PrimaryGeneratedColumn,
Column,
CreateDateColumn,
ManyToOne,
OneToOne,
JoinColumn,
} from 'typeorm'
import { Decimal } from 'decimal.js-light'
import { DecimalTransformer } from '../../src/typeorm/DecimalTransformer'
import { Account } from './Account'
import { Community } from './Community'
import { ConfirmedTransaction } from './ConfirmedTransaction'
@Entity('transaction_recipes')
export class TransactionRecipe {
@PrimaryGeneratedColumn('increment', { unsigned: true, type: 'bigint' })
id: number
@Column({ name: 'iota_message_id', type: 'binary', length: 32, nullable: true })
iotaMessageId?: Buffer
// if transaction has a sender than it is also the sender account
@ManyToOne(() => Account, (account) => account.transactionRecipesSigning)
@JoinColumn({ name: 'signing_account_id' })
signingAccount: Account
@Column({ name: 'signing_account_id', type: 'int', unsigned: true })
signingAccountId: number
@ManyToOne(() => Account, (account) => account.transactionRecipesRecipient)
@JoinColumn({ name: 'recipient_account_id' })
recipientAccount?: Account
@Column({ name: 'recipient_account_id', type: 'int', unsigned: true, nullable: true })
recipientAccountId?: number
@ManyToOne(() => Community, (community) => community.transactionRecipesSender)
@JoinColumn({ name: 'sender_community_id' })
senderCommunity: Community
@Column({ name: 'sender_community_id', type: 'int', unsigned: true })
senderCommunityId: number
@ManyToOne(() => Community, (community) => community.transactionRecipesRecipient)
@JoinColumn({ name: 'recipient_community_id' })
recipientCommunity?: Community
@Column({ name: 'sender_community_id', type: 'int', unsigned: true, nullable: true })
recipientCommunityId?: number
@Column({
type: 'decimal',
precision: 40,
scale: 20,
nullable: true,
transformer: DecimalTransformer,
})
amount?: Decimal
@Column({ type: 'tinyint' })
type: number
@CreateDateColumn({ name: 'created_at', type: 'datetime' })
createdAt: Date
@Column({ name: 'body_bytes', type: 'blob' })
bodyBytes: Buffer
@Column({ type: 'binary', length: 64 })
signature: Buffer
@Column({ name: 'protocol_version', type: 'int', default: 1 })
protocolVersion: number
@OneToOne(() => ConfirmedTransaction, (transaction) => transaction.transactionRecipe)
confirmedTransaction?: ConfirmedTransaction
}

View File

@ -0,0 +1,46 @@
import {
BaseEntity,
Entity,
PrimaryGeneratedColumn,
Column,
OneToMany,
JoinColumn,
CreateDateColumn,
} from 'typeorm'
import { Account } from './Account'
@Entity('users', { engine: 'InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci' })
export class User extends BaseEntity {
@PrimaryGeneratedColumn('increment', { unsigned: true })
id: number
@Column({
name: 'gradido_id',
length: 36,
nullable: true,
collation: 'utf8mb4_unicode_ci',
})
gradidoID?: string
@Column({ name: 'derive1_pubkey', type: 'binary', length: 32, unique: true })
derive1Pubkey: Buffer
@CreateDateColumn({
name: 'created_at',
type: 'datetime',
default: () => 'CURRENT_TIMESTAMP(3)',
})
createdAt: Date
@Column({
name: 'confirmed_at',
type: 'datetime',
nullable: true,
})
confirmedAt?: Date
@OneToMany(() => Account, (account) => account.user)
@JoinColumn({ name: 'user_id' })
accounts?: Account[]
}

View File

@ -0,0 +1 @@
export { Account } from './0001-init_db/Account'

View File

@ -0,0 +1 @@
export { AccountCommunity } from './0001-init_db/AccountCommunity'

View File

@ -0,0 +1 @@
export { Community } from './0001-init_db/Community'

View File

@ -0,0 +1 @@
export { ConfirmedTransaction } from './0001-init_db/ConfirmedTransaction'

View File

@ -0,0 +1 @@
export { InvalidTransaction } from './0001-init_db/InvalidTransaction'

View File

@ -0,0 +1 @@
export { TransactionRecipe } from './0001-init_db/TransactionRecipe'

View File

@ -0,0 +1 @@
export { User } from './0001-init_db/User'

2
dlt-database/log/.gitignore vendored Normal file
View File

@ -0,0 +1,2 @@
*
!.gitignore

View File

@ -0,0 +1,130 @@
/* FIRST MIGRATION
*
* This migration is special since it takes into account that
* the database can be setup already but also may not be.
* Therefore you will find all `CREATE TABLE` statements with
* a `IF NOT EXISTS`, all `INSERT` with an `IGNORE` and in the
* downgrade function all `DROP TABLE` with a `IF EXISTS`.
* This ensures compatibility for existing or non-existing
* databases.
*/
/* eslint-disable @typescript-eslint/explicit-module-boundary-types */
/* eslint-disable @typescript-eslint/no-explicit-any */
export async function upgrade(queryFn: (query: string, values?: any[]) => Promise<Array<any>>) {
// write upgrade logic as parameter of queryFn
await queryFn(`
CREATE TABLE IF NOT EXISTS \`users\` (
\`id\` int(10) unsigned NOT NULL AUTO_INCREMENT,
\`gradido_id\` char(36) DEFAULT NULL,
\`derive1_pubkey\` binary(32) NOT NULL,
\`created_at\` datetime(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3),
\`confirmed_at\` datetime(3) DEFAULT NULL,
PRIMARY KEY (\`id\`),
INDEX \`gradido_id\` (\`gradido_id\`),
UNIQUE KEY \`pubkey\` (\`pubkey\`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;`)
await queryFn(`
CREATE TABLE IF NOT EXISTS \`accounts\` (
\`id\` int(10) unsigned NOT NULL AUTO_INCREMENT,
\`user_id\` int(10) unsigned DEFAULT NULL,
\`derivation_index\` int(10) unsigned NOT NULL,
\`derive2_pubkey\` binary(32) NOT NULL,
\`type\` tinyint unsigned NOT NULL,
\`created_at\` datetime(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3),
\`confirmed_at\` datetime(3) DEFAULT NULL,
\`balance\` decimal(40,20) NOT NULL DEFAULT 0,
\`balance_date\` datetime(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3),
PRIMARY KEY (\`id\`),
UNIQUE KEY \`pubkey\` (\`pubkey\`),
FOREIGN KEY (\`user_id\`) REFERENCES users(id)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
`)
await queryFn(`
CREATE TABLE IF NOT EXISTS \`communities\` (
\`id\` int(10) unsigned NOT NULL AUTO_INCREMENT,
\`iota_topic\` varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL,
\`root_pubkey\` binary(32) NOT NULL,
\`root_privkey\` binary(32) DEFAULT NULL,
\`root_chaincode\` binary(32) DEFAULT NULL,
\`foreign\` tinyint(4) NOT NULL DEFAULT true,
\`gmw_account_id\` int(10) unsigned DEFAULT NULL,
\`auf_account_id\` int(10) unsigned DEFAULT NULL,
\`created_at\` datetime(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3),
\`confirmed_at\` datetime(3) DEFAULT NULL,
PRIMARY KEY (\`id\`),
UNIQUE KEY \`pubkey\` (\`pubkey\`),
FOREIGN KEY (\`gmw_account_id\`) REFERENCES accounts(id),
FOREIGN KEY (\`auf_account_id\`) REFERENCES accounts(id)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;`)
await queryFn(`
CREATE TABLE IF NOT EXISTS \`accounts_communities\` (
\`id\` int(10) unsigned NOT NULL AUTO_INCREMENT,
\`account_id\` int(10) unsigned NOT NULL,
\`community_id\` int(10) unsigned NOT NULL,
\`valid_from\` datetime(3) NOT NULL,
\`valid_to\` datetime(3) DEFAULT NULL,
PRIMARY KEY (\`id\`),
FOREIGN KEY (\`account_id\`) REFERENCES accounts(id),
FOREIGN KEY (\`community_id\`) REFERENCES communities(id)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;`)
await queryFn(`
CREATE TABLE IF NOT EXISTS \`transaction_recipes\` (
\`id\` bigint unsigned NOT NULL AUTO_INCREMENT,
\`iota_message_id\` binary(32) DEFAULT NULL,
\`signing_account_id\` int(10) unsigned NOT NULL,
\`recipient_account_id\` int(10) unsigned DEFAULT NULL,
\`sender_community_id\` int(10) unsigned NOT NULL,
\`recipient_community_id\` int(10) unsigned DEFAULT NULL,
\`amount\` decimal(40,20) DEFAULT NULL,
\`type\` tinyint unsigned NOT NULL,
\`created_at\` datetime(3) NOT NULL,
\`body_bytes\` BLOB NOT NULL,
\`signature\` binary(64) NOT NULL,
\`protocol_version\` int(10) NOT NULL DEFAULT 1,
PRIMARY KEY (\`id\`),
FOREIGN KEY (\`signing_account_id\`) REFERENCES accounts(id),
FOREIGN KEY (\`recipient_account_id\`) REFERENCES accounts(id),
FOREIGN KEY (\`sender_community_id\`) REFERENCES communities(id),
FOREIGN KEY (\`recipient_community_id\`) REFERENCES communities(id)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;`)
await queryFn(`
CREATE TABLE IF NOT EXISTS \`confirmed_transactions\` (
\`id\` bigint unsigned NOT NULL AUTO_INCREMENT,
\`transaction_recipe_id\` bigint unsigned NOT NULL,
\`nr\` bigint unsigned NOT NULL,
\`running_hash\` binary(48) NOT NULL,
\`account_id\` int(10) unsigned NOT NULL,
\`account_balance\` decimal(40,20) NOT NULL DEFAULT 0,
\`iota_milestone\` bigint NOT NULL,
\`confirmed_at\` datetime(3) NOT NULL,
PRIMARY KEY (\`id\`),
FOREIGN KEY (\`transaction_recipe_id\`) REFERENCES transaction_recipes(id),
FOREIGN KEY (\`account_id\`) REFERENCES accounts(id)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;`)
await queryFn(`
CREATE TABLE IF NOT EXISTS \`invalid_transactions\` (
\`id\` bigint unsigned NOT NULL AUTO_INCREMENT,
\`iota_message_id\` binary(32) NOT NULL,
PRIMARY KEY (\`id\`),
INDEX (\`iota_message_id\`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;`)
}
export async function downgrade(queryFn: (query: string, values?: any[]) => Promise<Array<any>>) {
// write downgrade logic as parameter of queryFn
await queryFn(`DROP TABLE IF EXISTS \`users\`;`)
await queryFn(`DROP TABLE IF EXISTS \`accounts\`;`)
await queryFn(`DROP TABLE IF EXISTS \`accounts_communities\`;`)
await queryFn(`DROP TABLE IF EXISTS \`transaction_recipes\`;`)
await queryFn(`DROP TABLE IF EXISTS \`confirmed_transactions\`;`)
await queryFn(`DROP TABLE IF EXISTS \`community\`;`)
await queryFn(`DROP TABLE IF EXISTS \`invalid_transactions\`;`)
}

55
dlt-database/package.json Normal file
View File

@ -0,0 +1,55 @@
{
"name": "gradido-database",
"version": "1.23.2",
"description": "Gradido Database Tool to execute database migrations",
"main": "src/index.ts",
"repository": "https://github.com/gradido/gradido/database",
"author": "Ulf Gebhardt",
"license": "Apache-2.0",
"private": false,
"scripts": {
"build": "tsc --build",
"clean": "tsc --build --clean",
"up": "cross-env TZ=UTC node build/src/index.js up",
"down": "cross-env TZ=UTC node build/src/index.js down",
"reset": "cross-env TZ=UTC node build/src/index.js reset",
"dev_up": "cross-env TZ=UTC ts-node src/index.ts up",
"dev_down": "cross-env TZ=UTC ts-node src/index.ts down",
"dev_reset": "cross-env TZ=UTC ts-node src/index.ts reset",
"lint": "eslint --max-warnings=0 --ext .js,.ts ."
},
"devDependencies": {
"@eslint-community/eslint-plugin-eslint-comments": "^3.2.1",
"@types/faker": "^5.5.9",
"@types/node": "^16.10.3",
"@typescript-eslint/eslint-plugin": "^5.57.1",
"@typescript-eslint/parser": "^5.57.1",
"eslint": "^8.37.0",
"eslint-config-prettier": "^8.8.0",
"eslint-config-standard": "^17.0.0",
"eslint-import-resolver-typescript": "^3.5.4",
"eslint-plugin-import": "^2.27.5",
"eslint-plugin-n": "^15.7.0",
"eslint-plugin-prettier": "^4.2.1",
"eslint-plugin-promise": "^6.1.1",
"eslint-plugin-security": "^1.7.1",
"prettier": "^2.8.7",
"ts-node": "^10.2.1",
"typescript": "^4.3.5"
},
"dependencies": {
"@types/uuid": "^8.3.4",
"cross-env": "^7.0.3",
"crypto": "^1.0.1",
"decimal.js-light": "^2.5.1",
"dotenv": "^10.0.0",
"mysql2": "^2.3.0",
"reflect-metadata": "^0.1.13",
"ts-mysql-migrate": "^1.0.2",
"typeorm": "^0.3.16",
"uuid": "^8.3.2"
},
"engines": {
"node": ">=14"
}
}

View File

@ -0,0 +1,39 @@
/* eslint-disable n/no-process-env */
import dotenv from 'dotenv'
dotenv.config()
const constants = {
CONFIG_VERSION: {
DEFAULT: 'DEFAULT',
EXPECTED: 'v1.2022-08-22',
CURRENT: '',
},
}
const database = {
DB_HOST: process.env.DB_HOST || 'localhost',
DB_PORT: process.env.DB_PORT ? parseInt(process.env.DB_PORT) : 3306,
DB_USER: process.env.DB_USER || 'root',
DB_PASSWORD: process.env.DB_PASSWORD || '',
DB_DATABASE: process.env.DB_DATABASE || 'gradido_dlt',
}
const migrations = {
MIGRATIONS_TABLE: process.env.MIGRATIONS_TABLE || 'migrations',
}
// Check config version
constants.CONFIG_VERSION.CURRENT = process.env.CONFIG_VERSION || constants.CONFIG_VERSION.DEFAULT
if (
![constants.CONFIG_VERSION.EXPECTED, constants.CONFIG_VERSION.DEFAULT].includes(
constants.CONFIG_VERSION.CURRENT,
)
) {
throw new Error(
`Fatal: Config Version incorrect - expected "${constants.CONFIG_VERSION.EXPECTED}" or "${constants.CONFIG_VERSION.DEFAULT}", but found "${constants.CONFIG_VERSION.CURRENT}"`,
)
}
export const CONFIG = { ...constants, ...database, ...migrations }

56
dlt-database/src/index.ts Normal file
View File

@ -0,0 +1,56 @@
import { createDatabase } from './prepare'
import { CONFIG } from './config'
import { createPool } from 'mysql'
import { Migration } from 'ts-mysql-migrate'
import path from 'path'
const run = async (command: string) => {
// Database actions not supported by our migration library
await createDatabase()
// Initialize Migrations
const pool = createPool({
host: CONFIG.DB_HOST,
port: CONFIG.DB_PORT,
user: CONFIG.DB_USER,
password: CONFIG.DB_PASSWORD,
database: CONFIG.DB_DATABASE,
})
const migration = new Migration({
conn: pool,
tableName: CONFIG.MIGRATIONS_TABLE,
silent: true,
dir: path.join(__dirname, '..', 'migrations'),
})
await migration.initialize()
// Execute command
switch (command) {
case 'up':
await migration.up() // use for upgrade script
break
case 'down':
await migration.down() // use for downgrade script
break
case 'reset':
// TODO protect from production
await migration.reset()
break
default:
throw new Error(`Unsupported command ${command}`)
}
// Terminate connections gracefully
pool.end()
}
run(process.argv[2])
.catch((err) => {
// eslint-disable-next-line no-console
console.log(err)
process.exit(1)
})
.then(() => {
process.exit()
})

View File

@ -0,0 +1,22 @@
import { createConnection } from 'mysql2/promise'
import { CONFIG } from './config'
export const createDatabase = async (): Promise<void> => {
const con = await createConnection({
host: CONFIG.DB_HOST,
port: CONFIG.DB_PORT,
user: CONFIG.DB_USER,
password: CONFIG.DB_PASSWORD,
})
await con.connect()
// Create Database `gradido_dlt`
await con.query(`
CREATE DATABASE IF NOT EXISTS ${CONFIG.DB_DATABASE}
DEFAULT CHARACTER SET utf8mb4
DEFAULT COLLATE utf8mb4_unicode_ci;`)
await con.end()
}

View File

@ -0,0 +1 @@
export * from 'typeorm'

View File

@ -0,0 +1,19 @@
import { Decimal } from 'decimal.js-light'
import { ValueTransformer } from 'typeorm'
Decimal.set({
precision: 25,
rounding: Decimal.ROUND_HALF_UP,
})
export const DecimalTransformer: ValueTransformer = {
/**
* Used to marshal Decimal when writing to the database.
*/
to: (decimal: Decimal | null): string | null => (decimal ? decimal.toString() : null),
/**
* Used to unmarshal Decimal when reading from the database.
*/
from: (decimal: string | null): Decimal | null => (decimal ? new Decimal(decimal) : null),
}

View File

@ -0,0 +1,73 @@
{
"compilerOptions": {
/* Visit https://aka.ms/tsconfig.json to read more about this file */
/* Basic Options */
// "incremental": true, /* Enable incremental compilation */
"target": "es6", /* Specify ECMAScript target version: 'ES3' (default), 'ES5', 'ES2015', 'ES2016', 'ES2017', 'ES2018', 'ES2019', 'ES2020', 'ES2021', or 'ESNEXT'. */
"module": "commonjs", /* Specify module code generation: 'none', 'commonjs', 'amd', 'system', 'umd', 'es2015', 'es2020', or 'ESNext'. */
// "lib": [], /* Specify library files to be included in the compilation. */
// "allowJs": true, /* Allow javascript files to be compiled. */
// "checkJs": true, /* Report errors in .js files. */
// "jsx": "preserve", /* Specify JSX code generation: 'preserve', 'react-native', 'react', 'react-jsx' or 'react-jsxdev'. */
"declaration": true, /* Generates corresponding '.d.ts' file. */
"declarationMap": true, /* Generates a sourcemap for each corresponding '.d.ts' file. */
// "sourceMap": true, /* Generates corresponding '.map' file. */
// "outFile": "./build/outfile.js", /* Concatenate and emit output to single file. */
"outDir": "./build", /* Redirect output structure to the directory. */
// "rootDir": "./", /* Specify the root directory of input files. Use to control the output directory structure with --outDir. */
"composite": true, /* Enable project compilation */
// "tsBuildInfoFile": "./", /* Specify file to store incremental compilation information */
// "removeComments": true, /* Do not emit comments to output. */
// "noEmit": true, /* Do not emit outputs. */
// "importHelpers": true, /* Import emit helpers from 'tslib'. */
// "downlevelIteration": true, /* Provide full support for iterables in 'for-of', spread, and destructuring when targeting 'ES5' or 'ES3'. */
// "isolatedModules": true, /* Transpile each file as a separate module (similar to 'ts.transpileModule'). */
/* Strict Type-Checking Options */
"strict": true, /* Enable all strict type-checking options. */
// "noImplicitAny": true, /* Raise error on expressions and declarations with an implied 'any' type. */
// "strictNullChecks": true, /* Enable strict null checks. */
// "strictFunctionTypes": true, /* Enable strict checking of function types. */
// "strictBindCallApply": true, /* Enable strict 'bind', 'call', and 'apply' methods on functions. */
"strictPropertyInitialization": false, /* Enable strict checking of property initialization in classes. */
// "noImplicitThis": true, /* Raise error on 'this' expressions with an implied 'any' type. */
// "alwaysStrict": true, /* Parse in strict mode and emit "use strict" for each source file. */
/* Additional Checks */
// "noUnusedLocals": true, /* Report errors on unused locals. */
// "noUnusedParameters": true, /* Report errors on unused parameters. */
// "noImplicitReturns": true, /* Report error when not all code paths in function return a value. */
// "noFallthroughCasesInSwitch": true, /* Report errors for fallthrough cases in switch statement. */
// "noUncheckedIndexedAccess": true, /* Include 'undefined' in index signature results */
// "noImplicitOverride": true, /* Ensure overriding members in derived classes are marked with an 'override' modifier. */
// "noPropertyAccessFromIndexSignature": true, /* Require undeclared properties from index signatures to use element accesses. */
/* Module Resolution Options */
// "moduleResolution": "node", /* Specify module resolution strategy: 'node' (Node.js) or 'classic' (TypeScript pre-1.6). */
// "baseUrl": "./", /* Base directory to resolve non-absolute module names. */
// "paths": {}, /* A series of entries which re-map imports to lookup locations relative to the 'baseUrl'. */
// "rootDirs": [".", "../database"], /* List of root folders whose combined content represents the structure of the project at runtime. */
// "typeRoots": [], /* List of folders to include type definitions from. */
// "types": [], /* Type declaration files to be included in compilation. */
// "allowSyntheticDefaultImports": true, /* Allow default imports from modules with no default export. This does not affect code emit, just typechecking. */
"esModuleInterop": true, /* Enables emit interoperability between CommonJS and ES Modules via creation of namespace objects for all imports. Implies 'allowSyntheticDefaultImports'. */
// "preserveSymlinks": true, /* Do not resolve the real path of symlinks. */
// "allowUmdGlobalAccess": true, /* Allow accessing UMD globals from modules. */
/* Source Map Options */
// "sourceRoot": "", /* Specify the location where debugger should locate TypeScript files instead of source locations. */
// "mapRoot": "", /* Specify the location where debugger should locate map files instead of generated locations. */
// "inlineSourceMap": true, /* Emit a single file with source maps instead of having a separate file. */
// "inlineSources": true, /* Emit the source alongside the sourcemaps within a single file; requires '--inlineSourceMap' or '--sourceMap' to be set. */
/* Experimental Options */
"experimentalDecorators": true, /* Enables experimental support for ES7 decorators. */
"emitDecoratorMetadata": true, /* Enables experimental support for emitting type metadata for decorators. */
/* Advanced Options */
"skipLibCheck": true, /* Skip type checking of declaration files. */
"forceConsistentCasingInFileNames": true /* Disallow inconsistently-cased references to the same file. */
},
"references": [] /* Any project that is referenced must itself have a `references` array (which may be empty). */
}

2573
dlt-database/yarn.lock Normal file

File diff suppressed because it is too large Load Diff

View File

@ -35,6 +35,12 @@ services:
########################################################
database:
platform: linux/amd64
########################################################
# DLT-DATABASE #############################################
########################################################
dlt-database:
platform: linux/amd64
#########################################################
## NGINX ################################################

View File

@ -149,6 +149,28 @@ services:
# bind the local folder to the docker to allow live reload
- ./database:/app
########################################################
# DLT-DATABASE ##############################################
########################################################
dlt-database:
# we always run on production here since else the service lingers
# feel free to change this behaviour if it seems useful
# Due to problems with the volume caching the built files
# we changed this to test build. This keeps the service running.
# name the image so that it cannot be found in a DockerHub repository, otherwise it will not be built locally from the 'dockerfile' but pulled from there
image: gradido/dlt-database:local-test_up
build:
target: test_up
environment:
- NODE_ENV="development"
volumes:
# This makes sure the docker container has its own node modules.
# Therefore it is possible to have a different node version on the host machine
- dlt-database_node_modules:/app/node_modules
- dlt-database_build:/app/build
# bind the local folder to the docker to allow live reload
- ./dlt-database:/app
#########################################################
## MARIADB ##############################################
#########################################################
@ -203,4 +225,6 @@ volumes:
federation_database_node_modules:
federation_database_build:
database_node_modules:
database_build:
database_build:
dlt-database_node_modules:
dlt-database_build:

View File

@ -77,6 +77,18 @@ services:
- NODE_ENV="test"
# restart: always # this is very dangerous, but worth a test for the delayed mariadb startup at first run
########################################################
# DLT-DATABASE #############################################
########################################################
dlt-database:
image: gradido/dlt-database:test_up
build:
context: ./dlt-database
target: test_up
environment:
- NODE_ENV="test"
# restart: always # this is very dangerous, but worth a test for the delayed mariadb startup at first run
#########################################################
## MARIADB ##############################################
#########################################################

View File

@ -239,6 +239,32 @@ services:
# Application only envs
#env_file:
# - ./frontend/.env
########################################################
# DLT-DATABASE #############################################
########################################################
dlt-database:
# name the image so that it cannot be found in a DockerHub repository, otherwise it will not be built locally from the 'dockerfile' but pulled from there
image: gradido/dlt-database:local-production_up
build:
context: ./dlt-database
target: production_up
depends_on:
- mariadb
networks:
- internal-net
- external-net # this is required to fetch the packages
environment:
# Envs used in Dockerfile
# - DOCKER_WORKDIR="/app"
- BUILD_DATE
- BUILD_VERSION
- BUILD_COMMIT
- NODE_ENV="production"
- DB_HOST=mariadb
# Application only envs
#env_file:
# - ./frontend/.env
#########################################################
## NGINX ################################################

View File

@ -0,0 +1,4 @@
# DLT Connector Database
![Diagram](img/dlt-diagramm.png)
[Link zum PDF](img/dlt-diagramm.pdf)

View File

@ -0,0 +1,26 @@
# Key Derivation
The DLT connector uses key derivation to derive keys for each user in the community account with a master key.
![Bip32](https://github.com/bitcoin/bips/blob/master/bip-0032.mediawiki)
## user accounts
The Path for key derivation contain the gradido id, and derivation index of account
Gradido ID: 03857ac1-9cc2-483e-8a91-e5b10f5b8d16
Derivation Index: 1
Key derivation Path:
```
m/03857ac1'/9cc2'/483e'/8a91'/e5b10f5b8d16'/1
```
## gmw and auf accounts
For gmw and auf accounts two special Paths used:
gmw account => account nr 1
```
m/1'
```
auf account => account nr 2
```
m/2'
```

File diff suppressed because one or more lines are too long

Binary file not shown.

After

Width:  |  Height:  |  Size: 146 KiB

View File

@ -0,0 +1,67 @@
# DLT-Connector Overview
What the DLT Connector does roughly.
- create transaction
- receive transactions from iota
- warmup, load missing transactions from iota on startup
## create transaction
- called from backend with transaction details
- sender user | signing user (in case of contribution)
- uuid
- account nr | default = 1
- community uuid
- recipient user
- uuid
- account nr | default = 1
- community uuid
- amount
- memo
- type
- createdAt
- load or create accounts
- compose protobuf transaction
- derive correct private key for signing account and sign transaction
- validate transaction
- write transaction into transaction_recipes table
- send transaction to iota
- update iota message id in transaction_recipes table
- return to backend with iota message id
## receive transactions from iota
- listen on all registered community topics on iota
- make sure we have everything from milestone
- sort per community by iota milestone, createdAt ASC
- per message:
- deserialize to protobuf object
- validate
- if valid:
- calculate running_hash and account_balance
- write into confirmed_transactions
- if invalid:
- write into invalid_transactions
- send request to backend with final transaction data for comparison
- sender user | signing user (in case of contribution)
- uuid
- account nr
- community uuid
- recipient user
- uuid
- account nr
- community uuid
- amount
- memo
- createdAt
- confirmedAt
- type
- iota message id
- balance for createdAt
- decay for createdAt
## warmup, load missing transactions from iota or Chronicle on startup
- read all iota message ids from all registered topics
- check if already exist
- load details for not existing message ids
- do for every message [receive](#receive-transactions-from-iota)

View File

@ -56,3 +56,23 @@ export default {
},
}
</script>
<style>
.community-switch > div,
.community-switch ul.dropdown-menu {
width: 100%;
}
.community-switch > div > button {
border-radius: 17px;
height: 50px;
text-align: left;
}
.community-switch .dropdown-toggle::after {
float: right;
top: 50%;
transform: translateY(-50%);
position: relative;
}
.community-switch ul li:first-child {
display: none;
}
</style>