mirror of
https://github.com/IT4Change/gradido.git
synced 2026-02-06 09:56:05 +00:00
add new send to iota interaction
This commit is contained in:
parent
68cb7b368b
commit
2727b6ebe9
@ -21,10 +21,12 @@ TYPEORM_LOGGING_RELATIVE_PATH=typeorm.backend.log
|
||||
# DLT-Connector
|
||||
DLT_CONNECTOR_PORT=6010
|
||||
|
||||
# Gradido Node Server URL
|
||||
NODE_SERVER_URL=http://localhost:8340
|
||||
|
||||
# Gradido Blockchain
|
||||
GRADIDO_BLOCKCHAIN_CRYPTO_APP_SECRET=21ffbbc616fe
|
||||
GRADIDO_BLOCKCHAIN_SERVER_CRYPTO_KEY=a51ef8ac7ef1abf162fb7a65261acd7a
|
||||
GRADIDO_BLOCKCHAIN_PRIVATE_KEY_ENCRYPTION_PASSWORD=YourPassword
|
||||
|
||||
# Route to Backend
|
||||
BACKEND_SERVER_URL=http://localhost:4000
|
||||
|
||||
@ -19,10 +19,12 @@ TYPEORM_LOGGING_RELATIVE_PATH=typeorm.backend.log
|
||||
# DLT-Connector
|
||||
DLT_CONNECTOR_PORT=$DLT_CONNECTOR_PORT
|
||||
|
||||
# Gradido Node Server URL
|
||||
NODE_SERVER_URL=$NODE_SERVER_URL
|
||||
|
||||
# Gradido Blockchain
|
||||
GRADIDO_BLOCKCHAIN_CRYPTO_APP_SECRET=$GRADIDO_BLOCKCHAIN_CRYPTO_APP_SECRET
|
||||
GRADIDO_BLOCKCHAIN_SERVER_CRYPTO_KEY=$GRADIDO_BLOCKCHAIN_SERVER_CRYPTO_KEY
|
||||
GRADIDO_BLOCKCHAIN_PRIVATE_KEY_ENCRYPTION_PASSWORD=$GRADIDO_BLOCKCHAIN_PRIVATE_KEY_ENCRYPTION_PASSWORD
|
||||
|
||||
# Route to Backend
|
||||
BACKEND_SERVER_URL=http://localhost:4000
|
||||
@ -33,6 +33,7 @@
|
||||
"graphql-scalars": "^1.22.2",
|
||||
"helmet": "^7.1.0",
|
||||
"jose": "^5.2.2",
|
||||
"jsonrpc-ts-client": "^0.2.3",
|
||||
"log4js": "^6.7.1",
|
||||
"nodemon": "^2.0.20",
|
||||
"reflect-metadata": "^0.1.13",
|
||||
|
||||
124
dlt-connector/src/client/GradidoNode.ts
Normal file
124
dlt-connector/src/client/GradidoNode.ts
Normal file
@ -0,0 +1,124 @@
|
||||
/* eslint-disable camelcase */
|
||||
import { AddressType, ConfirmedTransaction, stringToAddressType } from 'gradido-blockchain-js'
|
||||
import JsonRpcClient from 'jsonrpc-ts-client'
|
||||
import { JsonRpcEitherResponse } from 'jsonrpc-ts-client/dist/types/utils/jsonrpc'
|
||||
|
||||
import { CONFIG } from '@/config'
|
||||
import { logger } from '@/logging/logger'
|
||||
import { LogError } from '@/server/LogError'
|
||||
import { confirmedTransactionFromBase64, getEnumValue } from '@/utils/typeConverter'
|
||||
|
||||
const client = new JsonRpcClient({
|
||||
url: CONFIG.NODE_SERVER_URL,
|
||||
})
|
||||
/*
|
||||
enum JsonRPCErrorCodes {
|
||||
NONE = 0,
|
||||
GRADIDO_NODE_ERROR = -10000,
|
||||
UNKNOWN_GROUP = -10001,
|
||||
NOT_IMPLEMENTED = -10002,
|
||||
TRANSACTION_NOT_FOUND = -10003,
|
||||
// default errors from json rpc standard: https://www.jsonrpc.org/specification
|
||||
// -32700 Parse error Invalid JSON was received by the server.
|
||||
PARSE_ERROR = -32700,
|
||||
// -32600 Invalid Request The JSON sent is not a valid Request object.
|
||||
INVALID_REQUEST = -32600,
|
||||
// -32601 Method not found The method does not exist / is not available.
|
||||
METHODE_NOT_FOUND = -32601,
|
||||
// -32602 Invalid params Invalid method parameter(s).
|
||||
INVALID_PARAMS = -32602,
|
||||
// -32603 Internal error Internal JSON - RPC error.
|
||||
INTERNAL_ERROR = -32603,
|
||||
// -32000 to -32099 Server error Reserved for implementation-defined server-errors.
|
||||
}
|
||||
*/
|
||||
|
||||
interface ConfirmedTransactionList {
|
||||
transactions: string[]
|
||||
timeUsed: string
|
||||
}
|
||||
|
||||
interface ConfirmedTransactionResponse {
|
||||
transaction: string
|
||||
timeUsed: string
|
||||
}
|
||||
|
||||
interface AddressTypeResult {
|
||||
addressType: string
|
||||
}
|
||||
|
||||
function resolveResponse<T, R>(response: JsonRpcEitherResponse<T>, onSuccess: (result: T) => R): R {
|
||||
if (response.isSuccess()) {
|
||||
return onSuccess(response.result)
|
||||
} else if (response.isError()) {
|
||||
throw new LogError('error by json rpc request to gradido node server', response.error)
|
||||
}
|
||||
throw new LogError('no success and no error', response)
|
||||
}
|
||||
|
||||
async function getTransactions(
|
||||
fromTransactionId: number,
|
||||
maxResultCount: number,
|
||||
iotaTopic: string,
|
||||
): Promise<ConfirmedTransaction[]> {
|
||||
const parameter = {
|
||||
format: 'base64',
|
||||
fromTransactionId,
|
||||
maxResultCount,
|
||||
communityId: iotaTopic,
|
||||
}
|
||||
logger.info('call getTransactions on Node Server via jsonrpc 2.0 with ', parameter)
|
||||
const response = await client.exec<ConfirmedTransactionList>('getTransactions', parameter) // sends payload {jsonrpc: '2.0', params: ...}
|
||||
return resolveResponse(response, (result: ConfirmedTransactionList) => {
|
||||
logger.debug('GradidoNode used time', result.timeUsed)
|
||||
return result.transactions.map((transactionBase64) =>
|
||||
confirmedTransactionFromBase64(transactionBase64),
|
||||
)
|
||||
})
|
||||
}
|
||||
|
||||
async function getTransaction(
|
||||
transactionId: number | Buffer,
|
||||
iotaTopic: string,
|
||||
): Promise<ConfirmedTransaction | undefined> {
|
||||
logger.info('call gettransaction on Node Server via jsonrpc 2.0')
|
||||
const response = await client.exec<ConfirmedTransactionResponse>('gettransaction', {
|
||||
format: 'base64',
|
||||
communityId: iotaTopic,
|
||||
transactionId: typeof transactionId === 'number' ? transactionId : undefined,
|
||||
iotaMessageId: transactionId instanceof Buffer ? transactionId.toString('hex') : undefined,
|
||||
})
|
||||
return resolveResponse(response, (result: ConfirmedTransactionResponse) => {
|
||||
logger.debug('GradidoNode used time', result.timeUsed)
|
||||
return result.transaction && result.transaction !== ''
|
||||
? confirmedTransactionFromBase64(result.transaction)
|
||||
: undefined
|
||||
})
|
||||
}
|
||||
|
||||
async function getLastTransaction(iotaTopic: string): Promise<ConfirmedTransaction | undefined> {
|
||||
logger.info('call getlasttransaction on Node Server via jsonrpc 2.0')
|
||||
const response = await client.exec<ConfirmedTransactionResponse>('getlasttransaction', {
|
||||
format: 'base64',
|
||||
communityId: iotaTopic,
|
||||
})
|
||||
return resolveResponse(response, (result: ConfirmedTransactionResponse) => {
|
||||
logger.debug('GradidoNode used time', result.timeUsed)
|
||||
return result.transaction && result.transaction !== ''
|
||||
? confirmedTransactionFromBase64(result.transaction)
|
||||
: undefined
|
||||
})
|
||||
}
|
||||
|
||||
async function getAddressType(pubkey: Buffer, iotaTopic: string): Promise<AddressType | undefined> {
|
||||
logger.info('call getaddresstype on Node Server via jsonrpc 2.0')
|
||||
const response = await client.exec<AddressTypeResult>('getaddresstype', {
|
||||
pubkey: pubkey.toString('hex'),
|
||||
communityId: iotaTopic,
|
||||
})
|
||||
return resolveResponse(response, (result: AddressTypeResult) =>
|
||||
stringToAddressType(result.addressType),
|
||||
)
|
||||
}
|
||||
|
||||
export { getTransaction, getLastTransaction, getTransactions, getAddressType }
|
||||
@ -39,13 +39,15 @@ const dltConnector = {
|
||||
DLT_CONNECTOR_PORT: process.env.DLT_CONNECTOR_PORT ?? 6010,
|
||||
}
|
||||
|
||||
const nodeServer = {
|
||||
NODE_SERVER_URL: process.env.NODE_SERVER_URL ?? 'http://localhost:8340',
|
||||
}
|
||||
|
||||
const gradidoBlockchain = {
|
||||
GRADIDO_BLOCKCHAIN_CRYPTO_APP_SECRET:
|
||||
process.env.GRADIDO_BLOCKCHAIN_CRYPTO_APP_SECRET ?? 'invalid',
|
||||
GRADIDO_BLOCKCHAIN_SERVER_CRYPTO_KEY:
|
||||
process.env.GRADIDO_BLOCKCHAIN_SERVER_CRYPTO_KEY ?? 'invalid',
|
||||
GRADIDO_BLOCKCHAIN_PRIVATE_KEY_ENCRYPTION_PASSWORD:
|
||||
process.env.GRADIDO_BLOCKCHAIN_PRIVATE_KEY_ENCRYPTION_PASSWORD,
|
||||
}
|
||||
|
||||
const backendServer = {
|
||||
@ -70,6 +72,7 @@ export const CONFIG = {
|
||||
...database,
|
||||
...iota,
|
||||
...dltConnector,
|
||||
...nodeServer,
|
||||
...gradidoBlockchain,
|
||||
...backendServer,
|
||||
}
|
||||
|
||||
@ -8,7 +8,7 @@ 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 { uuid4ToHash } from '@/utils/typeConverter'
|
||||
|
||||
import { KeyPair } from './KeyPair'
|
||||
|
||||
@ -17,7 +17,7 @@ export const CommunityRepository = getDataSource()
|
||||
.extend({
|
||||
async isExist(community: CommunityDraft | string): Promise<boolean> {
|
||||
const iotaTopic =
|
||||
community instanceof CommunityDraft ? iotaTopicFromCommunityUUID(community.uuid) : community
|
||||
community instanceof CommunityDraft ? uuid4ToHash(community.uuid) : community
|
||||
const result = await this.find({
|
||||
where: { iotaTopic },
|
||||
})
|
||||
@ -27,7 +27,7 @@ export const CommunityRepository = getDataSource()
|
||||
async findByCommunityArg({ uuid, foreign, confirmed }: CommunityArg): Promise<Community[]> {
|
||||
return await this.find({
|
||||
where: {
|
||||
...(uuid && { iotaTopic: iotaTopicFromCommunityUUID(uuid) }),
|
||||
...(uuid && { iotaTopic: uuid4ToHash(uuid) }),
|
||||
...(foreign && { foreign }),
|
||||
...(confirmed && { confirmedAt: Not(IsNull()) }),
|
||||
},
|
||||
@ -35,7 +35,7 @@ export const CommunityRepository = getDataSource()
|
||||
},
|
||||
|
||||
async findByCommunityUuid(communityUuid: string): Promise<Community | null> {
|
||||
return await this.findOneBy({ iotaTopic: iotaTopicFromCommunityUUID(communityUuid) })
|
||||
return await this.findOneBy({ iotaTopic: uuid4ToHash(communityUuid) })
|
||||
},
|
||||
|
||||
async findByIotaTopic(iotaTopic: string): Promise<Community | null> {
|
||||
@ -54,7 +54,7 @@ export const CommunityRepository = getDataSource()
|
||||
}
|
||||
return (
|
||||
(await this.findOneBy({
|
||||
iotaTopic: iotaTopicFromCommunityUUID(identifier.communityUuid),
|
||||
iotaTopic: uuid4ToHash(identifier.communityUuid),
|
||||
})) ?? undefined
|
||||
)
|
||||
},
|
||||
|
||||
@ -1,26 +1,20 @@
|
||||
import { Transaction } from '@entity/Transaction'
|
||||
import { Field, Int, ObjectType } from 'type-graphql'
|
||||
import { GradidoTransaction, MemoryBlock, transactionTypeToString } from 'gradido-blockchain-js'
|
||||
import { Field, 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, signature }: Transaction) {
|
||||
const transactionType = getEnumValue(TransactionType, type)
|
||||
if (!transactionType) {
|
||||
throw new LogError('invalid transaction, type is missing')
|
||||
public constructor(transaction: GradidoTransaction, messageId: MemoryBlock) {
|
||||
const body = transaction.getTransactionBody()
|
||||
if (!body) {
|
||||
throw new LogError('invalid gradido transaction, cannot geht valid TransactionBody')
|
||||
}
|
||||
this.id = id
|
||||
this.createdAt = createdAt.toString()
|
||||
this.type = transactionType.toString()
|
||||
this.topic = community.iotaTopic
|
||||
this.signatureHex = signature.toString('hex')
|
||||
}
|
||||
|
||||
@Field(() => Int)
|
||||
id: number
|
||||
this.createdAt = body.getCreatedAt().getDate().toString()
|
||||
this.type = transactionTypeToString(body?.getTransactionType())
|
||||
this.messageIdHex = messageId.convertToHex()
|
||||
}
|
||||
|
||||
@Field(() => String)
|
||||
createdAt: string
|
||||
@ -29,8 +23,5 @@ export class TransactionRecipe {
|
||||
type: string
|
||||
|
||||
@Field(() => String)
|
||||
topic: string
|
||||
|
||||
@Field(() => String)
|
||||
signatureHex: string
|
||||
messageIdHex: string
|
||||
}
|
||||
|
||||
@ -10,7 +10,7 @@ import { CommunityRepository } from '@/data/Community.repository'
|
||||
import { AddCommunityContext } from '@/interactions/backendToDb/community/AddCommunity.context'
|
||||
import { logger } from '@/logging/logger'
|
||||
import { LogError } from '@/server/LogError'
|
||||
import { iotaTopicFromCommunityUUID } from '@/utils/typeConverter'
|
||||
import { uuid4ToHash } from '@/utils/typeConverter'
|
||||
|
||||
@Resolver()
|
||||
export class CommunityResolver {
|
||||
@ -46,7 +46,7 @@ export class CommunityResolver {
|
||||
communityDraft: CommunityDraft,
|
||||
): Promise<TransactionResult> {
|
||||
logger.info('addCommunity', communityDraft)
|
||||
const topic = iotaTopicFromCommunityUUID(communityDraft.uuid)
|
||||
const topic = uuid4ToHash(communityDraft.uuid)
|
||||
// check if community was already written to db
|
||||
if (await CommunityRepository.isExist(topic)) {
|
||||
return new TransactionResult(
|
||||
|
||||
@ -6,10 +6,10 @@ import { loadCryptoKeys, MemoryBlock } from 'gradido-blockchain-js'
|
||||
import { CONFIG } from '@/config'
|
||||
|
||||
import { BackendClient } from './client/BackendClient'
|
||||
import { CommunityRepository } from './data/Community.repository'
|
||||
import { CommunityDraft } from './graphql/input/CommunityDraft'
|
||||
import { AddCommunityContext } from './interactions/backendToDb/community/AddCommunity.context'
|
||||
import { logger } from './logging/logger'
|
||||
import { KeyPairCacheManager } from './manager/KeyPairCacheManager'
|
||||
import createServer from './server/createServer'
|
||||
import { LogError } from './server/LogError'
|
||||
import { stopTransmitToIota, transmitToIota } from './tasks/transmitToIota'
|
||||
@ -61,20 +61,17 @@ async function main() {
|
||||
const { app } = await createServer()
|
||||
|
||||
// ask backend for home community if we haven't one
|
||||
try {
|
||||
await CommunityRepository.loadHomeCommunityKeyPair()
|
||||
} catch (e) {
|
||||
const backend = BackendClient.getInstance()
|
||||
if (!backend) {
|
||||
throw new LogError('cannot create backend client')
|
||||
}
|
||||
// wait for backend server to be ready
|
||||
await waitForServer(backend, 2500, 10)
|
||||
|
||||
const communityDraft = await backend.getHomeCommunityDraft()
|
||||
const addCommunityContext = new AddCommunityContext(communityDraft)
|
||||
await addCommunityContext.run()
|
||||
const backend = BackendClient.getInstance()
|
||||
if (!backend) {
|
||||
throw new LogError('cannot create backend client')
|
||||
}
|
||||
// wait for backend server to be ready
|
||||
await waitForServer(backend, 2500, 10)
|
||||
|
||||
const communityDraft = await backend.getHomeCommunityDraft()
|
||||
KeyPairCacheManager.getInstance().setHomeCommunityUUID(communityDraft.uuid)
|
||||
const addCommunityContext = new AddCommunityContext(communityDraft)
|
||||
await addCommunityContext.run()
|
||||
|
||||
// loop run all the time, check for new transaction for sending to iota
|
||||
void transmitToIota()
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
import { CommunityDraft } from '@/graphql/input/CommunityDraft'
|
||||
import { iotaTopicFromCommunityUUID } from '@/utils/typeConverter'
|
||||
import { uuid4ToHash } from '@/utils/typeConverter'
|
||||
|
||||
import { CommunityRole } from './Community.role'
|
||||
import { ForeignCommunityRole } from './ForeignCommunity.role'
|
||||
@ -15,7 +15,7 @@ export class AddCommunityContext {
|
||||
private iotaTopic: string
|
||||
public constructor(private communityDraft: CommunityDraft, iotaTopic?: string) {
|
||||
if (!iotaTopic) {
|
||||
this.iotaTopic = iotaTopicFromCommunityUUID(this.communityDraft.uuid)
|
||||
this.iotaTopic = uuid4ToHash(this.communityDraft.uuid)
|
||||
} else {
|
||||
this.iotaTopic = iotaTopic
|
||||
}
|
||||
|
||||
@ -22,7 +22,7 @@ import { AccountType } from '@/graphql/enum/AccountType'
|
||||
import { InputTransactionType } from '@/graphql/enum/InputTransactionType'
|
||||
import { TransactionDraft } from '@/graphql/input/TransactionDraft'
|
||||
import { UserAccountDraft } from '@/graphql/input/UserAccountDraft'
|
||||
import { iotaTopicFromCommunityUUID } from '@/utils/typeConverter'
|
||||
import { uuid4ToHash } from '@/utils/typeConverter'
|
||||
|
||||
import { CreateTransactionRecipeContext } from './CreateTransactionRecipe.context'
|
||||
|
||||
@ -50,8 +50,8 @@ let secondUser: UserSet
|
||||
let foreignUser: UserSet
|
||||
let homeCommunity: Community
|
||||
|
||||
const topic = iotaTopicFromCommunityUUID(homeCommunityUuid)
|
||||
const foreignTopic = iotaTopicFromCommunityUUID(foreignCommunityUuid)
|
||||
const topic = uuid4ToHash(homeCommunityUuid)
|
||||
const foreignTopic = uuid4ToHash(foreignCommunityUuid)
|
||||
|
||||
describe('interactions/backendToDb/transaction/Create Transaction Recipe Context Test', () => {
|
||||
beforeAll(async () => {
|
||||
|
||||
@ -0,0 +1,5 @@
|
||||
import { KeyPairEd25519 } from 'gradido-blockchain-js'
|
||||
|
||||
export abstract class AbstractKeyPairRole {
|
||||
public abstract generateKeyPair(): KeyPairEd25519
|
||||
}
|
||||
@ -0,0 +1,5 @@
|
||||
import { KeyPairEd25519 } from 'gradido-blockchain-js'
|
||||
|
||||
export abstract class AbstractRemoteKeyPairRole {
|
||||
public abstract retrieveKeyPair(): Promise<KeyPairEd25519>
|
||||
}
|
||||
@ -0,0 +1,13 @@
|
||||
import { KeyPairEd25519 } from 'gradido-blockchain-js'
|
||||
|
||||
import { AbstractKeyPairRole } from './AbstractKeyPair.role'
|
||||
|
||||
export class AccountKeyPairRole extends AbstractKeyPairRole {
|
||||
public constructor(private accountNr: number, private userKeyPair: KeyPairEd25519) {
|
||||
super()
|
||||
}
|
||||
|
||||
public generateKeyPair(): KeyPairEd25519 {
|
||||
return this.userKeyPair.deriveChild(this.accountNr)
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,31 @@
|
||||
import { KeyPairEd25519 } from 'gradido-blockchain-js'
|
||||
|
||||
import { getTransaction } from '@/client/GradidoNode'
|
||||
import { LogError } from '@/server/LogError'
|
||||
import { uuid4ToHash } from '@/utils/typeConverter'
|
||||
|
||||
import { AbstractRemoteKeyPairRole } from './AbstractRemoteKeyPair.role'
|
||||
|
||||
export class ForeignCommunityKeyPairRole extends AbstractRemoteKeyPairRole {
|
||||
public constructor(private communityUuid: string) {
|
||||
super()
|
||||
}
|
||||
|
||||
public async retrieveKeyPair(): Promise<KeyPairEd25519> {
|
||||
const firstTransaction = await getTransaction(1, uuid4ToHash(this.communityUuid).convertToHex())
|
||||
if (!firstTransaction) {
|
||||
throw new LogError(
|
||||
"GradidoNode Server don't know this community with uuid " + this.communityUuid,
|
||||
)
|
||||
}
|
||||
const transactionBody = firstTransaction.getGradidoTransaction()?.getTransactionBody()
|
||||
if (!transactionBody || !transactionBody.isCommunityRoot()) {
|
||||
throw new LogError('get invalid confirmed transaction from gradido node')
|
||||
}
|
||||
const communityRoot = transactionBody.getCommunityRoot()
|
||||
if (!communityRoot) {
|
||||
throw new LogError('invalid confirmed transaction')
|
||||
}
|
||||
return new KeyPairEd25519(communityRoot.getPubkey())
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,22 @@
|
||||
import { KeyPairEd25519, MemoryBlock } from 'gradido-blockchain-js'
|
||||
|
||||
import { CONFIG } from '@/config'
|
||||
import { LogError } from '@/server/LogError'
|
||||
|
||||
import { AbstractKeyPairRole } from './AbstractKeyPair.role'
|
||||
|
||||
export class HomeCommunityKeyPairRole extends AbstractKeyPairRole {
|
||||
public generateKeyPair(): KeyPairEd25519 {
|
||||
if (!CONFIG.IOTA_HOME_COMMUNITY_SEED) {
|
||||
throw new LogError(
|
||||
'IOTA_HOME_COMMUNITY_SEED is missing either in config or as environment variable',
|
||||
)
|
||||
}
|
||||
const seed = MemoryBlock.fromHex(CONFIG.IOTA_HOME_COMMUNITY_SEED)
|
||||
const keyPair = KeyPairEd25519.create(seed)
|
||||
if (!keyPair) {
|
||||
throw new LogError("couldn't create keyPair from IOTA_HOME_COMMUNITY_SEED")
|
||||
}
|
||||
return keyPair
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,56 @@
|
||||
import { KeyPairEd25519 } from 'gradido-blockchain-js'
|
||||
|
||||
import { UserIdentifier } from '@/graphql/input/UserIdentifier'
|
||||
import { KeyPairCacheManager } from '@/manager/KeyPairCacheManager'
|
||||
|
||||
import { AbstractRemoteKeyPairRole } from './AbstractRemoteKeyPair.role'
|
||||
import { AccountKeyPairRole } from './AccountKeyPair.role'
|
||||
import { ForeignCommunityKeyPairRole } from './ForeignCommunityKeyPair.role'
|
||||
import { HomeCommunityKeyPairRole } from './HomeCommunityKeyPair.role'
|
||||
import { RemoteAccountKeyPairRole } from './RemoteAccountKeyPair.role'
|
||||
import { UserKeyPairRole } from './UserKeyPair.role'
|
||||
|
||||
/**
|
||||
* @DCI-Context
|
||||
* Context for calculating key pair for signing transactions
|
||||
*/
|
||||
export async function KeyPairCalculation(input: UserIdentifier | string): Promise<KeyPairEd25519> {
|
||||
const cache = KeyPairCacheManager.getInstance()
|
||||
const keyPair = cache.findKeyPair(input)
|
||||
if (keyPair) {
|
||||
return keyPair
|
||||
}
|
||||
let communityUUID: string
|
||||
if (input instanceof UserIdentifier) {
|
||||
communityUUID = input.communityUuid
|
||||
} else {
|
||||
communityUUID = input
|
||||
}
|
||||
|
||||
if (cache.getHomeCommunityUUID() !== communityUUID) {
|
||||
// it isn't home community so we can only retrieve public keys
|
||||
let role: AbstractRemoteKeyPairRole
|
||||
if (input instanceof UserIdentifier) {
|
||||
role = new RemoteAccountKeyPairRole(input)
|
||||
} else {
|
||||
role = new ForeignCommunityKeyPairRole(input)
|
||||
}
|
||||
const keyPair = await role.retrieveKeyPair()
|
||||
cache.addKeyPair(input, keyPair)
|
||||
return keyPair
|
||||
}
|
||||
|
||||
let communityKeyPair = cache.findKeyPair(communityUUID)
|
||||
if (!communityKeyPair) {
|
||||
communityKeyPair = new HomeCommunityKeyPairRole().generateKeyPair()
|
||||
cache.addKeyPair(communityUUID, communityKeyPair)
|
||||
}
|
||||
if (input instanceof UserIdentifier) {
|
||||
const userKeyPair = new UserKeyPairRole(input, communityKeyPair).generateKeyPair()
|
||||
const accountNr = input.accountNr ?? 1
|
||||
const accountKeyPair = new AccountKeyPairRole(accountNr, userKeyPair).generateKeyPair()
|
||||
cache.addKeyPair(input, accountKeyPair)
|
||||
return accountKeyPair
|
||||
}
|
||||
return communityKeyPair
|
||||
}
|
||||
@ -0,0 +1,35 @@
|
||||
import { KeyPairEd25519 } from 'gradido-blockchain-js'
|
||||
|
||||
import { getTransactions } from '@/client/GradidoNode'
|
||||
import { UserIdentifier } from '@/graphql/input/UserIdentifier'
|
||||
import { LogError } from '@/server/LogError'
|
||||
import { uuid4ToHash } from '@/utils/typeConverter'
|
||||
|
||||
import { AbstractRemoteKeyPairRole } from './AbstractRemoteKeyPair.role'
|
||||
|
||||
export class RemoteAccountKeyPairRole extends AbstractRemoteKeyPairRole {
|
||||
public constructor(private user: UserIdentifier) {
|
||||
super()
|
||||
}
|
||||
|
||||
public async retrieveKeyPair(): Promise<KeyPairEd25519> {
|
||||
const nameHash = uuid4ToHash(this.user.uuid)
|
||||
const confirmedTransactions = await getTransactions(
|
||||
0,
|
||||
30,
|
||||
uuid4ToHash(this.user.communityUuid).convertToHex(),
|
||||
)
|
||||
for (let i = 0; i < confirmedTransactions.length; i++) {
|
||||
const transactionBody = confirmedTransactions[i].getGradidoTransaction()?.getTransactionBody()
|
||||
if (transactionBody && transactionBody.isRegisterAddress()) {
|
||||
const registerAddress = transactionBody.getRegisterAddress()
|
||||
if (registerAddress && registerAddress.getNameHash()?.equal(nameHash)) {
|
||||
return new KeyPairEd25519(registerAddress.getAccountPublicKey())
|
||||
}
|
||||
}
|
||||
}
|
||||
throw new LogError(
|
||||
'cannot find remote user in first 30 transaction from remote blockchain, please wait for better recover implementation',
|
||||
)
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,28 @@
|
||||
import { KeyPairEd25519 } from 'gradido-blockchain-js'
|
||||
|
||||
import { UserIdentifier } from '@/graphql/input/UserIdentifier'
|
||||
import { hardenDerivationIndex } from '@/utils/derivationHelper'
|
||||
import { uuid4ToBuffer } from '@/utils/typeConverter'
|
||||
|
||||
import { AbstractKeyPairRole } from './AbstractKeyPair.role'
|
||||
|
||||
export class UserKeyPairRole extends AbstractKeyPairRole {
|
||||
public constructor(private user: UserIdentifier, private communityKeys: KeyPairEd25519) {
|
||||
super()
|
||||
}
|
||||
|
||||
public generateKeyPair(): KeyPairEd25519 {
|
||||
// example gradido id: 03857ac1-9cc2-483e-8a91-e5b10f5b8d16 =>
|
||||
// wholeHex: '03857ac19cc2483e8a91e5b10f5b8d16']
|
||||
const wholeHex = uuid4ToBuffer(this.user.uuid)
|
||||
const parts = []
|
||||
for (let i = 0; i < 4; i++) {
|
||||
parts[i] = hardenDerivationIndex(wholeHex.subarray(i * 4, (i + 1) * 4).readUInt32BE())
|
||||
}
|
||||
// parts: [2206563009, 2629978174, 2324817329, 2405141782]
|
||||
return parts.reduce(
|
||||
(keyPair: KeyPairEd25519, node: number) => keyPair.deriveChild(node),
|
||||
this.communityKeys,
|
||||
)
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,7 @@
|
||||
import { GradidoTransactionBuilder } from 'gradido-blockchain-js'
|
||||
|
||||
export abstract class AbstractTransactionRole {
|
||||
abstract getGradidoTransactionBuilder(): Promise<GradidoTransactionBuilder>
|
||||
abstract getSenderCommunityUuid(): string
|
||||
abstract getRecipientCommunityUuid(): string
|
||||
}
|
||||
@ -0,0 +1,47 @@
|
||||
import { GradidoTransactionBuilder } from 'gradido-blockchain-js'
|
||||
|
||||
import { CommunityDraft } from '@/graphql/input/CommunityDraft'
|
||||
import { LogError } from '@/server/LogError'
|
||||
import {
|
||||
AUF_ACCOUNT_DERIVATION_INDEX,
|
||||
GMW_ACCOUNT_DERIVATION_INDEX,
|
||||
hardenDerivationIndex,
|
||||
} from '@/utils/derivationHelper'
|
||||
|
||||
import { KeyPairCalculation } from '../keyPairCalculation/KeyPairCalculation.context'
|
||||
|
||||
import { AbstractTransactionRole } from './AbstractTransaction.role'
|
||||
|
||||
export class CommunityRootTransactionRole extends AbstractTransactionRole {
|
||||
constructor(private self: CommunityDraft) {
|
||||
super()
|
||||
}
|
||||
|
||||
getSenderCommunityUuid(): string {
|
||||
return this.self.uuid
|
||||
}
|
||||
|
||||
getRecipientCommunityUuid(): string {
|
||||
throw new LogError('cannot be used as cross group transaction')
|
||||
}
|
||||
|
||||
public async getGradidoTransactionBuilder(): Promise<GradidoTransactionBuilder> {
|
||||
const builder = new GradidoTransactionBuilder()
|
||||
const communityKeyPair = await KeyPairCalculation(this.self.uuid)
|
||||
const gmwKeyPair = communityKeyPair.deriveChild(
|
||||
hardenDerivationIndex(GMW_ACCOUNT_DERIVATION_INDEX),
|
||||
)
|
||||
const aufKeyPair = communityKeyPair.deriveChild(
|
||||
hardenDerivationIndex(AUF_ACCOUNT_DERIVATION_INDEX),
|
||||
)
|
||||
builder
|
||||
.setCreatedAt(new Date(this.self.createdAt))
|
||||
.setCommunityRoot(
|
||||
communityKeyPair.getPublicKey(),
|
||||
gmwKeyPair.getPublicKey(),
|
||||
aufKeyPair.getPublicKey(),
|
||||
)
|
||||
.sign(communityKeyPair)
|
||||
return builder
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,39 @@
|
||||
import { GradidoTransactionBuilder, TransferAmount } from 'gradido-blockchain-js'
|
||||
|
||||
import { TransactionDraft } from '@/graphql/input/TransactionDraft'
|
||||
import { LogError } from '@/server/LogError'
|
||||
|
||||
import { KeyPairCalculation } from '../keyPairCalculation/KeyPairCalculation.context'
|
||||
|
||||
import { AbstractTransactionRole } from './AbstractTransaction.role'
|
||||
|
||||
export class CreationTransactionRole extends AbstractTransactionRole {
|
||||
constructor(private self: TransactionDraft) {
|
||||
super()
|
||||
}
|
||||
|
||||
getSenderCommunityUuid(): string {
|
||||
return this.self.user.communityUuid
|
||||
}
|
||||
|
||||
getRecipientCommunityUuid(): string {
|
||||
throw new LogError('cannot be used as cross group transaction')
|
||||
}
|
||||
|
||||
public async getGradidoTransactionBuilder(): Promise<GradidoTransactionBuilder> {
|
||||
const builder = new GradidoTransactionBuilder()
|
||||
const recipientKeyPair = await KeyPairCalculation(this.self.user)
|
||||
const signerKeyPair = await KeyPairCalculation(this.self.linkedUser)
|
||||
if (!this.self.targetDate) {
|
||||
throw new LogError('target date missing for creation transaction')
|
||||
}
|
||||
builder
|
||||
.setCreatedAt(new Date(this.self.createdAt))
|
||||
.setTransactionCreation(
|
||||
new TransferAmount(recipientKeyPair.getPublicKey(), this.self.amount.toString()),
|
||||
new Date(this.self.targetDate),
|
||||
)
|
||||
.sign(signerKeyPair)
|
||||
return builder
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,40 @@
|
||||
/* eslint-disable camelcase */
|
||||
import { AddressType_COMMUNITY_HUMAN, GradidoTransactionBuilder } from 'gradido-blockchain-js'
|
||||
|
||||
import { UserAccountDraft } from '@/graphql/input/UserAccountDraft'
|
||||
import { LogError } from '@/server/LogError'
|
||||
import { uuid4ToHash } from '@/utils/typeConverter'
|
||||
|
||||
import { KeyPairCalculation } from '../keyPairCalculation/KeyPairCalculation.context'
|
||||
|
||||
import { AbstractTransactionRole } from './AbstractTransaction.role'
|
||||
|
||||
export class RegisterAddressTransactionRole extends AbstractTransactionRole {
|
||||
constructor(private self: UserAccountDraft) {
|
||||
super()
|
||||
}
|
||||
|
||||
getSenderCommunityUuid(): string {
|
||||
return this.self.user.communityUuid
|
||||
}
|
||||
|
||||
getRecipientCommunityUuid(): string {
|
||||
throw new LogError('cannot yet be used as cross group transaction')
|
||||
}
|
||||
|
||||
public async getGradidoTransactionBuilder(): Promise<GradidoTransactionBuilder> {
|
||||
const builder = new GradidoTransactionBuilder()
|
||||
const communityKeyPair = await KeyPairCalculation(this.self.user.communityUuid)
|
||||
const accountKeyPair = await KeyPairCalculation(this.self.user)
|
||||
builder
|
||||
.setCreatedAt(new Date(this.self.createdAt))
|
||||
.setRegisterAddress(
|
||||
accountKeyPair.getPublicKey(),
|
||||
AddressType_COMMUNITY_HUMAN,
|
||||
uuid4ToHash(this.self.user.uuid),
|
||||
)
|
||||
.sign(communityKeyPair)
|
||||
.sign(accountKeyPair)
|
||||
return builder
|
||||
}
|
||||
}
|
||||
116
dlt-connector/src/interactions/sendToIota/SendToIota.context.ts
Normal file
116
dlt-connector/src/interactions/sendToIota/SendToIota.context.ts
Normal file
@ -0,0 +1,116 @@
|
||||
/* eslint-disable camelcase */
|
||||
import {
|
||||
GradidoTransaction,
|
||||
InteractionSerialize,
|
||||
InteractionValidate,
|
||||
MemoryBlock,
|
||||
ValidateType_SINGLE,
|
||||
} from 'gradido-blockchain-js'
|
||||
|
||||
import { sendMessage as iotaSendMessage } from '@/client/IotaClient'
|
||||
import { InputTransactionType } from '@/graphql/enum/InputTransactionType'
|
||||
import { TransactionErrorType } from '@/graphql/enum/TransactionErrorType'
|
||||
import { CommunityDraft } from '@/graphql/input/CommunityDraft'
|
||||
import { TransactionDraft } from '@/graphql/input/TransactionDraft'
|
||||
import { UserAccountDraft } from '@/graphql/input/UserAccountDraft'
|
||||
import { TransactionError } from '@/graphql/model/TransactionError'
|
||||
import { TransactionRecipe } from '@/graphql/model/TransactionRecipe'
|
||||
import { TransactionResult } from '@/graphql/model/TransactionResult'
|
||||
import { logger } from '@/logging/logger'
|
||||
import { LogError } from '@/server/LogError'
|
||||
import { uuid4ToHash } from '@/utils/typeConverter'
|
||||
|
||||
import { AbstractTransactionRole } from './AbstractTransaction.role'
|
||||
import { CommunityRootTransactionRole } from './CommunityRootTransaction.role'
|
||||
import { CreationTransactionRole } from './CreationTransaction.role'
|
||||
import { RegisterAddressTransactionRole } from './RegisterAddressTransaction.role'
|
||||
import { TransferTransactionRole } from './TransferTransaction.role'
|
||||
|
||||
/**
|
||||
* @DCI-Context
|
||||
* Context for sending transaction to iota
|
||||
* send every transaction only once to iota!
|
||||
*/
|
||||
export async function SendToIotaContext(
|
||||
input: TransactionDraft | UserAccountDraft | CommunityDraft,
|
||||
): Promise<TransactionResult> {
|
||||
const validate = (transaction: GradidoTransaction): void => {
|
||||
try {
|
||||
// throw an exception when something is wrong
|
||||
const validator = new InteractionValidate(transaction)
|
||||
validator.run(ValidateType_SINGLE)
|
||||
} catch (e) {
|
||||
if (e instanceof Error) {
|
||||
throw new TransactionError(TransactionErrorType.VALIDATION_ERROR, e.message)
|
||||
} else if (typeof e === 'string') {
|
||||
throw new TransactionError(TransactionErrorType.VALIDATION_ERROR, e)
|
||||
} else {
|
||||
throw e
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const sendViaIota = async (
|
||||
gradidoTransaction: GradidoTransaction,
|
||||
topic: string,
|
||||
): Promise<MemoryBlock> => {
|
||||
// protobuf serializing function
|
||||
const serialized = new InteractionSerialize(gradidoTransaction).run()
|
||||
if (!serialized) {
|
||||
throw new TransactionError(
|
||||
TransactionErrorType.PROTO_ENCODE_ERROR,
|
||||
'cannot serialize transaction',
|
||||
)
|
||||
}
|
||||
const resultMessage = await iotaSendMessage(
|
||||
Uint8Array.from(serialized.data()),
|
||||
Uint8Array.from(Buffer.from(topic, 'hex')),
|
||||
)
|
||||
logger.info('transmitted Gradido Transaction to Iota', {
|
||||
messageId: resultMessage.messageId,
|
||||
})
|
||||
return MemoryBlock.fromHex(resultMessage.messageId)
|
||||
}
|
||||
|
||||
let role: AbstractTransactionRole
|
||||
if (input instanceof TransactionDraft) {
|
||||
if (input.type === InputTransactionType.CREATION) {
|
||||
role = new CreationTransactionRole(input)
|
||||
} else if (input.type === InputTransactionType.SEND) {
|
||||
role = new TransferTransactionRole(input)
|
||||
} else {
|
||||
throw new LogError('not supported transaction type')
|
||||
}
|
||||
} else if (input instanceof UserAccountDraft) {
|
||||
role = new RegisterAddressTransactionRole(input)
|
||||
} else if (input instanceof CommunityDraft) {
|
||||
role = new CommunityRootTransactionRole(input)
|
||||
} else {
|
||||
throw new LogError('not expected input')
|
||||
}
|
||||
const builder = await role.getGradidoTransactionBuilder()
|
||||
if (builder.isCrossCommunityTransaction()) {
|
||||
const outboundTransaction = builder.buildOutbound()
|
||||
validate(outboundTransaction)
|
||||
const outboundIotaMessageId = await sendViaIota(
|
||||
outboundTransaction,
|
||||
uuid4ToHash(role.getSenderCommunityUuid()).convertToHex(),
|
||||
)
|
||||
builder.setParentMessageId(outboundIotaMessageId)
|
||||
const inboundTransaction = builder.buildInbound()
|
||||
validate(inboundTransaction)
|
||||
await sendViaIota(
|
||||
inboundTransaction,
|
||||
uuid4ToHash(role.getRecipientCommunityUuid()).convertToHex(),
|
||||
)
|
||||
return new TransactionResult(new TransactionRecipe(outboundTransaction, outboundIotaMessageId))
|
||||
} else {
|
||||
const transaction = builder.build()
|
||||
validate(transaction)
|
||||
const iotaMessageId = await sendViaIota(
|
||||
transaction,
|
||||
uuid4ToHash(role.getSenderCommunityUuid()).convertToHex(),
|
||||
)
|
||||
return new TransactionResult(new TransactionRecipe(transaction, iotaMessageId))
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,44 @@
|
||||
import { GradidoTransactionBuilder, TransferAmount } from 'gradido-blockchain-js'
|
||||
|
||||
import { TransactionDraft } from '@/graphql/input/TransactionDraft'
|
||||
import { uuid4ToHash } from '@/utils/typeConverter'
|
||||
|
||||
import { KeyPairCalculation } from '../keyPairCalculation/KeyPairCalculation.context'
|
||||
|
||||
import { AbstractTransactionRole } from './AbstractTransaction.role'
|
||||
|
||||
export class TransferTransactionRole extends AbstractTransactionRole {
|
||||
constructor(private self: TransactionDraft) {
|
||||
super()
|
||||
}
|
||||
|
||||
getSenderCommunityUuid(): string {
|
||||
return this.self.user.communityUuid
|
||||
}
|
||||
|
||||
getRecipientCommunityUuid(): string {
|
||||
return this.self.linkedUser.communityUuid
|
||||
}
|
||||
|
||||
public async getGradidoTransactionBuilder(): Promise<GradidoTransactionBuilder> {
|
||||
const builder = new GradidoTransactionBuilder()
|
||||
const senderKeyPair = await KeyPairCalculation(this.self.user)
|
||||
const recipientKeyPair = await KeyPairCalculation(this.self.linkedUser)
|
||||
builder
|
||||
.setCreatedAt(new Date(this.self.createdAt))
|
||||
.setTransactionTransfer(
|
||||
new TransferAmount(senderKeyPair.getPublicKey(), this.self.amount.toString()),
|
||||
recipientKeyPair.getPublicKey(),
|
||||
)
|
||||
const senderCommunity = this.self.user.communityUuid
|
||||
const recipientCommunity = this.self.linkedUser.communityUuid
|
||||
if (senderCommunity !== recipientCommunity) {
|
||||
// we have a cross group transaction
|
||||
builder
|
||||
.setSenderCommunity(uuid4ToHash(senderCommunity).convertToHex())
|
||||
.setRecipientCommunity(uuid4ToHash(recipientCommunity).convertToHex())
|
||||
}
|
||||
builder.sign(senderKeyPair)
|
||||
return builder
|
||||
}
|
||||
}
|
||||
66
dlt-connector/src/manager/KeyPairCacheManager.ts
Normal file
66
dlt-connector/src/manager/KeyPairCacheManager.ts
Normal file
@ -0,0 +1,66 @@
|
||||
import { KeyPairEd25519 } from 'gradido-blockchain-js'
|
||||
|
||||
import { UserIdentifier } from '@/graphql/input/UserIdentifier'
|
||||
import { LogError } from '@/server/LogError'
|
||||
|
||||
// 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 KeyPairCacheManager {
|
||||
// eslint-disable-next-line no-use-before-define
|
||||
private static instance: KeyPairCacheManager
|
||||
private cache: Map<string, KeyPairEd25519> = new Map<string, KeyPairEd25519>()
|
||||
private homeCommunityUUID: string
|
||||
|
||||
/**
|
||||
* 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(): KeyPairCacheManager {
|
||||
if (!KeyPairCacheManager.instance) {
|
||||
KeyPairCacheManager.instance = new KeyPairCacheManager()
|
||||
}
|
||||
return KeyPairCacheManager.instance
|
||||
}
|
||||
|
||||
public setHomeCommunityUUID(uuid: string): void {
|
||||
this.homeCommunityUUID = uuid
|
||||
}
|
||||
|
||||
public getHomeCommunityUUID(): string {
|
||||
return this.homeCommunityUUID
|
||||
}
|
||||
|
||||
public findKeyPair(input: UserIdentifier | string): KeyPairEd25519 | undefined {
|
||||
return this.cache.get(this.getKey(input))
|
||||
}
|
||||
|
||||
public addKeyPair(input: UserIdentifier | string, keyPair: KeyPairEd25519): void {
|
||||
const key = this.getKey(input)
|
||||
if (this.cache.has(key)) {
|
||||
throw new LogError('key already exist, cannot add', key)
|
||||
}
|
||||
this.cache.set(key, keyPair)
|
||||
}
|
||||
|
||||
protected getKey(input: UserIdentifier | string): string {
|
||||
if (input instanceof UserIdentifier) {
|
||||
return input.uuid
|
||||
} else {
|
||||
return input
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1,4 +1,6 @@
|
||||
export const HARDENED_KEY_BITMASK = 0x80000000
|
||||
export const GMW_ACCOUNT_DERIVATION_INDEX = 1
|
||||
export const AUF_ACCOUNT_DERIVATION_INDEX = 2
|
||||
|
||||
/*
|
||||
* change derivation index from x => x'
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
import 'reflect-metadata'
|
||||
|
||||
import { base64ToBuffer, iotaTopicFromCommunityUUID, uuid4ToBuffer } from './typeConverter'
|
||||
import { base64ToBuffer, uuid4ToHash, uuid4ToBuffer } from './typeConverter'
|
||||
|
||||
describe('utils/typeConverter', () => {
|
||||
it('uuid4ToBuffer', () => {
|
||||
@ -10,7 +10,7 @@ describe('utils/typeConverter', () => {
|
||||
})
|
||||
|
||||
it('iotaTopicFromCommunityUUID', () => {
|
||||
expect(iotaTopicFromCommunityUUID('4f28e081-5c39-4dde-b6a4-3bde71de8d65')).toBe(
|
||||
expect(uuid4ToHash('4f28e081-5c39-4dde-b6a4-3bde71de8d65')).toBe(
|
||||
'3138b3590311fdf0a823e173caa9487b7d275c23fab07106b4b1364cb038affd',
|
||||
)
|
||||
})
|
||||
|
||||
@ -8,10 +8,14 @@ import {
|
||||
AddressType_CRYPTO_ACCOUNT,
|
||||
AddressType_NONE,
|
||||
AddressType_SUBACCOUNT,
|
||||
ConfirmedTransaction,
|
||||
DeserializeType_CONFIRMED_TRANSACTION,
|
||||
InteractionDeserialize,
|
||||
MemoryBlock,
|
||||
} from 'gradido-blockchain-js'
|
||||
import { crypto_generichash as cryptoHash } from 'sodium-native'
|
||||
|
||||
import { AccountType } from '@/graphql/enum/AccountType'
|
||||
import { LogError } from '@/server/LogError'
|
||||
|
||||
export const uuid4ToBuffer = (uuid: string): Buffer => {
|
||||
// Remove dashes from the UUIDv4 string
|
||||
@ -23,10 +27,13 @@ export const uuid4ToBuffer = (uuid: string): Buffer => {
|
||||
return buffer
|
||||
}
|
||||
|
||||
export const iotaTopicFromCommunityUUID = (communityUUID: string): string => {
|
||||
const hash = Buffer.alloc(32)
|
||||
cryptoHash(hash, uuid4ToBuffer(communityUUID))
|
||||
return hash.toString('hex')
|
||||
export const uuid4ToMemoryBlock = (uuid: string): MemoryBlock => {
|
||||
// Remove dashes from the UUIDv4 string
|
||||
return MemoryBlock.fromHex(uuid.replace(/-/g, ''))
|
||||
}
|
||||
|
||||
export const uuid4ToHash = (communityUUID: string): MemoryBlock => {
|
||||
return uuid4ToMemoryBlock(communityUUID).calculateHash()
|
||||
}
|
||||
|
||||
export const base64ToBuffer = (base64: string): Buffer => {
|
||||
@ -86,3 +93,16 @@ export const addressTypeToAccountType = (type: AddressType): AccountType => {
|
||||
return AccountType.NONE
|
||||
}
|
||||
}
|
||||
|
||||
export const confirmedTransactionFromBase64 = (base64: string): ConfirmedTransaction => {
|
||||
const deserializer = new InteractionDeserialize(
|
||||
MemoryBlock.fromBase64(base64),
|
||||
DeserializeType_CONFIRMED_TRANSACTION,
|
||||
)
|
||||
deserializer.run()
|
||||
const confirmedTransaction = deserializer.getConfirmedTransaction()
|
||||
if (!confirmedTransaction) {
|
||||
throw new LogError("invalid data, couldn't deserialize")
|
||||
}
|
||||
return confirmedTransaction
|
||||
}
|
||||
|
||||
@ -3,7 +3,7 @@ import { Community } from '@entity/Community'
|
||||
import { KeyPair } from '@/data/KeyPair'
|
||||
import { CommunityDraft } from '@/graphql/input/CommunityDraft'
|
||||
import { AddCommunityContext } from '@/interactions/backendToDb/community/AddCommunity.context'
|
||||
import { iotaTopicFromCommunityUUID } from '@/utils/typeConverter'
|
||||
import { uuid4ToHash } from '@/utils/typeConverter'
|
||||
|
||||
export const communitySeed = async (
|
||||
uuid: string,
|
||||
@ -14,7 +14,7 @@ export const communitySeed = async (
|
||||
homeCommunityDraft.uuid = uuid
|
||||
homeCommunityDraft.foreign = foreign
|
||||
homeCommunityDraft.createdAt = new Date().toISOString()
|
||||
const iotaTopic = iotaTopicFromCommunityUUID(uuid)
|
||||
const iotaTopic = uuid4ToHash(uuid)
|
||||
const addCommunityContext = new AddCommunityContext(homeCommunityDraft, iotaTopic)
|
||||
await addCommunityContext.run()
|
||||
|
||||
|
||||
@ -1589,6 +1589,13 @@ available-typed-arrays@^1.0.7:
|
||||
dependencies:
|
||||
possible-typed-array-names "^1.0.0"
|
||||
|
||||
axios@^0.24.0:
|
||||
version "0.24.0"
|
||||
resolved "https://registry.yarnpkg.com/axios/-/axios-0.24.0.tgz#804e6fa1e4b9c5288501dd9dff56a7a0940d20d6"
|
||||
integrity sha512-Q6cWsys88HoPgAaFAVUb0WpPk0O8iTeisR9IMqy9G8AbO4NlpVknrnQS03zzF9PGAWgO3cgletO3VjV/P7VztA==
|
||||
dependencies:
|
||||
follow-redirects "^1.14.4"
|
||||
|
||||
axios@^1.6.5:
|
||||
version "1.7.7"
|
||||
resolved "https://registry.yarnpkg.com/axios/-/axios-1.7.7.tgz#2f554296f9892a72ac8d8e4c5b79c14a91d0a47f"
|
||||
@ -2254,7 +2261,7 @@ debug@2.6.9:
|
||||
dependencies:
|
||||
ms "2.0.0"
|
||||
|
||||
debug@4, debug@^4, debug@^4.1.0, debug@^4.1.1, debug@^4.3.1, debug@^4.3.2, debug@^4.3.4, debug@^4.3.5:
|
||||
debug@4, debug@^4, debug@^4.1.0, debug@^4.1.1, debug@^4.3.1, debug@^4.3.2, debug@^4.3.3, debug@^4.3.4, debug@^4.3.5:
|
||||
version "4.3.7"
|
||||
resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.7.tgz#87945b4151a011d76d95a198d7111c865c360a52"
|
||||
integrity sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ==
|
||||
@ -2463,9 +2470,9 @@ ee-first@1.1.1:
|
||||
integrity sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==
|
||||
|
||||
electron-to-chromium@^1.5.4:
|
||||
version "1.5.26"
|
||||
resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.5.26.tgz#449b4fa90e83ab98abbe3b6a96c8ee395de94452"
|
||||
integrity sha512-Z+OMe9M/V6Ep9n/52+b7lkvYEps26z4Yz3vjWL1V61W0q+VLF1pOHhMY17sa4roz4AWmULSI8E6SAojZA5L0YQ==
|
||||
version "1.5.27"
|
||||
resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.5.27.tgz#5203ce5d6054857d84ba84d3681cbe59132ade78"
|
||||
integrity sha512-o37j1vZqCoEgBuWWXLHQgTN/KDKe7zwpiY5CPeq2RvUqOyJw9xnrULzZAEVQ5p4h+zjMk7hgtOoPdnLxr7m/jw==
|
||||
|
||||
emittery@^0.8.1:
|
||||
version "0.8.1"
|
||||
@ -3182,7 +3189,7 @@ flatted@^3.2.7, flatted@^3.2.9:
|
||||
resolved "https://registry.yarnpkg.com/flatted/-/flatted-3.3.1.tgz#21db470729a6734d4997002f439cb308987f567a"
|
||||
integrity sha512-X8cqMLLie7KsNUDSdzeN8FYK9rEt4Dt67OsG/DNGnYTSDBG4uFAJFBnUeiV+zCVAvwFy56IjM9sH51jVaEhNxw==
|
||||
|
||||
follow-redirects@^1.15.6:
|
||||
follow-redirects@^1.14.4, follow-redirects@^1.15.6:
|
||||
version "1.15.9"
|
||||
resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.15.9.tgz#a604fa10e443bf98ca94228d9eebcc2e8a2c8ee1"
|
||||
integrity sha512-gew4GsXizNgdoRyqmyfMHyAmXsZDk6mHkSxZFCzW9gwlbtOW44CDtYavM+y+72qD/Vq2l550kMF52DT8fOLJqQ==
|
||||
@ -3468,7 +3475,7 @@ graceful-fs@^4.1.6, graceful-fs@^4.2.0, graceful-fs@^4.2.4, graceful-fs@^4.2.9:
|
||||
|
||||
"gradido-blockchain-js@git+https://github.com/gradido/gradido-blockchain-js#master":
|
||||
version "0.0.1"
|
||||
resolved "git+https://github.com/gradido/gradido-blockchain-js#02aaeefc015c8ec8b1a2c453d75e7c2cf803a7c2"
|
||||
resolved "git+https://github.com/gradido/gradido-blockchain-js#5e7bc50af82d30ef0fdbe48414b1f916c592b6f4"
|
||||
dependencies:
|
||||
bindings "^1.5.0"
|
||||
nan "^2.20.0"
|
||||
@ -4528,6 +4535,14 @@ jsonfile@^6.0.1:
|
||||
optionalDependencies:
|
||||
graceful-fs "^4.1.6"
|
||||
|
||||
jsonrpc-ts-client@^0.2.3:
|
||||
version "0.2.3"
|
||||
resolved "https://registry.yarnpkg.com/jsonrpc-ts-client/-/jsonrpc-ts-client-0.2.3.tgz#ec50c413d84041564e6c8a4003ab4bb360d5cfcc"
|
||||
integrity sha512-9uYpKrZKN3/3+9MYA/0vdhl9/esn59u6I9Qj6ohczxKwJ+e7DD4prf3i2nSdAl0Wlw5gBHZOL3wajSa1uiE16g==
|
||||
dependencies:
|
||||
axios "^0.24.0"
|
||||
debug "^4.3.3"
|
||||
|
||||
keyv@^4.5.3:
|
||||
version "4.5.4"
|
||||
resolved "https://registry.yarnpkg.com/keyv/-/keyv-4.5.4.tgz#a879a99e29452f942439f2a405e3af8b31d4de93"
|
||||
@ -4554,9 +4569,9 @@ levn@^0.4.1:
|
||||
type-check "~0.4.0"
|
||||
|
||||
libphonenumber-js@^1.10.53:
|
||||
version "1.11.8"
|
||||
resolved "https://registry.yarnpkg.com/libphonenumber-js/-/libphonenumber-js-1.11.8.tgz#697fdd36500a97bc672d7927d867edf34b4bd2a7"
|
||||
integrity sha512-0fv/YKpJBAgXKy0kaS3fnqoUVN8901vUYAKIGD/MWZaDfhJt1nZjPL3ZzdZBt/G8G8Hw2J1xOIrXWdNHFHPAvg==
|
||||
version "1.11.9"
|
||||
resolved "https://registry.yarnpkg.com/libphonenumber-js/-/libphonenumber-js-1.11.9.tgz#e653042b11da2b50b7ea3b206fa7ca998436ae99"
|
||||
integrity sha512-Zs5wf5HaWzW2/inlupe2tstl0I/Tbqo7lH20ZLr6Is58u7Dz2n+gRFGNlj9/gWxFvNfp9+YyDsiegjNhdixB9A==
|
||||
|
||||
lines-and-columns@^1.1.6:
|
||||
version "1.2.4"
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user