github.com/kaleido-io/firefly@v0.0.0-20210622132723-8b4b6aacb971/kat/src/lib/utils.ts (about) 1 // Copyright © 2021 Kaleido, Inc. 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 import { encode, decode } from 'bs58'; 16 import crypto from 'crypto'; 17 import axios, { AxiosRequestConfig } from 'axios'; 18 import { databaseCollectionName, indexes } from './interfaces'; 19 import { parseDN } from 'ldapjs'; 20 import { Logger } from './logging'; 21 22 export const constants = { 23 DATA_DIRECTORY: process.env.DATA_DIRECTORY || '/data', 24 LOG_LEVEL: process.env.LOG_LEVEL || 'info', 25 CONFIG_FILE_NAME: 'config.json', 26 SETTINGS_FILE_NAME: 'settings.json', 27 IPFS_TIMEOUT_MS: 15000, 28 DEFAULT_PAGINATION_LIMIT: 100, 29 EVENT_STREAM_WEBSOCKET_RECONNECTION_DELAY_SECONDS: 5, 30 DOC_EXCHANGE_ASSET_FOLDER_NAME: 'assets', 31 EVENT_STREAM_PING_TIMEOUT_SECONDS: 60, 32 ASSET_INSTANCE_TRADE_TIMEOUT_SECONDS: 15, 33 TRADE_AUTHORIZATION_TIMEOUT_SECONDS: 10, 34 DOCUMENT_EXCHANGE_TRANSFER_TIMEOUT_SECONDS: 15, 35 SUBSCRIBE_RETRY_INTERVAL: 5 * 1000, 36 APP2APP_BATCH_SIZE: parseInt(<string>process.env.APP2APP_BATCH_SIZE || "100"), 37 APP2APP_BATCH_TIMEOUT: parseInt(<string>process.env.APP2APP_BATCH_TIMEOUT || "250"), 38 APP2APP_READ_AHEAD: parseInt(<string>process.env.APP2APP_READ_AHEAD || "50"), 39 REST_API_CALL_MAX_ATTEMPTS: parseInt(<string>process.env.REST_API_CALL_MAX_ATTEMPTS || "5"), 40 REST_API_CALL_RETRY_DELAY_MS: parseInt(<string>process.env.REST_API_CALL_MAX_ATTEMPTS || "500"), 41 BATCH_ADD_TIMEOUT_MILLIS: parseInt(<string>process.env.BATCH_ADD_TIMEOUT_MILLIS || '30000'), 42 BATCH_TIMEOUT_OVERALL_MILLIS: parseInt(<string>process.env.BATCH_TIMEOUT_OVERALL_MILLIS || '2500'), 43 BATCH_TIMEOUT_ARRIVAL_MILLIS: parseInt(<string>process.env.BATCH_TIMEOUT_ARRIVAL_MILLIS || '250'), 44 BATCH_MAX_RECORDS: parseInt(<string>process.env.BATCH_MAX_RECORDS || '1000'), 45 BATCH_RETRY_INITIAL_DELAY_MILLIS: parseInt(<string>process.env.BATCH_RETRY_INITIAL_DELAY_MILLIS || '100'), 46 BATCH_RETRY_MAX_DELAY_MILLIS: parseInt(<string>process.env.BATCH_RETRY_MAX_DELAY_MILLIS || '10000'), 47 BATCH_RETRY_MULTIPLIER: parseFloat(<string>process.env.BATCH_RETRY_MULTIPLIER || '2.0'), 48 }; 49 50 const log = new Logger('utis.ts'); 51 52 export const databaseCollectionIndexes: { [name in databaseCollectionName]: indexes } = { 53 members: [{ fields: ['address'], unique: true }], 54 'asset-definitions': [{ fields: ['assetDefinitionID'], unique: true }], 55 'payment-definitions': [{ fields: ['paymentDefinitionID'], unique: true }], 56 'payment-instances': [{ fields: ['paymentInstanceID'], unique: true }], 57 'batches': [ 58 { fields: ['batchID'], unique: true }, // Primary key 59 { fields: ['type', 'author', 'completed', 'created'] }, // Search index for startup processing, and other queries 60 { fields: ['batchHash'] } // To retrieve a batch by its hash, in response to a blockchain event 61 ], 62 'state': [{ fields: ['key'], unique: true }], 63 }; 64 65 const ETHEREUM_ACCOUNT_REGEXP = /^0x[a-fA-F0-9]{40}$/; 66 67 const isValidX500Name = (name: string) => { 68 try { 69 parseDN(name); 70 } catch (e) { 71 return false; 72 } 73 return true; 74 }; 75 76 export const isAuthorValid = (author: string, protocol: string) => { 77 switch (protocol) { 78 case 'corda': 79 return isValidX500Name(author); 80 case 'ethereum': 81 return ETHEREUM_ACCOUNT_REGEXP.test(author); 82 } 83 } 84 85 export const requestKeys = { 86 ASSET_AUTHOR: 'author', 87 ASSET_DEFINITION_ID: 'assetDefinitionID', 88 ASSET_DESCRIPTION: 'description', 89 ASSET_CONTENT: 'content', 90 ASSET_IS_CONTENT_PRIVATE: 'isContentPrivate' 91 }; 92 93 export const contractEventSignaturesCorda = { 94 ASSET_DEFINITION_CREATED: 'io.kaleido.kat.states.AssetDefinitionCreated', 95 MEMBER_REGISTERED: 'io.kaleido.kat.states.MemberRegistered', 96 DESCRIBED_PAYMENT_DEFINITION_CREATED: 'io.kaleido.kat.states.DescribedPaymentDefinitionCreated', 97 PAYMENT_DEFINITION_CREATED: 'io.kaleido.kat.states.PaymentDefinitionCreated', 98 DESCRIBED_ASSET_INSTANCE_CREATED: 'io.kaleido.kat.states.DescribedAssetInstanceCreated', 99 ASSET_INSTANCE_BATCH_CREATED: 'io.kaleido.kat.states.AssetInstanceBatchCreated', 100 ASSET_INSTANCE_CREATED: 'io.kaleido.kat.states.AssetInstanceCreated', 101 DESCRIBED_PAYMENT_INSTANCE_CREATED: 'io.kaleido.kat.states.DescribedPaymentInstanceCreated', 102 PAYMENT_INSTANCE_CREATED: 'io.kaleido.kat.states.PaymentInstanceCreated', 103 ASSET_PROPERTY_SET: 'io.kaleido.kat.states.AssetInstancePropertySet' 104 } 105 106 export const contractEventSignatures = { 107 ASSET_DEFINITION_CREATED: 'AssetDefinitionCreated(bytes32,address,uint256)', 108 MEMBER_REGISTERED: 'MemberRegistered(address,string,string,string,string,uint256)', 109 DESCRIBED_PAYMENT_DEFINITION_CREATED: 'DescribedPaymentDefinitionCreated(bytes32,address,string,bytes32,uint256)', 110 PAYMENT_DEFINITION_CREATED: 'PaymentDefinitionCreated(bytes32,address,string,uint256)', 111 DESCRIBED_ASSET_INSTANCE_CREATED: 'DescribedAssetInstanceCreated(bytes32,bytes32,address,bytes32,bytes32,uint256)', 112 ASSET_INSTANCE_BATCH_CREATED: 'AssetInstanceBatchCreated(bytes32,address,uint256)', 113 ASSET_INSTANCE_CREATED: 'AssetInstanceCreated(bytes32,bytes32,address,bytes32,uint256)', 114 DESCRIBED_PAYMENT_INSTANCE_CREATED: 'DescribedPaymentInstanceCreated(bytes32,bytes32,address,address,uint256,bytes32,uint256)', 115 PAYMENT_INSTANCE_CREATED: 'PaymentInstanceCreated(bytes32,bytes32,address,address,uint256,uint256)', 116 ASSET_PROPERTY_SET: 'AssetInstancePropertySet(bytes32,bytes32,address,string,string,uint256)' 117 }; 118 119 export const getSha256 = (value: string) => crypto.createHash('sha256').update(value).digest('hex'); 120 121 export const ipfsHashToSha256 = (hash: string) => '0x' + decode(hash).slice(2).toString('hex'); 122 123 export const sha256ToIPFSHash = (short: string) => encode(Buffer.from('1220' + short.slice(2), 'hex')); 124 125 export const getTimestamp = () => { 126 return Math.round(new Date().getTime() / 1000); 127 }; 128 129 export const streamToString = (stream: NodeJS.ReadableStream): Promise<string> => { 130 const chunks: Buffer[] = []; 131 return new Promise((resolve, reject) => { 132 stream.on('data', chunk => chunks.push(chunk)); 133 stream.on('error', reject); 134 stream.on('end', () => resolve(Buffer.concat(chunks).toString('utf8'))); 135 }) 136 } 137 138 export const getUnstructuredFilePathInDocExchange = (assetInstanceID: string) => { 139 return `${constants.DOC_EXCHANGE_ASSET_FOLDER_NAME}/${assetInstanceID}`; 140 }; 141 142 export const uuidToHex = (uuid: string) => { 143 return '0x' + Buffer.from(uuid.replace(/-/g, '')).toString('hex'); 144 }; 145 146 export const hexToUuid = (hex: string) => { 147 const decodedTransferID = Buffer.from(hex.substr(2), 'hex').toString('utf-8'); 148 return decodedTransferID.substr(0, 8) + '-' + decodedTransferID.substr(8, 4) + '-' + 149 decodedTransferID.substr(12, 4) + '-' + decodedTransferID.substr(16, 4) + '-' + 150 decodedTransferID.substr(20, 12); 151 }; 152 153 export const axiosWithRetry = async (config: AxiosRequestConfig) => { 154 let attempts = 0; 155 let currentError; 156 while (attempts < constants.REST_API_CALL_MAX_ATTEMPTS) { 157 try { 158 return await axios(config); 159 } catch (err) { 160 const data = err.response?.data; 161 log.error(`${config.method} ${config.url} attempt ${attempts} [${err.response?.status}]`, (data && !data.on) ? data : err.stack) 162 if (err.response?.status === 404) { 163 throw err; 164 } else { 165 currentError = err; 166 attempts++; 167 await new Promise(resolve => setTimeout(resolve, constants.REST_API_CALL_RETRY_DELAY_MS)); 168 } 169 } 170 } 171 throw currentError; 172 }; 173 174 export function getLogger(label: string) { 175 return new Logger(label); 176 }