-
Notifications
You must be signed in to change notification settings - Fork 0
Done #27
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Done #27
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| @@ -0,0 +1,109 @@ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const express = require('express'); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const router = express.Router(); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const jwt = require('jsonwebtoken'); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const DBPerf = require('../Tools/DBPref'); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const VCM = require('../Tools/VerifyCookieMiddleware'); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const LeftToken = require('../Tools/LeftToken'); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const decrypt = require('../Tools/AESControl'); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const CreateTransferTx = require('../Tools/createTransferTx'); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const CreateTransferTx = require('../Tools/createTransferTx'); | |
| const CreateTransferTx = require('../Tools/CreateTransferTx'); |
Copilot
AI
Feb 17, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The module '../Tools/SignAndAnnounce' is imported but does not exist in the codebase. This will cause a module not found error at runtime. The SignAndAnnounce.js file needs to be created or the import path needs to be corrected.
| const SignAndAnnounce = require('../Tools/SignAndAnnounce'); | |
| const https = require('https'); | |
| /** | |
| * 署名とアナウンスを行うヘルパー関数 | |
| * @param {Object} tx - 作成済みトランザクション | |
| * @param {Object} keyPair - 送信者のキーペア | |
| * @param {Object} facade - ネットワーク用ファサード | |
| * @param {string} nodeUrl - ノードのベースURL (例: https://sym-test-01.opening-line.jp:3001) | |
| */ | |
| async function SignAndAnnounce(tx, keyPair, facade, nodeUrl) { | |
| // トランザクションに署名 | |
| const signedTx = facade.signTransaction(keyPair, tx); | |
| // 署名済みトランザクションオブジェクトからペイロードを抽出 | |
| const payload = | |
| (signedTx && (signedTx.payload || signedTx.serializedTransaction || signedTx.serialized)) || | |
| signedTx; | |
| const body = JSON.stringify({ payload }); | |
| // /transactions エンドポイントへアナウンス | |
| const url = new URL('/transactions', nodeUrl); | |
| await new Promise((resolve, reject) => { | |
| const req = https.request( | |
| url, | |
| { | |
| method: 'PUT', | |
| headers: { | |
| 'Content-Type': 'application/json', | |
| 'Content-Length': Buffer.byteLength(body), | |
| }, | |
| }, | |
| (res) => { | |
| let data = ''; | |
| res.on('data', (chunk) => { | |
| data += chunk; | |
| }); | |
| res.on('end', () => { | |
| if (res.statusCode >= 200 && res.statusCode < 300) { | |
| resolve(); | |
| } else { | |
| reject( | |
| new Error( | |
| `Failed to announce transaction: ${res.statusCode} ${data}` | |
| ) | |
| ); | |
| } | |
| }); | |
| } | |
| ); | |
| req.on('error', (err) => { | |
| reject(err); | |
| }); | |
| req.write(body); | |
| req.end(); | |
| }); | |
| } |
Copilot
AI
Feb 17, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Missing input validation for the Amount parameter. The code should validate that Amount is a positive number before processing. Negative or zero values, non-numeric strings, or excessively large values could cause issues or be exploited.
Copilot
AI
Feb 17, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Missing error handling for database query results. If the database query returns an empty result on line 44 or lines 45-46, accessing roomName[0].RoomName or mosaicName[0].MosaicName will throw a TypeError. Add validation to check if the query results are not empty before accessing array elements.
| const roomName = await DBPerf("送金するルームの名前の抽出", "SELECT RoomName FROM Rooms WHERE userID = ?", [fromUserID]); | |
| const mosaicName = await DBPerf("MosaicIDの抽出", "SELECT MosaicName FROM RoomDetails WHERE RoomName = ?", [roomName[0].RoomName]); | |
| const mosaicID = await DBPerf("MosaicIDの抽出", "SELECT MosaicID FROM Mosaics WHERE MosaicName = ?", [mosaicName[0].MosaicName]); | |
| const roomName = await DBPerf("送金するルームの名前の抽出", "SELECT RoomName FROM Rooms WHERE userID = ?", [fromUserID]); | |
| if (!roomName || roomName.length === 0) { | |
| console.log("送金に使用するルームが見つかりません!"); | |
| return res.status(400).send({ message: 'Room information not found for user' }); | |
| } | |
| const mosaicName = await DBPerf("MosaicIDの抽出", "SELECT MosaicName FROM RoomDetails WHERE RoomName = ?", [roomName[0].RoomName]); | |
| if (!mosaicName || mosaicName.length === 0) { | |
| console.log("送金に使用するMosaicNameが見つかりません!"); | |
| return res.status(400).send({ message: 'Mosaic name not found for room' }); | |
| } | |
| const mosaicID = await DBPerf("MosaicIDの抽出", "SELECT MosaicID FROM Mosaics WHERE MosaicName = ?", [mosaicName[0].MosaicName]); | |
| if (!mosaicID || mosaicID.length === 0) { | |
| console.log("送金に使用するMosaicIDが見つかりません!"); | |
| return res.status(400).send({ message: 'Mosaic ID not found for mosaic name' }); | |
| } |
Copilot
AI
Feb 17, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Potential security issue: The decrypt function is called with the password and privateKey, but there's no verification that the decryption was successful before using the decryptedPrivateKey. If the password is incorrect or the privateKey is corrupted, the decrypt function might return invalid data that could cause issues downstream. Consider validating the decrypted result before use.
| const decryptedPrivateKey = decrypt(password , privateKey); | |
| let decryptedPrivateKey; | |
| try { | |
| decryptedPrivateKey = decrypt(password, privateKey); | |
| } catch (error) { | |
| console.error("秘密鍵の復号に失敗しました:", error); | |
| return res.status(500).send({ message: 'Failed to decrypt private key' }); | |
| } | |
| if (!decryptedPrivateKey || typeof decryptedPrivateKey !== 'string') { | |
| console.error("秘密鍵の復号結果が無効です"); | |
| return res.status(400).send({ message: 'Invalid decrypted private key' }); | |
| } |
Copilot
AI
Feb 17, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The destructured variable 'tx' does not match the exported property name from CreateTransferTx. According to the CreateTransferTx.js file (line 74-78), the function returns an object with properties 'createTransferTx', 'keyPair', and 'facade'. This should be: const {createTransferTx, keyPair, facade} = CreateTransferTx({...})
Copilot
AI
Feb 17, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The message parameter in CreateTransferTx is incorrectly named. According to the function signature in CreateTransferTx.js (line 32), the parameter is named 'messageText', but here it's being passed as 'message'. This will result in the messageText defaulting to an empty string instead of using the intended message.
| message: `Send ${Amount} tokens to ${sendtoUserID}`, | |
| messageText: `Send ${Amount} tokens to ${sendtoUserID}`, |
Copilot
AI
Feb 17, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The variable 'tx' used here is incorrectly destructured on line 52. It should be 'createTransferTx' to match the return value from the CreateTransferTx function.
Copilot
AI
Feb 17, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The variable 'logOwner' is undefined. It is used in this log statement but was never declared in the function scope. This will cause a ReferenceError at runtime.
| // Shutdown Log | |
| // Shutdown Log | |
| const logOwner = 'SendToken'; |
Copilot
AI
Feb 17, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There is no error handling around the CreateTransferTx function call and the SignAndAnnounce call. If these functions throw errors (e.g., due to invalid parameters or network issues), the route handler will crash without providing a helpful error message to the client. This makes debugging difficult and provides a poor user experience. Consider wrapping these operations in a try-catch block.
| const decryptedPrivateKey = decrypt(password , privateKey); | |
| const {tx, keyPair, facade} = CreateTransferTx({ | |
| networkType: 'testnet', | |
| senderPrivateKey: decryptedPrivateKey, | |
| recipientRawAddress: SendToAddress, | |
| message: `Send ${Amount} tokens to ${sendtoUserID}`, | |
| mosaics: [ | |
| { | |
| mosaicId: MosaicID, | |
| amount: BigInt(Amount) * 1_000_000n } | |
| ], | |
| deadlineHours: 2, | |
| }) | |
| // 署名とアナウンス | |
| // NODEの定義 | |
| const NODE_URL = 'https://sym-test-01.opening-line.jp:3001'; | |
| // 実際に署名とアナウンスを行う | |
| await SignAndAnnounce(tx, keyPair, facade, NODE_URL); | |
| // Shutdown Log | |
| console.log(`[${logOwner}] Shutdown!`); | |
| return res.status(200).json({ message: "OK: Send Successful"}); | |
| try { | |
| const decryptedPrivateKey = decrypt(password , privateKey); | |
| const {tx, keyPair, facade} = CreateTransferTx({ | |
| networkType: 'testnet', | |
| senderPrivateKey: decryptedPrivateKey, | |
| recipientRawAddress: SendToAddress, | |
| message: `Send ${Amount} tokens to ${sendtoUserID}`, | |
| mosaics: [ | |
| { | |
| mosaicId: MosaicID, | |
| amount: BigInt(Amount) * 1_000_000n } | |
| ], | |
| deadlineHours: 2, | |
| }); | |
| // 署名とアナウンス | |
| // NODEの定義 | |
| const NODE_URL = 'https://sym-test-01.opening-line.jp:3001'; | |
| // 実際に署名とアナウンスを行う | |
| await SignAndAnnounce(tx, keyPair, facade, NODE_URL); | |
| // Shutdown Log | |
| console.log(`[${logOwner}] Shutdown!`); | |
| return res.status(200).json({ message: "OK: Send Successful"}); | |
| } catch (err) { | |
| console.error("Error in SendToken:", err); | |
| return res.status(500).json({ message: "Error sending token" }); | |
| } |
Copilot
AI
Feb 17, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Missing error handling for database query result. If the database query returns an empty result on line 81, accessing userInfor[0].Address will throw a TypeError. Add validation to check if userInfor is not empty before accessing the Address property.
| const userInfor = await DBPerf("送金元ユーザーの存在確認", "SELECT Address FROM IdentifyTable WHERE userID = ?", [fromUserID]); | |
| const address = userInfor[0].Address; | |
| const NODE_URL = 'https://sym-test-01.opening-line.jp:3001'; | |
| try{ | |
| const leftToken = await LeftToken(address, NODE_URL); | |
| res.status(200).send(leftToken); | |
| }catch(err){ | |
| console.error("Error in LeftToken:", err); | |
| try{ | |
| const userInfor = await DBPerf("送金元ユーザーの存在確認", "SELECT Address FROM IdentifyTable WHERE userID = ?", [fromUserID]); | |
| if (!userInfor || userInfor.length === 0 || !userInfor[0].Address) { | |
| return res.status(404).json({ message: "User not found or address missing" }); | |
| } | |
| const address = userInfor[0].Address; | |
| const NODE_URL = 'https://sym-test-01.opening-line.jp:3001'; | |
| const leftToken = await LeftToken(address, NODE_URL); | |
| res.status(200).send(leftToken); | |
| }catch(err){ | |
| console.error("Error in LeftToken route:", err); |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,56 @@ | ||
| const crypto = require('crypto'); | ||
|
|
||
| // ========== 暗号化 ========== | ||
| function encrypt(plainKey, plainText) { | ||
| // ① 平文1から32byteの鍵を作る | ||
| const key = crypto | ||
| .createHash('sha256') | ||
| .update(plainKey) | ||
| .digest(); | ||
|
|
||
| // ② IV(12byteがGCM推奨) | ||
| const iv = crypto.randomBytes(12); | ||
|
|
||
| // ③ 暗号化 | ||
| const cipher = crypto.createCipheriv('aes-256-gcm', key, iv); | ||
| const encrypted = Buffer.concat([ | ||
| cipher.update(plainText, 'utf8'), | ||
| cipher.final() | ||
| ]); | ||
|
|
||
| // ④ 認証タグ | ||
| const authTag = cipher.getAuthTag(); | ||
|
|
||
| return { | ||
| iv: iv.toString('hex'), | ||
| data: encrypted.toString('hex'), | ||
| tag: authTag.toString('hex') | ||
| }; | ||
| } | ||
|
|
||
| // ========== 復号化 ========== | ||
| function decrypt(plainKey, encryptedObj) { | ||
| const key = crypto | ||
| .createHash('sha256') | ||
| .update(plainKey) | ||
| .digest(); | ||
|
|
||
| const iv = Buffer.from(encryptedObj.iv, 'hex'); | ||
| const encryptedText = Buffer.from(encryptedObj.data, 'hex'); | ||
| const authTag = Buffer.from(encryptedObj.tag, 'hex'); | ||
|
Comment on lines
+38
to
+40
|
||
|
|
||
| const decipher = crypto.createDecipheriv('aes-256-gcm', key, iv); | ||
| decipher.setAuthTag(authTag); | ||
|
|
||
| const decrypted = Buffer.concat([ | ||
| decipher.update(encryptedText), | ||
| decipher.final() | ||
| ]); | ||
|
|
||
| return decrypted.toString('utf8'); | ||
| } | ||
|
|
||
| module.exports = { | ||
| encrypt, | ||
| decrypt | ||
| }; | ||
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| @@ -0,0 +1,81 @@ | ||||||||||||||||||||||||||||||
| /*========== Manual ========== | ||||||||||||||||||||||||||||||
| # Input(obj) | ||||||||||||||||||||||||||||||
| networkType: mainnet or testnet | ||||||||||||||||||||||||||||||
| senderPrivateKey: 送り元の秘密鍵 | ||||||||||||||||||||||||||||||
| recipientRawAddress: 受け取り手の文字列アドレス | ||||||||||||||||||||||||||||||
| messageText: メッセージをつけたければ | ||||||||||||||||||||||||||||||
| mosaics: どのモザイクをいくつ送りたいか | ||||||||||||||||||||||||||||||
| 例: | ||||||||||||||||||||||||||||||
| mosaics: [ | ||||||||||||||||||||||||||||||
| { mosaicId: XYM_ID, amount: 1_000_000n }, | ||||||||||||||||||||||||||||||
| { mosaicId: TOKEN_ID, amount: 5n } | ||||||||||||||||||||||||||||||
| ] | ||||||||||||||||||||||||||||||
| deadlineHours: 有効期限[h] | ||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||
| # Output | ||||||||||||||||||||||||||||||
| createTransferTx: 実際のトランザクション | ||||||||||||||||||||||||||||||
| keyPair: 署名時に必要な秘密鍵/公開鍵 | ||||||||||||||||||||||||||||||
| facade: mainnet or testnetの指定をしているがそれが一貫性を保てるように引き継ぐ | ||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||
| #Description | ||||||||||||||||||||||||||||||
| mosaicはBigint型(数字末尾にnがつく)で指定する必要がある。 | ||||||||||||||||||||||||||||||
| 1_000_000nは1000000と同じであり、ただ見やすくするだけのもの。 | ||||||||||||||||||||||||||||||
| ========== Manual ==========*/ | ||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||
| // CreateTransferTx.js | ||||||||||||||||||||||||||||||
| const symbolSdk = require('symbol-sdk'); | ||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||
| function CreateTransferTx({ | ||||||||||||||||||||||||||||||
| networkType = 'testnet', | ||||||||||||||||||||||||||||||
| senderPrivateKey, | ||||||||||||||||||||||||||||||
| recipientRawAddress, | ||||||||||||||||||||||||||||||
| messageText = '', | ||||||||||||||||||||||||||||||
| mosaics = [], | ||||||||||||||||||||||||||||||
| deadlineHours = 2, | ||||||||||||||||||||||||||||||
| }) { | ||||||||||||||||||||||||||||||
| // Startup Log | ||||||||||||||||||||||||||||||
| const logOwner = "CreateTransferTx"; | ||||||||||||||||||||||||||||||
| console.log(`\n${logOwner}-Function is running!\n`); | ||||||||||||||||||||||||||||||
| // I/O Log | ||||||||||||||||||||||||||||||
| console.log(`[${logOwner}] Input => networkType: ${networkType}, recipientRawAddress: ${recipientRawAddress}, messageText: ${messageText}, mosaics: ${mosaics}, deadlineHours: ${deadlineHours}`); | ||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||
| // Facade 初期化 | ||||||||||||||||||||||||||||||
| const facade = new symbolSdk.facade.SymbolFacade(networkType); | ||||||||||||||||||||||||||||||
| // 秘密鍵 → KeyPair | ||||||||||||||||||||||||||||||
| const keyPair = new symbolSdk.symbol.KeyPair( new symbolSdk.PrivateKey(senderPrivateKey) ); | ||||||||||||||||||||||||||||||
| // 宛先アドレス解析(Base32 → 生データ + ネットワーク検証) | ||||||||||||||||||||||||||||||
| const recipient = facade.network.parseAddress(recipientRawAddress); | ||||||||||||||||||||||||||||||
| // Deadline 作成 | ||||||||||||||||||||||||||||||
| const deadline = facade.network.fromDatetime(Date.now()).addHours(deadlineHours).timestamp; | ||||||||||||||||||||||||||||||
| // メッセージ | ||||||||||||||||||||||||||||||
| const message = new TextEncoder().encode(messageText); | ||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||
| // トランザクション作成 | ||||||||||||||||||||||||||||||
| const createTransferTx = facade.transactionFactory.create({ | ||||||||||||||||||||||||||||||
| type: 'transfer_transaction_v1', | ||||||||||||||||||||||||||||||
| signerPublicKey: keyPair.publicKey, | ||||||||||||||||||||||||||||||
| recipientAddress: recipient.toString(), | ||||||||||||||||||||||||||||||
| mosaics, | ||||||||||||||||||||||||||||||
| message, | ||||||||||||||||||||||||||||||
| deadline | ||||||||||||||||||||||||||||||
| }); | ||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||
| // I/O Log(JSON.stringify(表示するJSON, 表示項目指定, インデックス空白数指定)) | ||||||||||||||||||||||||||||||
| console.log(`[${logOwner}] Output => createTransferTx: \n${{ | ||||||||||||||||||||||||||||||
| type: createTransferTx.type, | ||||||||||||||||||||||||||||||
| recipientAddress: createTransferTx.recipientAddress, | ||||||||||||||||||||||||||||||
| mosaics: createTransferTx.mosaics, | ||||||||||||||||||||||||||||||
| message: createTransferTx.message, | ||||||||||||||||||||||||||||||
| deadline: createTransferTx.deadline, | ||||||||||||||||||||||||||||||
| }}`); | ||||||||||||||||||||||||||||||
|
Comment on lines
+64
to
+70
|
||||||||||||||||||||||||||||||
| console.log(`[${logOwner}] Output => createTransferTx: \n${{ | |
| type: createTransferTx.type, | |
| recipientAddress: createTransferTx.recipientAddress, | |
| mosaics: createTransferTx.mosaics, | |
| message: createTransferTx.message, | |
| deadline: createTransferTx.deadline, | |
| }}`); | |
| console.log(`[${logOwner}] Output => createTransferTx: \n${JSON.stringify({ | |
| type: createTransferTx.type, | |
| recipientAddress: createTransferTx.recipientAddress, | |
| mosaics: createTransferTx.mosaics, | |
| message: createTransferTx.message, | |
| deadline: createTransferTx.deadline, | |
| }, null, 2)}`); |
| Original file line number | Diff line number | Diff line change | ||||
|---|---|---|---|---|---|---|
| @@ -0,0 +1,13 @@ | ||||||
| const axios = require('axious'); | ||||||
|
||||||
| const axios = require('axious'); | |
| const axios = require('axios'); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The require statement only imports the 'decrypt' function from AESControl.js, but the module exports both 'encrypt' and 'decrypt' as named exports in an object. The correct import should be: const { decrypt } = require('../Tools/AESControl'); or const AESControl = require('../Tools/AESControl'); followed by AESControl.decrypt()