From 8f847d368962c2bccb32aae8c4d52c7f64ef2cc6 Mon Sep 17 00:00:00 2001 From: QuickMythril Date: Fri, 21 Apr 2023 19:30:29 -0400 Subject: [PATCH 01/12] Upgraded to TLSv1.3 --- src/main/java/org/qortal/api/ApiService.java | 2 +- src/main/java/org/qortal/api/DomainMapService.java | 2 +- src/main/java/org/qortal/api/GatewayService.java | 2 +- src/main/java/org/qortal/crypto/TrustlessSSLSocketFactory.java | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/main/java/org/qortal/api/ApiService.java b/src/main/java/org/qortal/api/ApiService.java index 059b8971..1ee733c6 100644 --- a/src/main/java/org/qortal/api/ApiService.java +++ b/src/main/java/org/qortal/api/ApiService.java @@ -96,7 +96,7 @@ public class ApiService { throw new RuntimeException("Failed to start SSL API due to broken keystore"); // BouncyCastle-specific SSLContext build - SSLContext sslContext = SSLContext.getInstance("TLS", "BCJSSE"); + SSLContext sslContext = SSLContext.getInstance("TLSv1.3", "BCJSSE"); KeyManagerFactory keyManagerFactory = KeyManagerFactory.getInstance("PKIX", "BCJSSE"); KeyStore keyStore = KeyStore.getInstance(KeyStore.getDefaultType(), "BC"); diff --git a/src/main/java/org/qortal/api/DomainMapService.java b/src/main/java/org/qortal/api/DomainMapService.java index 3b81d94c..8b791121 100644 --- a/src/main/java/org/qortal/api/DomainMapService.java +++ b/src/main/java/org/qortal/api/DomainMapService.java @@ -69,7 +69,7 @@ public class DomainMapService { throw new RuntimeException("Failed to start SSL API due to broken keystore"); // BouncyCastle-specific SSLContext build - SSLContext sslContext = SSLContext.getInstance("TLS", "BCJSSE"); + SSLContext sslContext = SSLContext.getInstance("TLSv1.3", "BCJSSE"); KeyManagerFactory keyManagerFactory = KeyManagerFactory.getInstance("PKIX", "BCJSSE"); KeyStore keyStore = KeyStore.getInstance(KeyStore.getDefaultType(), "BC"); diff --git a/src/main/java/org/qortal/api/GatewayService.java b/src/main/java/org/qortal/api/GatewayService.java index 51191af3..24a7b7c9 100644 --- a/src/main/java/org/qortal/api/GatewayService.java +++ b/src/main/java/org/qortal/api/GatewayService.java @@ -69,7 +69,7 @@ public class GatewayService { throw new RuntimeException("Failed to start SSL API due to broken keystore"); // BouncyCastle-specific SSLContext build - SSLContext sslContext = SSLContext.getInstance("TLS", "BCJSSE"); + SSLContext sslContext = SSLContext.getInstance("TLSv1.3", "BCJSSE"); KeyManagerFactory keyManagerFactory = KeyManagerFactory.getInstance("PKIX", "BCJSSE"); KeyStore keyStore = KeyStore.getInstance(KeyStore.getDefaultType(), "BC"); diff --git a/src/main/java/org/qortal/crypto/TrustlessSSLSocketFactory.java b/src/main/java/org/qortal/crypto/TrustlessSSLSocketFactory.java index aba1955e..f723e651 100644 --- a/src/main/java/org/qortal/crypto/TrustlessSSLSocketFactory.java +++ b/src/main/java/org/qortal/crypto/TrustlessSSLSocketFactory.java @@ -28,7 +28,7 @@ public abstract class TrustlessSSLSocketFactory { private static final SSLContext sc; static { try { - sc = SSLContext.getInstance("SSL"); + sc = SSLContext.getInstance("TLSv1.3"); sc.init(null, TRUSTLESS_MANAGER, new java.security.SecureRandom()); } catch (Exception e) { throw new RuntimeException(e); From 655073c524d6b53f8bf591cd6d62fc8fb5218f50 Mon Sep 17 00:00:00 2001 From: QuickMythril Date: Thu, 25 May 2023 04:41:03 -0400 Subject: [PATCH 02/12] Added 2m timeout for GET_WALLET_BALANCE action --- src/main/resources/q-apps/q-apps.js | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/main/resources/q-apps/q-apps.js b/src/main/resources/q-apps/q-apps.js index dae20e5d..d26b7791 100644 --- a/src/main/resources/q-apps/q-apps.js +++ b/src/main/resources/q-apps/q-apps.js @@ -467,6 +467,10 @@ function getDefaultTimeout(action) { // Allow extra time for other actions that create transactions, even if there is no PoW return 5 * 60 * 1000; + case "GET_WALLET_BALANCE": + // Getting a wallet balance can take a while, if there are many transactions + return 2 * 60 * 1000; + default: break; } From d260c0a9a9fab04c8dc0d28ee9fc026ca1334729 Mon Sep 17 00:00:00 2001 From: QuickMythril Date: Thu, 25 May 2023 16:35:08 -0400 Subject: [PATCH 03/12] Updated info on foreign coin fees --- Q-Apps.md | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/Q-Apps.md b/Q-Apps.md index ca750e7d..83718507 100644 --- a/Q-Apps.md +++ b/Q-Apps.md @@ -576,14 +576,15 @@ let res = await qortalRequest({ ``` ### Send foreign coin to address -_Requires user approval_ +_Requires user approval_
+Note: default fees can be found [here](https://github.com/Qortal/qortal-ui/blob/master/plugins/plugins/core/qdn/browser/browser.src.js#L205-L209). ``` let res = await qortalRequest({ action: "SEND_COIN", coin: "LTC", destinationAddress: "LSdTvMHRm8sScqwCi6x9wzYQae8JeZhx6y", amount: 1.00000000, // 1 LTC - fee: 0.00000020 // fee per byte + fee: 20 // Optional sats per byte (default fee used if omitted, recommended) - not used for QORT or ARRR }); ``` From 13da0e8a7a04dc227907dd1f1b61a7c04c59ec3e Mon Sep 17 00:00:00 2001 From: QuickMythril Date: Thu, 25 May 2023 17:26:29 -0400 Subject: [PATCH 04/12] Adjusted fee info to long format --- Q-Apps.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Q-Apps.md b/Q-Apps.md index 83718507..83f2a356 100644 --- a/Q-Apps.md +++ b/Q-Apps.md @@ -584,7 +584,7 @@ let res = await qortalRequest({ coin: "LTC", destinationAddress: "LSdTvMHRm8sScqwCi6x9wzYQae8JeZhx6y", amount: 1.00000000, // 1 LTC - fee: 20 // Optional sats per byte (default fee used if omitted, recommended) - not used for QORT or ARRR + fee: 0.00000020 // Optional fee per byte (default fee used if omitted, recommended) - not used for QORT or ARRR }); ``` From 27afcf12bfe1d0e5cccad73ef2fb548b12105d3d Mon Sep 17 00:00:00 2001 From: QuickMythril Date: Mon, 29 May 2023 04:07:52 -0400 Subject: [PATCH 05/12] Prepared files for Japanese translations --- .../resources/i18n/ApiError_jp.properties | 83 ++++++++ src/main/resources/i18n/SysTray_jp.properties | 48 +++++ .../i18n/TransactionValidity_jp.properties | 195 ++++++++++++++++++ 3 files changed, 326 insertions(+) create mode 100644 src/main/resources/i18n/ApiError_jp.properties create mode 100644 src/main/resources/i18n/SysTray_jp.properties create mode 100644 src/main/resources/i18n/TransactionValidity_jp.properties diff --git a/src/main/resources/i18n/ApiError_jp.properties b/src/main/resources/i18n/ApiError_jp.properties new file mode 100644 index 00000000..a9ab51c2 --- /dev/null +++ b/src/main/resources/i18n/ApiError_jp.properties @@ -0,0 +1,83 @@ +#Generated by ResourceBundle Editor (http://essiembre.github.io/eclipse-rbe/) +# Keys are from api.ApiError enum + +# "localeLang": "en", + +### Common ### +JSON = failed to parse JSON message + +INSUFFICIENT_BALANCE = insufficient balance + +UNAUTHORIZED = API call unauthorized + +REPOSITORY_ISSUE = repository error + +NON_PRODUCTION = this API call is not permitted for production systems + +BLOCKCHAIN_NEEDS_SYNC = blockchain needs to synchronize first + +NO_TIME_SYNC = no clock synchronization yet + +### Validation ### +INVALID_SIGNATURE = invalid signature + +INVALID_ADDRESS = invalid address + +INVALID_PUBLIC_KEY = invalid public key + +INVALID_DATA = invalid data + +INVALID_NETWORK_ADDRESS = invalid network address + +ADDRESS_UNKNOWN = account address unknown + +INVALID_CRITERIA = invalid search criteria + +INVALID_REFERENCE = invalid reference + +TRANSFORMATION_ERROR = could not transform JSON into transaction + +INVALID_PRIVATE_KEY = invalid private key + +INVALID_HEIGHT = invalid block height + +CANNOT_MINT = account cannot mint + +### Blocks ### +BLOCK_UNKNOWN = block unknown + +### Transactions ### +TRANSACTION_UNKNOWN = transaction unknown + +PUBLIC_KEY_NOT_FOUND = public key not found + +# this one is special in that caller expected to pass two additional strings, hence the two %s +TRANSACTION_INVALID = transaction invalid: %s (%s) + +### Naming ### +NAME_UNKNOWN = name unknown + +### Asset ### +INVALID_ASSET_ID = invalid asset ID + +INVALID_ORDER_ID = invalid asset order ID + +ORDER_UNKNOWN = unknown asset order ID + +### Groups ### +GROUP_UNKNOWN = group unknown + +### Foreign Blockchain ### +FOREIGN_BLOCKCHAIN_NETWORK_ISSUE = foreign blockchain or ElectrumX network issue + +FOREIGN_BLOCKCHAIN_BALANCE_ISSUE = insufficient balance on foreign blockchain + +FOREIGN_BLOCKCHAIN_TOO_SOON = too soon to broadcast foreign blockchain transaction (LockTime/median block time) + +### Trade Portal ### +ORDER_SIZE_TOO_SMALL = order amount too low + +### Data ### +FILE_NOT_FOUND = file not found + +NO_REPLY = peer didn't reply within the allowed time diff --git a/src/main/resources/i18n/SysTray_jp.properties b/src/main/resources/i18n/SysTray_jp.properties new file mode 100644 index 00000000..39940be0 --- /dev/null +++ b/src/main/resources/i18n/SysTray_jp.properties @@ -0,0 +1,48 @@ +#Generated by ResourceBundle Editor (http://essiembre.github.io/eclipse-rbe/) +# SysTray pop-up menu + +APPLYING_UPDATE_AND_RESTARTING = Applying automatic update and restarting... + +AUTO_UPDATE = Auto Update + +BLOCK_HEIGHT = height + +BLOCKS_REMAINING = blocks remaining + +BUILD_VERSION = Build version + +CHECK_TIME_ACCURACY = Check time accuracy + +CONNECTING = Connecting + +CONNECTION = connection + +CONNECTIONS = connections + +CREATING_BACKUP_OF_DB_FILES = Creating backup of database files... + +DB_BACKUP = Database Backup + +DB_CHECKPOINT = Database Checkpoint + +DB_MAINTENANCE = Database Maintenance + +EXIT = Exit + +LITE_NODE = Lite node + +MINTING_DISABLED = NOT minting + +MINTING_ENABLED = \u2714 Minting + +OPEN_UI = Open UI + +PERFORMING_DB_CHECKPOINT = Saving uncommitted database changes... + +PERFORMING_DB_MAINTENANCE = Performing scheduled maintenance... + +SYNCHRONIZE_CLOCK = Synchronize clock + +SYNCHRONIZING_BLOCKCHAIN = Synchronizing + +SYNCHRONIZING_CLOCK = Synchronizing clock diff --git a/src/main/resources/i18n/TransactionValidity_jp.properties b/src/main/resources/i18n/TransactionValidity_jp.properties new file mode 100644 index 00000000..3f33771d --- /dev/null +++ b/src/main/resources/i18n/TransactionValidity_jp.properties @@ -0,0 +1,195 @@ +# + +ACCOUNT_ALREADY_EXISTS = account already exists + +ACCOUNT_CANNOT_REWARD_SHARE = account cannot reward-share + +ADDRESS_ABOVE_RATE_LIMIT = address reached specified rate limit + +ADDRESS_BLOCKED = this address is blocked + +ALREADY_GROUP_ADMIN = already group admin + +ALREADY_GROUP_MEMBER = already group member + +ALREADY_VOTED_FOR_THAT_OPTION = already voted for that option + +ASSET_ALREADY_EXISTS = asset already exists + +ASSET_DOES_NOT_EXIST = asset does not exist + +ASSET_DOES_NOT_MATCH_AT = asset does not match AT's asset + +ASSET_NOT_SPENDABLE = asset is not spendable + +AT_ALREADY_EXISTS = AT already exists + +AT_IS_FINISHED = AT has finished + +AT_UNKNOWN = AT unknown + +BAN_EXISTS = ban already exists + +BAN_UNKNOWN = ban unknown + +BANNED_FROM_GROUP = banned from group + +BUYER_ALREADY_OWNER = buyer is already owner + +CLOCK_NOT_SYNCED = clock not synchronized + +DUPLICATE_MESSAGE = address sent duplicate message + +DUPLICATE_OPTION = duplicate option + +GROUP_ALREADY_EXISTS = group already exists + +GROUP_APPROVAL_DECIDED = group-approval already decided + +GROUP_APPROVAL_NOT_REQUIRED = group-approval not required + +GROUP_DOES_NOT_EXIST = group does not exist + +GROUP_ID_MISMATCH = group ID mismatch + +GROUP_OWNER_CANNOT_LEAVE = group owner cannot leave group + +HAVE_EQUALS_WANT = have-asset is the same as want-asset + +INCORRECT_NONCE = incorrect PoW nonce + +INSUFFICIENT_FEE = insufficient fee + +INVALID_ADDRESS = invalid address + +INVALID_AMOUNT = invalid amount + +INVALID_ASSET_OWNER = invalid asset owner + +INVALID_AT_TRANSACTION = invalid AT transaction + +INVALID_AT_TYPE_LENGTH = invalid AT 'type' length + +INVALID_BUT_OK = invalid but OK + +INVALID_CREATION_BYTES = invalid creation bytes + +INVALID_DATA_LENGTH = invalid data length + +INVALID_DESCRIPTION_LENGTH = invalid description length + +INVALID_GROUP_APPROVAL_THRESHOLD = invalid group-approval threshold + +INVALID_GROUP_BLOCK_DELAY = invalid group-approval block delay + +INVALID_GROUP_ID = invalid group ID + +INVALID_GROUP_OWNER = invalid group owner + +INVALID_LIFETIME = invalid lifetime + +INVALID_NAME_LENGTH = invalid name length + +INVALID_NAME_OWNER = invalid name owner + +INVALID_OPTION_LENGTH = invalid options length + +INVALID_OPTIONS_COUNT = invalid options count + +INVALID_ORDER_CREATOR = invalid order creator + +INVALID_PAYMENTS_COUNT = invalid payments count + +INVALID_PUBLIC_KEY = invalid public key + +INVALID_QUANTITY = invalid quantity + +INVALID_REFERENCE = invalid reference + +INVALID_RETURN = invalid return + +INVALID_REWARD_SHARE_PERCENT = invalid reward-share percent + +INVALID_SELLER = invalid seller + +INVALID_TAGS_LENGTH = invalid 'tags' length + +INVALID_TIMESTAMP_SIGNATURE = invalid timestamp signature + +INVALID_TX_GROUP_ID = invalid transaction group ID + +INVALID_VALUE_LENGTH = invalid 'value' length + +INVITE_UNKNOWN = group invite unknown + +JOIN_REQUEST_EXISTS = group join request already exists + +MAXIMUM_REWARD_SHARES = already at maximum number of reward-shares for this account + +MISSING_CREATOR = missing creator + +MULTIPLE_NAMES_FORBIDDEN = multiple registered names per account is forbidden + +NAME_ALREADY_FOR_SALE = name already for sale + +NAME_ALREADY_REGISTERED = name already registered + +NAME_BLOCKED = this name is blocked + +NAME_DOES_NOT_EXIST = name does not exist + +NAME_NOT_FOR_SALE = name is not for sale + +NAME_NOT_NORMALIZED = name not in Unicode 'normalized' form + +NEGATIVE_AMOUNT = invalid/negative amount + +NEGATIVE_FEE = invalid/negative fee + +NEGATIVE_PRICE = invalid/negative price + +NO_BALANCE = insufficient balance + +NO_BLOCKCHAIN_LOCK = node's blockchain currently busy + +NO_FLAG_PERMISSION = account does not have that permission + +NOT_GROUP_ADMIN = account is not a group admin + +NOT_GROUP_MEMBER = account is not a group member + +NOT_MINTING_ACCOUNT = account cannot mint + +NOT_YET_RELEASED = feature not yet released + +OK = OK + +ORDER_ALREADY_CLOSED = asset trade order is already closed + +ORDER_DOES_NOT_EXIST = asset trade order does not exist + +POLL_ALREADY_EXISTS = poll already exists + +POLL_DOES_NOT_EXIST = poll does not exist + +POLL_OPTION_DOES_NOT_EXIST = poll option does not exist + +PUBLIC_KEY_UNKNOWN = public key unknown + +REWARD_SHARE_UNKNOWN = reward-share unknown + +SELF_SHARE_EXISTS = self-share (reward-share) already exists + +TIMESTAMP_TOO_NEW = timestamp too new + +TIMESTAMP_TOO_OLD = timestamp too old + +TOO_MANY_UNCONFIRMED = account has too many unconfirmed transactions pending + +TRANSACTION_ALREADY_CONFIRMED = transaction has already confirmed + +TRANSACTION_ALREADY_EXISTS = transaction already exists + +TRANSACTION_UNKNOWN = transaction unknown + +TX_GROUP_ID_MISMATCH = transaction's group ID does not match From a4bb445f3e0da29c91e9012133c53bd61b98eff8 Mon Sep 17 00:00:00 2001 From: QuickMythril Date: Mon, 29 May 2023 04:23:22 -0400 Subject: [PATCH 06/12] Added Japanese translations for review --- .../resources/i18n/ApiError_jp.properties | 70 +++---- src/main/resources/i18n/SysTray_jp.properties | 46 ++--- .../i18n/TransactionValidity_jp.properties | 192 +++++++++--------- 3 files changed, 154 insertions(+), 154 deletions(-) diff --git a/src/main/resources/i18n/ApiError_jp.properties b/src/main/resources/i18n/ApiError_jp.properties index a9ab51c2..f1308ea8 100644 --- a/src/main/resources/i18n/ApiError_jp.properties +++ b/src/main/resources/i18n/ApiError_jp.properties @@ -1,83 +1,83 @@ #Generated by ResourceBundle Editor (http://essiembre.github.io/eclipse-rbe/) # Keys are from api.ApiError enum -# "localeLang": "en", +# "localeLang": "jp", ### Common ### -JSON = failed to parse JSON message +JSON = JSON メッセージの解析に失敗しました -INSUFFICIENT_BALANCE = insufficient balance +INSUFFICIENT_BALANCE = 残高が不足しています -UNAUTHORIZED = API call unauthorized +UNAUTHORIZED = API 呼び出しが承認されていません -REPOSITORY_ISSUE = repository error +REPOSITORY_ISSUE = リポジトリ エラー -NON_PRODUCTION = this API call is not permitted for production systems +NON_PRODUCTION = この API 呼び出しは運用システムでは許可されていません -BLOCKCHAIN_NEEDS_SYNC = blockchain needs to synchronize first +BLOCKCHAIN_NEEDS_SYNC = ブロックチェーンは最初に同期する必要があります -NO_TIME_SYNC = no clock synchronization yet +NO_TIME_SYNC = クロック同期はまだありません ### Validation ### -INVALID_SIGNATURE = invalid signature +INVALID_SIGNATURE = 無効な署名 -INVALID_ADDRESS = invalid address +INVALID_ADDRESS = 無効なアドレス -INVALID_PUBLIC_KEY = invalid public key +INVALID_PUBLIC_KEY = 無効な公開キー -INVALID_DATA = invalid data +INVALID_DATA = 無効なデータ -INVALID_NETWORK_ADDRESS = invalid network address +INVALID_NETWORK_ADDRESS = 無効なネットワーク アドレス -ADDRESS_UNKNOWN = account address unknown +ADDRESS_UNKNOWN = アカウントアドレスが不明です -INVALID_CRITERIA = invalid search criteria +INVALID_CRITERIA = 無効な検索条件 -INVALID_REFERENCE = invalid reference +INVALID_REFERENCE = 無効な参照 -TRANSFORMATION_ERROR = could not transform JSON into transaction +TRANSFORMATION_ERROR = JSON をトランザクションに変換できませんでした -INVALID_PRIVATE_KEY = invalid private key +INVALID_PRIVATE_KEY = 無効な秘密キー -INVALID_HEIGHT = invalid block height +INVALID_HEIGHT = 無効なブロックの高さ -CANNOT_MINT = account cannot mint +CANNOT_MINT = アカウントはミントできません ### Blocks ### -BLOCK_UNKNOWN = block unknown +BLOCK_UNKNOWN = 不明なブロック ### Transactions ### -TRANSACTION_UNKNOWN = transaction unknown +TRANSACTION_UNKNOWN = トランザクションが不明です -PUBLIC_KEY_NOT_FOUND = public key not found +PUBLIC_KEY_NOT_FOUND = 公開キーが見つかりません # this one is special in that caller expected to pass two additional strings, hence the two %s -TRANSACTION_INVALID = transaction invalid: %s (%s) +TRANSACTION_INVALID = トランザクションが無効です: %s (%s) ### Naming ### -NAME_UNKNOWN = name unknown +NAME_UNKNOWN = 名前が不明です ### Asset ### -INVALID_ASSET_ID = invalid asset ID +INVALID_ASSET_ID = 無効なアセット ID -INVALID_ORDER_ID = invalid asset order ID +INVALID_ORDER_ID = 無効なアセット注文 ID -ORDER_UNKNOWN = unknown asset order ID +ORDER_UNKNOWN = 不明なアセット注文 ID ### Groups ### -GROUP_UNKNOWN = group unknown +GROUP_UNKNOWN = 不明なグループ ### Foreign Blockchain ### -FOREIGN_BLOCKCHAIN_NETWORK_ISSUE = foreign blockchain or ElectrumX network issue +FOREIGN_BLOCKCHAIN_NETWORK_ISSUE = 外部ブロックチェーンまたは ElectrumX ネットワークの問題 -FOREIGN_BLOCKCHAIN_BALANCE_ISSUE = insufficient balance on foreign blockchain +FOREIGN_BLOCKCHAIN_BALANCE_ISSUE = 外部ブロックチェーンの残高が不足しています -FOREIGN_BLOCKCHAIN_TOO_SOON = too soon to broadcast foreign blockchain transaction (LockTime/median block time) +FOREIGN_BLOCKCHAIN_TOO_SOON = 外部ブロックチェーン トランザクションをブロードキャストするには早すぎます (LockTime/ブロック時間の中央値) ### Trade Portal ### -ORDER_SIZE_TOO_SMALL = order amount too low +ORDER_SIZE_TOO_SMALL = 注文金額が低すぎます ### Data ### -FILE_NOT_FOUND = file not found +FILE_NOT_FOUND = ファイルが見つかりません -NO_REPLY = peer didn't reply within the allowed time +NO_REPLY = ピアは許可された時間内に応答しませんでした diff --git a/src/main/resources/i18n/SysTray_jp.properties b/src/main/resources/i18n/SysTray_jp.properties index 39940be0..6f0f9b06 100644 --- a/src/main/resources/i18n/SysTray_jp.properties +++ b/src/main/resources/i18n/SysTray_jp.properties @@ -1,48 +1,48 @@ #Generated by ResourceBundle Editor (http://essiembre.github.io/eclipse-rbe/) # SysTray pop-up menu -APPLYING_UPDATE_AND_RESTARTING = Applying automatic update and restarting... +APPLYING_UPDATE_AND_RESTARTING = 自動アップデートを適用して再起動しています... -AUTO_UPDATE = Auto Update +AUTO_UPDATE = 自動更新 -BLOCK_HEIGHT = height +BLOCK_HEIGHT = 高さ -BLOCKS_REMAINING = blocks remaining +BLOCKS_REMAINING = 残りのブロック -BUILD_VERSION = Build version +BUILD_VERSION = ビルドバージョン -CHECK_TIME_ACCURACY = Check time accuracy +CHECK_TIME_ACCURACY = 時間の精度を確認する -CONNECTING = Connecting +CONNECTING = 接続中 -CONNECTION = connection +CONNECTION = つながり -CONNECTIONS = connections +CONNECTIONS = 接続 -CREATING_BACKUP_OF_DB_FILES = Creating backup of database files... +CREATING_BACKUP_OF_DB_FILES = データベース ファイルのバックアップを作成しています... -DB_BACKUP = Database Backup +DB_BACKUP = データベースのバックアップ -DB_CHECKPOINT = Database Checkpoint +DB_CHECKPOINT = データベースチェックポイント -DB_MAINTENANCE = Database Maintenance +DB_MAINTENANCE = データベースのメンテナンス -EXIT = Exit +EXIT = 終了 -LITE_NODE = Lite node +LITE_NODE = ライトノード -MINTING_DISABLED = NOT minting +MINTING_DISABLED = 鋳造しない -MINTING_ENABLED = \u2714 Minting +MINTING_ENABLED = \u2714 ミント -OPEN_UI = Open UI +OPEN_UI = オープン UI -PERFORMING_DB_CHECKPOINT = Saving uncommitted database changes... +PERFORMING_DB_CHECKPOINT = コミットされていないデータベースの変更を保存しています... -PERFORMING_DB_MAINTENANCE = Performing scheduled maintenance... +PERFORMING_DB_MAINTENANCE = 定期メンテナンスを実行しています... -SYNCHRONIZE_CLOCK = Synchronize clock +SYNCHRONIZE_CLOCK = クロックを同期する -SYNCHRONIZING_BLOCKCHAIN = Synchronizing +SYNCHRONIZING_BLOCKCHAIN = 同期中 -SYNCHRONIZING_CLOCK = Synchronizing clock +SYNCHRONIZING_CLOCK = 同期クロック diff --git a/src/main/resources/i18n/TransactionValidity_jp.properties b/src/main/resources/i18n/TransactionValidity_jp.properties index 3f33771d..c1111619 100644 --- a/src/main/resources/i18n/TransactionValidity_jp.properties +++ b/src/main/resources/i18n/TransactionValidity_jp.properties @@ -1,195 +1,195 @@ # -ACCOUNT_ALREADY_EXISTS = account already exists +ACCOUNT_ALREADY_EXISTS = アカウントはすでに存在します -ACCOUNT_CANNOT_REWARD_SHARE = account cannot reward-share +ACCOUNT_CANNOT_REWARD_SHARE = アカウントは報酬を共有できません -ADDRESS_ABOVE_RATE_LIMIT = address reached specified rate limit +ADDRESS_ABOVE_RATE_LIMIT = アドレスが指定されたレート制限に達しました -ADDRESS_BLOCKED = this address is blocked +ADDRESS_BLOCKED = このアドレスはブロックされています -ALREADY_GROUP_ADMIN = already group admin +ALREADY_GROUP_ADMIN = すでにグループ管理者です -ALREADY_GROUP_MEMBER = already group member +ALREADY_GROUP_MEMBER = すでにグループメンバーです -ALREADY_VOTED_FOR_THAT_OPTION = already voted for that option +ALREADY_VOTED_FOR_THAT_OPTION = すでにそのオプションに投票しています -ASSET_ALREADY_EXISTS = asset already exists +ASSET_ALREADY_EXISTS = アセットはすでに存在します -ASSET_DOES_NOT_EXIST = asset does not exist +ASSET_DOES_NOT_EXIST = アセットが存在しません -ASSET_DOES_NOT_MATCH_AT = asset does not match AT's asset +ASSET_DOES_NOT_MATCH_AT = アセットが AT のアセットと一致しません -ASSET_NOT_SPENDABLE = asset is not spendable +ASSET_NOT_SPENDABLE = 資産は使用できません -AT_ALREADY_EXISTS = AT already exists +AT_ALREADY_EXISTS = AT はすでに存在します -AT_IS_FINISHED = AT has finished +AT_IS_FINISHED = AT が終了しました -AT_UNKNOWN = AT unknown +AT_UNKNOWN = AT が不明 -BAN_EXISTS = ban already exists +BAN_EXISTS = 禁止はすでに存在します -BAN_UNKNOWN = ban unknown +BAN_UNKNOWN = 禁止は不明 -BANNED_FROM_GROUP = banned from group +BANNED_FROM_GROUP = グループからの参加を禁止されています -BUYER_ALREADY_OWNER = buyer is already owner +BUYER_ALREADY_OWNER = 購入者はすでに所有者です -CLOCK_NOT_SYNCED = clock not synchronized +CLOCK_NOT_SYNCED = クロックが同期されていません -DUPLICATE_MESSAGE = address sent duplicate message +DUPLICATE_MESSAGE = 重複メッセージを送信したアドレス -DUPLICATE_OPTION = duplicate option +DUPLICATE_OPTION = 重複したオプション -GROUP_ALREADY_EXISTS = group already exists +GROUP_ALREADY_EXISTS = グループはすでに存在します -GROUP_APPROVAL_DECIDED = group-approval already decided +GROUP_APPROVAL_DECIDED = グループの承認はすでに決定されています -GROUP_APPROVAL_NOT_REQUIRED = group-approval not required +GROUP_APPROVAL_NOT_REQUIRED = グループ承認は必要ありません -GROUP_DOES_NOT_EXIST = group does not exist +GROUP_DOES_NOT_EXIST = グループが存在しません -GROUP_ID_MISMATCH = group ID mismatch +GROUP_ID_MISMATCH = グループ ID の不一致 -GROUP_OWNER_CANNOT_LEAVE = group owner cannot leave group +GROUP_OWNER_CANNOT_LEAVE = グループ所有者はグループを脱退できません -HAVE_EQUALS_WANT = have-asset is the same as want-asset +HAVE_EQUALS_WANT = 持っている資産は欲しい資産と同じです -INCORRECT_NONCE = incorrect PoW nonce +INCORRECT_NONCE = 不正な PoW ナンス -INSUFFICIENT_FEE = insufficient fee +INSUFFICIENT_FEE = 料金が不十分です -INVALID_ADDRESS = invalid address +INVALID_ADDRESS = 無効なアドレス -INVALID_AMOUNT = invalid amount +INVALID_AMOUNT = 無効な金額 -INVALID_ASSET_OWNER = invalid asset owner +INVALID_ASSET_OWNER = 無効なアセット所有者 -INVALID_AT_TRANSACTION = invalid AT transaction +INVALID_AT_TRANSACTION = 無効な AT トランザクション -INVALID_AT_TYPE_LENGTH = invalid AT 'type' length +INVALID_AT_TYPE_LENGTH = AT の「タイプ」の長さが無効です -INVALID_BUT_OK = invalid but OK +INVALID_BUT_OK = 無効だがOK -INVALID_CREATION_BYTES = invalid creation bytes +INVALID_CREATION_BYTES = 無効な作成バイト数 -INVALID_DATA_LENGTH = invalid data length +INVALID_DATA_LENGTH = 無効なデータ長 -INVALID_DESCRIPTION_LENGTH = invalid description length +INVALID_DESCRIPTION_LENGTH = 無効な説明の長さ -INVALID_GROUP_APPROVAL_THRESHOLD = invalid group-approval threshold +INVALID_GROUP_APPROVAL_THRESHOLD = 無効なグループ承認しきい値 -INVALID_GROUP_BLOCK_DELAY = invalid group-approval block delay +INVALID_GROUP_BLOCK_DELAY = 無効なグループ承認ブロックの遅延 -INVALID_GROUP_ID = invalid group ID +INVALID_GROUP_ID = 無効なグループ ID -INVALID_GROUP_OWNER = invalid group owner +INVALID_GROUP_OWNER = 無効なグループ所有者 -INVALID_LIFETIME = invalid lifetime +INVALID_LIFETIME = 無効な有効期間 -INVALID_NAME_LENGTH = invalid name length +INVALID_NAME_LENGTH = 名前の長さが無効です -INVALID_NAME_OWNER = invalid name owner +INVALID_NAME_OWNER = 無効な名前の所有者 -INVALID_OPTION_LENGTH = invalid options length +INVALID_OPTION_LENGTH = 無効なオプションの長さ -INVALID_OPTIONS_COUNT = invalid options count +INVALID_OPTIONS_COUNT = 無効なオプションの数 -INVALID_ORDER_CREATOR = invalid order creator +INVALID_ORDER_CREATOR = 無効な注文作成者 -INVALID_PAYMENTS_COUNT = invalid payments count +INVALID_PAYMENTS_COUNT = 無効な支払い数 -INVALID_PUBLIC_KEY = invalid public key +INVALID_PUBLIC_KEY = 無効な公開キー -INVALID_QUANTITY = invalid quantity +INVALID_QUANTITY = 無効な数量 -INVALID_REFERENCE = invalid reference +INVALID_REFERENCE = 無効な参照 -INVALID_RETURN = invalid return +INVALID_RETURN = 無効な返品 -INVALID_REWARD_SHARE_PERCENT = invalid reward-share percent +INVALID_REWARD_SHARE_PERCENT = 無効な報酬分配率 -INVALID_SELLER = invalid seller +INVALID_SELLER = 無効な販売者 -INVALID_TAGS_LENGTH = invalid 'tags' length +INVALID_TAGS_LENGTH = 無効な「タグ」の長さ -INVALID_TIMESTAMP_SIGNATURE = invalid timestamp signature +INVALID_TIMESTAMP_SIGNATURE = 無効なタイムスタンプ署名 -INVALID_TX_GROUP_ID = invalid transaction group ID +INVALID_TX_GROUP_ID = 無効なトランザクション グループ ID -INVALID_VALUE_LENGTH = invalid 'value' length +INVALID_VALUE_LENGTH = 無効な「値」の長さ -INVITE_UNKNOWN = group invite unknown +INVITE_UNKNOWN = グループの招待が不明です -JOIN_REQUEST_EXISTS = group join request already exists +JOIN_REQUEST_EXISTS = グループ参加リクエストはすでに存在します -MAXIMUM_REWARD_SHARES = already at maximum number of reward-shares for this account +MAXIMUM_REWARD_SHARES = このアカウントの特典シェアはすでに最大数に達しています -MISSING_CREATOR = missing creator +MISSING_CREATOR = 作成者が見つかりません -MULTIPLE_NAMES_FORBIDDEN = multiple registered names per account is forbidden +MULTIPLE_NAMES_FORBIDDEN = アカウントごとに複数の登録名は禁止されています -NAME_ALREADY_FOR_SALE = name already for sale +NAME_ALREADY_FOR_SALE = 名前はすでに販売中です -NAME_ALREADY_REGISTERED = name already registered +NAME_ALREADY_REGISTERED = 名前はすでに登録されています -NAME_BLOCKED = this name is blocked +NAME_BLOCKED = この名前はブロックされています -NAME_DOES_NOT_EXIST = name does not exist +NAME_DOES_NOT_EXIST = 名前は存在しません -NAME_NOT_FOR_SALE = name is not for sale +NAME_NOT_FOR_SALE = 名前は非売品です -NAME_NOT_NORMALIZED = name not in Unicode 'normalized' form +NAME_NOT_NORMALIZED = 名前は Unicode の「正規化」形式ではありません -NEGATIVE_AMOUNT = invalid/negative amount +NEGATIVE_AMOUNT = 無効/マイナスの金額 -NEGATIVE_FEE = invalid/negative fee +NEGATIVE_FEE = 無効/マイナスの料金 -NEGATIVE_PRICE = invalid/negative price +NEGATIVE_PRICE = 無効/マイナスの価格 -NO_BALANCE = insufficient balance +NO_BALANCE = 残高が不足しています -NO_BLOCKCHAIN_LOCK = node's blockchain currently busy +NO_BLOCKCHAIN_LOCK = ノードのブロックチェーンは現在ビジーです -NO_FLAG_PERMISSION = account does not have that permission +NO_FLAG_PERMISSION = アカウントにはその権限がありません -NOT_GROUP_ADMIN = account is not a group admin +NOT_GROUP_ADMIN = アカウントはグループ管理者ではありません -NOT_GROUP_MEMBER = account is not a group member +NOT_GROUP_MEMBER = アカウントはグループメンバーではありません -NOT_MINTING_ACCOUNT = account cannot mint +NOT_MINTING_ACCOUNT = アカウントはミントできません -NOT_YET_RELEASED = feature not yet released +NOT_YET_RELEASED = 機能はまだリリースされていません OK = OK -ORDER_ALREADY_CLOSED = asset trade order is already closed +ORDER_ALREADY_CLOSED = 資産取引注文はすでに終了しています -ORDER_DOES_NOT_EXIST = asset trade order does not exist +ORDER_DOES_NOT_EXIST = 資産取引注文が存在しません -POLL_ALREADY_EXISTS = poll already exists +POLL_ALREADY_EXISTS = ポーリングはすでに存在します -POLL_DOES_NOT_EXIST = poll does not exist +POLL_DOES_NOT_EXIST = 投票は存在しません -POLL_OPTION_DOES_NOT_EXIST = poll option does not exist +POLL_OPTION_DOES_NOT_EXIST = ポーリング オプションが存在しません -PUBLIC_KEY_UNKNOWN = public key unknown +PUBLIC_KEY_UNKNOWN = 公開鍵が不明です -REWARD_SHARE_UNKNOWN = reward-share unknown +REWARD_SHARE_UNKNOWN = 報酬シェアが不明 -SELF_SHARE_EXISTS = self-share (reward-share) already exists +SELF_SHARE_EXISTS = セルフシェア (報酬シェア) はすでに存在します -TIMESTAMP_TOO_NEW = timestamp too new +TIMESTAMP_TOO_NEW = タイムスタンプが新しすぎます -TIMESTAMP_TOO_OLD = timestamp too old +TIMESTAMP_TOO_OLD = タイムスタンプが古すぎます -TOO_MANY_UNCONFIRMED = account has too many unconfirmed transactions pending +TOO_MANY_UNCONFIRMED = アカウントに保留中の未確認トランザクションが多すぎます -TRANSACTION_ALREADY_CONFIRMED = transaction has already confirmed +TRANSACTION_ALREADY_CONFIRMED = トランザクションはすでに確認されています -TRANSACTION_ALREADY_EXISTS = transaction already exists +TRANSACTION_ALREADY_EXISTS = トランザクションはすでに存在します -TRANSACTION_UNKNOWN = transaction unknown +TRANSACTION_UNKNOWN = トランザクションが不明です -TX_GROUP_ID_MISMATCH = transaction's group ID does not match +TX_GROUP_ID_MISMATCH = トランザクションのグループ ID が一致しません From bfc03db6a9d78ba0ad5e962f70aa1db58d2b17f5 Mon Sep 17 00:00:00 2001 From: CalDescent Date: Tue, 30 May 2023 17:41:39 +0100 Subject: [PATCH 07/12] Default minPeerVersion set to 4.1.2 --- src/main/java/org/qortal/settings/Settings.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/org/qortal/settings/Settings.java b/src/main/java/org/qortal/settings/Settings.java index 362227a5..cce3f441 100644 --- a/src/main/java/org/qortal/settings/Settings.java +++ b/src/main/java/org/qortal/settings/Settings.java @@ -219,7 +219,7 @@ public class Settings { public long recoveryModeTimeout = 24 * 60 * 60 * 1000L; /** Minimum peer version number required in order to sync with them */ - private String minPeerVersion = "4.1.1"; + private String minPeerVersion = "4.1.2"; /** Whether to allow connections with peers below minPeerVersion * If true, we won't sync with them but they can still sync with us, and will show in the peers list * If false, sync will be blocked both ways, and they will not appear in the peers list */ From 66c91fd365f321410e411b30f2ad0ffb8d978e3c Mon Sep 17 00:00:00 2001 From: CalDescent Date: Tue, 30 May 2023 17:42:31 +0100 Subject: [PATCH 08/12] MIN_PEER_VERSION for handshake set to 4.1.1 --- src/main/java/org/qortal/network/Handshake.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/org/qortal/network/Handshake.java b/src/main/java/org/qortal/network/Handshake.java index 4500cd59..341f4e21 100644 --- a/src/main/java/org/qortal/network/Handshake.java +++ b/src/main/java/org/qortal/network/Handshake.java @@ -265,7 +265,7 @@ public enum Handshake { private static final long PEER_VERSION_131 = 0x0100030001L; /** Minimum peer version that we are allowed to communicate with */ - private static final String MIN_PEER_VERSION = "4.0.0"; + private static final String MIN_PEER_VERSION = "4.1.1"; private static final int POW_BUFFER_SIZE_PRE_131 = 8 * 1024 * 1024; // bytes private static final int POW_DIFFICULTY_PRE_131 = 8; // leading zero bits From 5fb2640a3afea047c511973467a0f324311232e3 Mon Sep 17 00:00:00 2001 From: CalDescent Date: Tue, 30 May 2023 18:05:05 +0100 Subject: [PATCH 09/12] Bump version to 4.1.3 --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index c9986fd4..7c7ac147 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ 4.0.0 org.qortal qortal - 4.1.2 + 4.1.3 jar true From 7f7b02f0038957facf8a916d8a2b68c5f11de760 Mon Sep 17 00:00:00 2001 From: QuickMythril Date: Fri, 2 Jun 2023 06:17:48 -0400 Subject: [PATCH 10/12] Updated Japanese translations (Credit: R M) --- .../resources/i18n/ApiError_jp.properties | 38 +++---- src/main/resources/i18n/SysTray_jp.properties | 28 ++--- .../i18n/TransactionValidity_jp.properties | 106 +++++++++--------- 3 files changed, 86 insertions(+), 86 deletions(-) diff --git a/src/main/resources/i18n/ApiError_jp.properties b/src/main/resources/i18n/ApiError_jp.properties index f1308ea8..603914cb 100644 --- a/src/main/resources/i18n/ApiError_jp.properties +++ b/src/main/resources/i18n/ApiError_jp.properties @@ -6,56 +6,56 @@ ### Common ### JSON = JSON メッセージの解析に失敗しました -INSUFFICIENT_BALANCE = 残高が不足しています +INSUFFICIENT_BALANCE = 残高不足 -UNAUTHORIZED = API 呼び出しが承認されていません +UNAUTHORIZED = APIコール未承認 -REPOSITORY_ISSUE = リポジトリ エラー +REPOSITORY_ISSUE = リポジトリエラー -NON_PRODUCTION = この API 呼び出しは運用システムでは許可されていません +NON_PRODUCTION = この APIコールはプロダクションシステムでは許可されていません -BLOCKCHAIN_NEEDS_SYNC = ブロックチェーンは最初に同期する必要があります +BLOCKCHAIN_NEEDS_SYNC = ブロックチェーンをまず同期する必要があります -NO_TIME_SYNC = クロック同期はまだありません +NO_TIME_SYNC = 時刻が未同期 ### Validation ### INVALID_SIGNATURE = 無効な署名 INVALID_ADDRESS = 無効なアドレス -INVALID_PUBLIC_KEY = 無効な公開キー +INVALID_PUBLIC_KEY = 無効な公開鍵 INVALID_DATA = 無効なデータ INVALID_NETWORK_ADDRESS = 無効なネットワーク アドレス -ADDRESS_UNKNOWN = アカウントアドレスが不明です +ADDRESS_UNKNOWN = 不明なアカウントアドレス INVALID_CRITERIA = 無効な検索条件 INVALID_REFERENCE = 無効な参照 -TRANSFORMATION_ERROR = JSON をトランザクションに変換できませんでした +TRANSFORMATION_ERROR = JSONをトランザクションに変換出来ませんでした -INVALID_PRIVATE_KEY = 無効な秘密キー +INVALID_PRIVATE_KEY = 無効な秘密鍵 -INVALID_HEIGHT = 無効なブロックの高さ +INVALID_HEIGHT = 無効なブロック高 -CANNOT_MINT = アカウントはミントできません +CANNOT_MINT = アカウントはミント出来ません ### Blocks ### BLOCK_UNKNOWN = 不明なブロック ### Transactions ### -TRANSACTION_UNKNOWN = トランザクションが不明です +TRANSACTION_UNKNOWN = 不明なトランザクション -PUBLIC_KEY_NOT_FOUND = 公開キーが見つかりません +PUBLIC_KEY_NOT_FOUND = 公開鍵が見つかりません # this one is special in that caller expected to pass two additional strings, hence the two %s -TRANSACTION_INVALID = トランザクションが無効です: %s (%s) +TRANSACTION_INVALID = 無効なトランザクション: %s (%s) ### Naming ### -NAME_UNKNOWN = 名前が不明です +NAME_UNKNOWN = 不明な名前 ### Asset ### INVALID_ASSET_ID = 無効なアセット ID @@ -68,11 +68,11 @@ ORDER_UNKNOWN = 不明なアセット注文 ID GROUP_UNKNOWN = 不明なグループ ### Foreign Blockchain ### -FOREIGN_BLOCKCHAIN_NETWORK_ISSUE = 外部ブロックチェーンまたは ElectrumX ネットワークの問題 +FOREIGN_BLOCKCHAIN_NETWORK_ISSUE = 外部ブロックチェーンまたはElectrumXネットワークの問題 FOREIGN_BLOCKCHAIN_BALANCE_ISSUE = 外部ブロックチェーンの残高が不足しています -FOREIGN_BLOCKCHAIN_TOO_SOON = 外部ブロックチェーン トランザクションをブロードキャストするには早すぎます (LockTime/ブロック時間の中央値) +FOREIGN_BLOCKCHAIN_TOO_SOON = 外部ブロックチェーン トランザクションのブロードキャストが時期尚早 (ロックタイム/ブロック時間の中央値) ### Trade Portal ### ORDER_SIZE_TOO_SMALL = 注文金額が低すぎます @@ -80,4 +80,4 @@ ORDER_SIZE_TOO_SMALL = 注文金額が低すぎます ### Data ### FILE_NOT_FOUND = ファイルが見つかりません -NO_REPLY = ピアは許可された時間内に応答しませんでした +NO_REPLY = ピアが制限時間内に応答しませんでした diff --git a/src/main/resources/i18n/SysTray_jp.properties b/src/main/resources/i18n/SysTray_jp.properties index 6f0f9b06..c4cccb5b 100644 --- a/src/main/resources/i18n/SysTray_jp.properties +++ b/src/main/resources/i18n/SysTray_jp.properties @@ -1,29 +1,29 @@ #Generated by ResourceBundle Editor (http://essiembre.github.io/eclipse-rbe/) -# SysTray pop-up menu +# SysTray pop-up menu # Japanese translation by R M 2023 -APPLYING_UPDATE_AND_RESTARTING = 自動アップデートを適用して再起動しています... +APPLYING_UPDATE_AND_RESTARTING = 自動更新を適用して再起動しています... AUTO_UPDATE = 自動更新 -BLOCK_HEIGHT = 高さ +BLOCK_HEIGHT = ブロック高 BLOCKS_REMAINING = 残りのブロック BUILD_VERSION = ビルドバージョン -CHECK_TIME_ACCURACY = 時間の精度を確認する +CHECK_TIME_ACCURACY = 時刻の精度を確認 CONNECTING = 接続中 -CONNECTION = つながり +CONNECTION = 接続 CONNECTIONS = 接続 -CREATING_BACKUP_OF_DB_FILES = データベース ファイルのバックアップを作成しています... +CREATING_BACKUP_OF_DB_FILES = データベース ファイルのバックアップを作成中... DB_BACKUP = データベースのバックアップ -DB_CHECKPOINT = データベースチェックポイント +DB_CHECKPOINT = データベースのチェックポイント DB_MAINTENANCE = データベースのメンテナンス @@ -31,18 +31,18 @@ EXIT = 終了 LITE_NODE = ライトノード -MINTING_DISABLED = 鋳造しない +MINTING_DISABLED = ミント一時中止中 MINTING_ENABLED = \u2714 ミント -OPEN_UI = オープン UI +OPEN_UI = UIを開く -PERFORMING_DB_CHECKPOINT = コミットされていないデータベースの変更を保存しています... +PERFORMING_DB_CHECKPOINT = コミットされていないデータベースの変更を保存中... -PERFORMING_DB_MAINTENANCE = 定期メンテナンスを実行しています... +PERFORMING_DB_MAINTENANCE = 定期メンテナンスを実行中... -SYNCHRONIZE_CLOCK = クロックを同期する +SYNCHRONIZE_CLOCK = 時刻を同期 -SYNCHRONIZING_BLOCKCHAIN = 同期中 +SYNCHRONIZING_BLOCKCHAIN = ブロックチェーンを同期中 -SYNCHRONIZING_CLOCK = 同期クロック +SYNCHRONIZING_CLOCK = 時刻を同期中 diff --git a/src/main/resources/i18n/TransactionValidity_jp.properties b/src/main/resources/i18n/TransactionValidity_jp.properties index c1111619..9540372a 100644 --- a/src/main/resources/i18n/TransactionValidity_jp.properties +++ b/src/main/resources/i18n/TransactionValidity_jp.properties @@ -1,64 +1,64 @@ -# +# -ACCOUNT_ALREADY_EXISTS = アカウントはすでに存在します +ACCOUNT_ALREADY_EXISTS = 既にアカウントは存在します -ACCOUNT_CANNOT_REWARD_SHARE = アカウントは報酬を共有できません +ACCOUNT_CANNOT_REWARD_SHARE = アカウントは報酬シェアが出来ません ADDRESS_ABOVE_RATE_LIMIT = アドレスが指定されたレート制限に達しました ADDRESS_BLOCKED = このアドレスはブロックされています -ALREADY_GROUP_ADMIN = すでにグループ管理者です +ALREADY_GROUP_ADMIN = 既ににグループ管理者です -ALREADY_GROUP_MEMBER = すでにグループメンバーです +ALREADY_GROUP_MEMBER = 既にグループメンバーです -ALREADY_VOTED_FOR_THAT_OPTION = すでにそのオプションに投票しています +ALREADY_VOTED_FOR_THAT_OPTION = 既にそのオプションに投票しています -ASSET_ALREADY_EXISTS = アセットはすでに存在します +ASSET_ALREADY_EXISTS = 既にアセットは存在します ASSET_DOES_NOT_EXIST = アセットが存在しません -ASSET_DOES_NOT_MATCH_AT = アセットが AT のアセットと一致しません +ASSET_DOES_NOT_MATCH_AT = アセットがATのアセットと一致しません -ASSET_NOT_SPENDABLE = 資産は使用できません +ASSET_NOT_SPENDABLE = 資産が使用不可です -AT_ALREADY_EXISTS = AT はすでに存在します +AT_ALREADY_EXISTS = 既にATが存在します -AT_IS_FINISHED = AT が終了しました +AT_IS_FINISHED = ATが終了しました -AT_UNKNOWN = AT が不明 +AT_UNKNOWN = 不明なAT -BAN_EXISTS = 禁止はすでに存在します +BAN_EXISTS = 既にバンされてます -BAN_UNKNOWN = 禁止は不明 +BAN_UNKNOWN = 不明なバン -BANNED_FROM_GROUP = グループからの参加を禁止されています +BANNED_FROM_GROUP = グループからのバンされています -BUYER_ALREADY_OWNER = 購入者はすでに所有者です +BUYER_ALREADY_OWNER = 既に購入者が所有者です -CLOCK_NOT_SYNCED = クロックが同期されていません +CLOCK_NOT_SYNCED = 時刻が未同期 -DUPLICATE_MESSAGE = 重複メッセージを送信したアドレス +DUPLICATE_MESSAGE = このアドレスは重複メッセージを送信しました DUPLICATE_OPTION = 重複したオプション -GROUP_ALREADY_EXISTS = グループはすでに存在します +GROUP_ALREADY_EXISTS = 既にグループは存在します -GROUP_APPROVAL_DECIDED = グループの承認はすでに決定されています +GROUP_APPROVAL_DECIDED = 既にグループの承認は決定されています -GROUP_APPROVAL_NOT_REQUIRED = グループ承認は必要ありません +GROUP_APPROVAL_NOT_REQUIRED = グループ承認が不必要 GROUP_DOES_NOT_EXIST = グループが存在しません -GROUP_ID_MISMATCH = グループ ID の不一致 +GROUP_ID_MISMATCH = グループ ID が不一致 -GROUP_OWNER_CANNOT_LEAVE = グループ所有者はグループを脱退できません +GROUP_OWNER_CANNOT_LEAVE = グループ所有者はグループを退会出来ません HAVE_EQUALS_WANT = 持っている資産は欲しい資産と同じです INCORRECT_NONCE = 不正な PoW ナンス -INSUFFICIENT_FEE = 料金が不十分です +INSUFFICIENT_FEE = 手数料が不十分です INVALID_ADDRESS = 無効なアドレス @@ -66,9 +66,9 @@ INVALID_AMOUNT = 無効な金額 INVALID_ASSET_OWNER = 無効なアセット所有者 -INVALID_AT_TRANSACTION = 無効な AT トランザクション +INVALID_AT_TRANSACTION = 無効なATトランザクション -INVALID_AT_TYPE_LENGTH = AT の「タイプ」の長さが無効です +INVALID_AT_TYPE_LENGTH = 無効なATの「タイプ」の長さです INVALID_BUT_OK = 無効だがOK @@ -76,11 +76,11 @@ INVALID_CREATION_BYTES = 無効な作成バイト数 INVALID_DATA_LENGTH = 無効なデータ長 -INVALID_DESCRIPTION_LENGTH = 無効な説明の長さ +INVALID_DESCRIPTION_LENGTH = 無効な概要の長さ -INVALID_GROUP_APPROVAL_THRESHOLD = 無効なグループ承認しきい値 +INVALID_GROUP_APPROVAL_THRESHOLD = 無効なグループ承認のしきい値 -INVALID_GROUP_BLOCK_DELAY = 無効なグループ承認ブロックの遅延 +INVALID_GROUP_BLOCK_DELAY = 無効なグループ承認のブロック遅延 INVALID_GROUP_ID = 無効なグループ ID @@ -88,7 +88,7 @@ INVALID_GROUP_OWNER = 無効なグループ所有者 INVALID_LIFETIME = 無効な有効期間 -INVALID_NAME_LENGTH = 名前の長さが無効です +INVALID_NAME_LENGTH = 無効な名前の長さです INVALID_NAME_OWNER = 無効な名前の所有者 @@ -98,9 +98,9 @@ INVALID_OPTIONS_COUNT = 無効なオプションの数 INVALID_ORDER_CREATOR = 無効な注文作成者 -INVALID_PAYMENTS_COUNT = 無効な支払い数 +INVALID_PAYMENTS_COUNT = 無効な入出金数 -INVALID_PUBLIC_KEY = 無効な公開キー +INVALID_PUBLIC_KEY = 無効な公開鍵 INVALID_QUANTITY = 無効な数量 @@ -108,7 +108,7 @@ INVALID_REFERENCE = 無効な参照 INVALID_RETURN = 無効な返品 -INVALID_REWARD_SHARE_PERCENT = 無効な報酬分配率 +INVALID_REWARD_SHARE_PERCENT = 無効な報酬シェア率 INVALID_SELLER = 無効な販売者 @@ -120,19 +120,19 @@ INVALID_TX_GROUP_ID = 無効なトランザクション グループ ID INVALID_VALUE_LENGTH = 無効な「値」の長さ -INVITE_UNKNOWN = グループの招待が不明です +INVITE_UNKNOWN = 不明なグループ招待 -JOIN_REQUEST_EXISTS = グループ参加リクエストはすでに存在します +JOIN_REQUEST_EXISTS = 既にグループ参加リクエストが存在します -MAXIMUM_REWARD_SHARES = このアカウントの特典シェアはすでに最大数に達しています +MAXIMUM_REWARD_SHARES = 既にこのアカウントの報酬シェアは最大です MISSING_CREATOR = 作成者が見つかりません MULTIPLE_NAMES_FORBIDDEN = アカウントごとに複数の登録名は禁止されています -NAME_ALREADY_FOR_SALE = 名前はすでに販売中です +NAME_ALREADY_FOR_SALE = 既に名前は販売中です -NAME_ALREADY_REGISTERED = 名前はすでに登録されています +NAME_ALREADY_REGISTERED = 既に名前は登録されています NAME_BLOCKED = この名前はブロックされています @@ -142,11 +142,11 @@ NAME_NOT_FOR_SALE = 名前は非売品です NAME_NOT_NORMALIZED = 名前は Unicode の「正規化」形式ではありません -NEGATIVE_AMOUNT = 無効/マイナスの金額 +NEGATIVE_AMOUNT = 無効な/負の金額 -NEGATIVE_FEE = 無効/マイナスの料金 +NEGATIVE_FEE = 無効な/負の料金 -NEGATIVE_PRICE = 無効/マイナスの価格 +NEGATIVE_PRICE = 無効な/負の価格 NO_BALANCE = 残高が不足しています @@ -158,38 +158,38 @@ NOT_GROUP_ADMIN = アカウントはグループ管理者ではありません NOT_GROUP_MEMBER = アカウントはグループメンバーではありません -NOT_MINTING_ACCOUNT = アカウントはミントできません +NOT_MINTING_ACCOUNT = アカウントはミント出来ません NOT_YET_RELEASED = 機能はまだリリースされていません OK = OK -ORDER_ALREADY_CLOSED = 資産取引注文はすでに終了しています +ORDER_ALREADY_CLOSED = 既に資産取引注文は終了しています ORDER_DOES_NOT_EXIST = 資産取引注文が存在しません -POLL_ALREADY_EXISTS = ポーリングはすでに存在します +POLL_ALREADY_EXISTS = 既に投票は存在します POLL_DOES_NOT_EXIST = 投票は存在しません -POLL_OPTION_DOES_NOT_EXIST = ポーリング オプションが存在しません +POLL_OPTION_DOES_NOT_EXIST = 投票オプションが存在しません -PUBLIC_KEY_UNKNOWN = 公開鍵が不明です +PUBLIC_KEY_UNKNOWN = 不明な公開鍵 -REWARD_SHARE_UNKNOWN = 報酬シェアが不明 +REWARD_SHARE_UNKNOWN = 不明な報酬シェア -SELF_SHARE_EXISTS = セルフシェア (報酬シェア) はすでに存在します +SELF_SHARE_EXISTS = 既に自己シェア(報酬シェア)が存在します TIMESTAMP_TOO_NEW = タイムスタンプが新しすぎます TIMESTAMP_TOO_OLD = タイムスタンプが古すぎます -TOO_MANY_UNCONFIRMED = アカウントに保留中の未確認トランザクションが多すぎます +TOO_MANY_UNCONFIRMED = アカウントに保留中の未承認トランザクションが多すぎます -TRANSACTION_ALREADY_CONFIRMED = トランザクションはすでに確認されています +TRANSACTION_ALREADY_CONFIRMED = 既にトランザクションは承認されています -TRANSACTION_ALREADY_EXISTS = トランザクションはすでに存在します +TRANSACTION_ALREADY_EXISTS = 既にトランザクションは存在します -TRANSACTION_UNKNOWN = トランザクションが不明です +TRANSACTION_UNKNOWN = 不明なトランザクション -TX_GROUP_ID_MISMATCH = トランザクションのグループ ID が一致しません +TX_GROUP_ID_MISMATCH = トランザクションのグループIDが一致しません From 5928b54a33dc7f2082ae13b733d7afa1b6d3c51c Mon Sep 17 00:00:00 2001 From: CalDescent Date: Sat, 17 Jun 2023 13:03:29 +0100 Subject: [PATCH 11/12] Added developer QDN proxy. This allows Q-Apps and websites to be developed and tested in real time, by proxying an existing webserver such as localhost:5173 from React/Vite. The proxy adds all QDN functionality to the existing server in real time. Needs UI integration before all features can be used. --- .../java/org/qortal/api/DevProxyService.java | 173 ++++++++++++++++++ .../resource/DevProxyServerResource.java | 142 ++++++++++++++ .../api/resource/DeveloperResource.java | 96 ++++++++++ .../qortal/controller/DevProxyManager.java | 74 ++++++++ .../java/org/qortal/settings/Settings.java | 20 ++ 5 files changed, 505 insertions(+) create mode 100644 src/main/java/org/qortal/api/DevProxyService.java create mode 100644 src/main/java/org/qortal/api/proxy/resource/DevProxyServerResource.java create mode 100644 src/main/java/org/qortal/api/resource/DeveloperResource.java create mode 100644 src/main/java/org/qortal/controller/DevProxyManager.java diff --git a/src/main/java/org/qortal/api/DevProxyService.java b/src/main/java/org/qortal/api/DevProxyService.java new file mode 100644 index 00000000..e0bf02db --- /dev/null +++ b/src/main/java/org/qortal/api/DevProxyService.java @@ -0,0 +1,173 @@ +package org.qortal.api; + +import io.swagger.v3.jaxrs2.integration.resources.OpenApiResource; +import org.eclipse.jetty.http.HttpVersion; +import org.eclipse.jetty.rewrite.handler.RewriteHandler; +import org.eclipse.jetty.server.*; +import org.eclipse.jetty.server.handler.ErrorHandler; +import org.eclipse.jetty.server.handler.InetAccessHandler; +import org.eclipse.jetty.servlet.FilterHolder; +import org.eclipse.jetty.servlet.ServletContextHandler; +import org.eclipse.jetty.servlet.ServletHolder; +import org.eclipse.jetty.servlets.CrossOriginFilter; +import org.eclipse.jetty.util.ssl.SslContextFactory; +import org.glassfish.jersey.server.ResourceConfig; +import org.glassfish.jersey.servlet.ServletContainer; +import org.qortal.api.resource.AnnotationPostProcessor; +import org.qortal.api.resource.ApiDefinition; +import org.qortal.network.Network; +import org.qortal.repository.DataException; +import org.qortal.settings.Settings; + +import javax.net.ssl.KeyManagerFactory; +import javax.net.ssl.SSLContext; +import java.io.InputStream; +import java.net.InetAddress; +import java.net.InetSocketAddress; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.security.KeyStore; +import java.security.SecureRandom; + +public class DevProxyService { + + private static DevProxyService instance; + + private final ResourceConfig config; + private Server server; + + private DevProxyService() { + this.config = new ResourceConfig(); + this.config.packages("org.qortal.api.proxy.resource", "org.qortal.api.resource"); + this.config.register(OpenApiResource.class); + this.config.register(ApiDefinition.class); + this.config.register(AnnotationPostProcessor.class); + } + + public static DevProxyService getInstance() { + if (instance == null) + instance = new DevProxyService(); + + return instance; + } + + public Iterable> getResources() { + return this.config.getClasses(); + } + + public void start() throws DataException { + try { + // Create API server + + // SSL support if requested + String keystorePathname = Settings.getInstance().getSslKeystorePathname(); + String keystorePassword = Settings.getInstance().getSslKeystorePassword(); + + if (keystorePathname != null && keystorePassword != null) { + // SSL version + if (!Files.isReadable(Path.of(keystorePathname))) + throw new RuntimeException("Failed to start SSL API due to broken keystore"); + + // BouncyCastle-specific SSLContext build + SSLContext sslContext = SSLContext.getInstance("TLS", "BCJSSE"); + KeyManagerFactory keyManagerFactory = KeyManagerFactory.getInstance("PKIX", "BCJSSE"); + + KeyStore keyStore = KeyStore.getInstance(KeyStore.getDefaultType(), "BC"); + + try (InputStream keystoreStream = Files.newInputStream(Paths.get(keystorePathname))) { + keyStore.load(keystoreStream, keystorePassword.toCharArray()); + } + + keyManagerFactory.init(keyStore, keystorePassword.toCharArray()); + sslContext.init(keyManagerFactory.getKeyManagers(), null, new SecureRandom()); + + SslContextFactory.Server sslContextFactory = new SslContextFactory.Server(); + sslContextFactory.setSslContext(sslContext); + + this.server = new Server(); + + HttpConfiguration httpConfig = new HttpConfiguration(); + httpConfig.setSecureScheme("https"); + httpConfig.setSecurePort(Settings.getInstance().getDevProxyPort()); + + SecureRequestCustomizer src = new SecureRequestCustomizer(); + httpConfig.addCustomizer(src); + + HttpConnectionFactory httpConnectionFactory = new HttpConnectionFactory(httpConfig); + SslConnectionFactory sslConnectionFactory = new SslConnectionFactory(sslContextFactory, HttpVersion.HTTP_1_1.asString()); + + ServerConnector portUnifiedConnector = new ServerConnector(this.server, + new DetectorConnectionFactory(sslConnectionFactory), + httpConnectionFactory); + portUnifiedConnector.setHost(Network.getInstance().getBindAddress()); + portUnifiedConnector.setPort(Settings.getInstance().getDevProxyPort()); + + this.server.addConnector(portUnifiedConnector); + } else { + // Non-SSL + InetAddress bindAddr = InetAddress.getByName(Network.getInstance().getBindAddress()); + InetSocketAddress endpoint = new InetSocketAddress(bindAddr, Settings.getInstance().getDevProxyPort()); + this.server = new Server(endpoint); + } + + // Error handler + ErrorHandler errorHandler = new ApiErrorHandler(); + this.server.setErrorHandler(errorHandler); + + // Request logging + if (Settings.getInstance().isDevProxyLoggingEnabled()) { + RequestLogWriter logWriter = new RequestLogWriter("devproxy-requests.log"); + logWriter.setAppend(true); + logWriter.setTimeZone("UTC"); + RequestLog requestLog = new CustomRequestLog(logWriter, CustomRequestLog.EXTENDED_NCSA_FORMAT); + this.server.setRequestLog(requestLog); + } + + // Access handler (currently no whitelist is used) + InetAccessHandler accessHandler = new InetAccessHandler(); + this.server.setHandler(accessHandler); + + // URL rewriting + RewriteHandler rewriteHandler = new RewriteHandler(); + accessHandler.setHandler(rewriteHandler); + + // Context + ServletContextHandler context = new ServletContextHandler(ServletContextHandler.NO_SESSIONS); + context.setContextPath("/"); + rewriteHandler.setHandler(context); + + // Cross-origin resource sharing + FilterHolder corsFilterHolder = new FilterHolder(CrossOriginFilter.class); + corsFilterHolder.setInitParameter(CrossOriginFilter.ALLOWED_ORIGINS_PARAM, "*"); + corsFilterHolder.setInitParameter(CrossOriginFilter.ALLOWED_METHODS_PARAM, "GET, POST, DELETE"); + corsFilterHolder.setInitParameter(CrossOriginFilter.CHAIN_PREFLIGHT_PARAM, "false"); + context.addFilter(corsFilterHolder, "/*", null); + + // API servlet + ServletContainer container = new ServletContainer(this.config); + ServletHolder apiServlet = new ServletHolder(container); + apiServlet.setInitOrder(1); + context.addServlet(apiServlet, "/*"); + + // Start server + this.server.start(); + } catch (Exception e) { + // Failed to start + throw new DataException("Failed to start developer proxy", e); + } + } + + public void stop() { + try { + // Stop server + this.server.stop(); + } catch (Exception e) { + // Failed to stop + } + + this.server = null; + instance = null; + } + +} diff --git a/src/main/java/org/qortal/api/proxy/resource/DevProxyServerResource.java b/src/main/java/org/qortal/api/proxy/resource/DevProxyServerResource.java new file mode 100644 index 00000000..d51e6852 --- /dev/null +++ b/src/main/java/org/qortal/api/proxy/resource/DevProxyServerResource.java @@ -0,0 +1,142 @@ +package org.qortal.api.proxy.resource; + +import org.qortal.api.ApiError; +import org.qortal.api.ApiExceptionFactory; +import org.qortal.api.HTMLParser; +import org.qortal.arbitrary.misc.Service; +import org.qortal.controller.DevProxyManager; + +import javax.servlet.ServletContext; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import javax.ws.rs.GET; +import javax.ws.rs.Path; +import javax.ws.rs.PathParam; +import javax.ws.rs.core.Context; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.net.HttpURLConnection; +import java.net.URL; +import java.util.Enumeration; + + +@Path("/") +public class DevProxyServerResource { + + @Context HttpServletRequest request; + @Context HttpServletResponse response; + @Context ServletContext context; + + + @GET + public HttpServletResponse getProxyIndex() { + return this.proxy("/"); + } + + @GET + @Path("{path:.*}") + public HttpServletResponse getProxyPath(@PathParam("path") String inPath) { + return this.proxy(inPath); + } + + private HttpServletResponse proxy(String inPath) { + try { + String source = DevProxyManager.getInstance().getSourceHostAndPort(); + + // Convert localhost / 127.0.0.1 to IPv6 [::1] + if (source.startsWith("localhost") || source.startsWith("127.0.0.1")) { + int port = 80; + String[] parts = source.split(":"); + if (parts.length > 1) { + port = Integer.parseInt(parts[1]); + } + source = String.format("[::1]:%d", port); + } + + if (!inPath.startsWith("/")) { + inPath = "/" + inPath; + } + + String queryString = request.getQueryString() != null ? "?" + request.getQueryString() : ""; + + // Open URL + String urlString = String.format("http://%s%s%s", source, inPath, queryString); + URL url = new URL(urlString); + HttpURLConnection con = (HttpURLConnection) url.openConnection(); + con.setRequestMethod(request.getMethod()); + + // Proxy the request headers + Enumeration headerNames = request.getHeaderNames(); + while (headerNames.hasMoreElements()) { + String headerName = headerNames.nextElement(); + String headerValue = request.getHeader(headerName); + con.setRequestProperty(headerName, headerValue); + } + + // TODO: proxy any POST parameters from "request" to "con" + + // Proxy the response code + int responseCode = con.getResponseCode(); + response.setStatus(responseCode); + + // Proxy the response headers + for (int i = 0; ; i++) { + String headerKey = con.getHeaderFieldKey(i); + String headerValue = con.getHeaderField(i); + if (headerKey != null && headerValue != null) { + response.addHeader(headerKey, headerValue); + continue; + } + break; + } + + // Read the response body + InputStream inputStream = con.getInputStream(); + ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); + byte[] buffer = new byte[4096]; + int bytesRead; + while ((bytesRead = inputStream.read(buffer)) != -1) { + outputStream.write(buffer, 0, bytesRead); + } + byte[] data = outputStream.toByteArray(); // TODO: limit file size that can be read into memory + + // Close the streams + outputStream.close(); + inputStream.close(); + + // Extract filename + String filename = ""; + if (inPath.contains("/")) { + String[] parts = inPath.split("/"); + if (parts.length > 0) { + filename = parts[parts.length - 1]; + } + } + + // Parse and modify output if needed + if (HTMLParser.isHtmlFile(filename)) { + // HTML file - needs to be parsed + HTMLParser htmlParser = new HTMLParser("", inPath, "", false, data, "proxy", Service.APP, null, "light", true); + htmlParser.addAdditionalHeaderTags(); + response.addHeader("Content-Security-Policy", "default-src 'self' 'unsafe-inline' 'unsafe-eval'; media-src 'self' data: blob:; img-src 'self' data: blob:; connect-src 'self' ws:; font-src 'self' data:;"); + response.setContentType(con.getContentType()); + response.setContentLength(htmlParser.getData().length); + response.getOutputStream().write(htmlParser.getData()); + } + else { + // Regular file - can be streamed directly + response.addHeader("Content-Security-Policy", "default-src 'self'"); + response.setContentType(con.getContentType()); + response.setContentLength(data.length); + response.getOutputStream().write(data); + } + + } catch (IOException e) { + throw ApiExceptionFactory.INSTANCE.createCustomException(request, ApiError.INVALID_CRITERIA, e.getMessage()); + } + + return response; + } + +} diff --git a/src/main/java/org/qortal/api/resource/DeveloperResource.java b/src/main/java/org/qortal/api/resource/DeveloperResource.java new file mode 100644 index 00000000..ba534502 --- /dev/null +++ b/src/main/java/org/qortal/api/resource/DeveloperResource.java @@ -0,0 +1,96 @@ +package org.qortal.api.resource; + +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.media.Content; +import io.swagger.v3.oas.annotations.media.Schema; +import io.swagger.v3.oas.annotations.parameters.RequestBody; +import io.swagger.v3.oas.annotations.responses.ApiResponse; +import io.swagger.v3.oas.annotations.tags.Tag; +import org.qortal.api.ApiError; +import org.qortal.api.ApiErrors; +import org.qortal.api.ApiExceptionFactory; +import org.qortal.controller.DevProxyManager; +import org.qortal.repository.DataException; + +import javax.servlet.ServletContext; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import javax.ws.rs.POST; +import javax.ws.rs.Path; +import javax.ws.rs.core.Context; +import javax.ws.rs.core.MediaType; + + +@Path("/developer") +@Tag(name = "Developer Tools") +public class DeveloperResource { + + @Context HttpServletRequest request; + @Context HttpServletResponse response; + @Context ServletContext context; + + + @POST + @Path("/proxy/start") + @Operation( + summary = "Start proxy server, for real time QDN app/website development", + requestBody = @RequestBody( + description = "Host and port of source webserver to be proxied", + required = true, + content = @Content( + mediaType = MediaType.TEXT_PLAIN, + schema = @Schema( + type = "string", + example = "127.0.0.1:5173" + ) + ) + ), + responses = { + @ApiResponse( + description = "Port number of running server", + content = @Content( + mediaType = MediaType.TEXT_PLAIN, + schema = @Schema( + type = "number" + ) + ) + ) + } + ) + @ApiErrors({ApiError.INVALID_CRITERIA}) + public Integer startProxy(String sourceHostAndPort) { + // TODO: API key + DevProxyManager devProxyManager = DevProxyManager.getInstance(); + try { + devProxyManager.setSourceHostAndPort(sourceHostAndPort); + devProxyManager.start(); + return devProxyManager.getPort(); + + } catch (DataException e) { + throw ApiExceptionFactory.INSTANCE.createCustomException(request, ApiError.INVALID_CRITERIA, e.getMessage()); + } + } + + @POST + @Path("/proxy/stop") + @Operation( + summary = "Stop proxy server", + responses = { + @ApiResponse( + description = "true if stopped", + content = @Content( + mediaType = MediaType.TEXT_PLAIN, + schema = @Schema( + type = "boolean" + ) + ) + ) + } + ) + public boolean stopProxy() { + DevProxyManager devProxyManager = DevProxyManager.getInstance(); + devProxyManager.stop(); + return !devProxyManager.isRunning(); + } + +} \ No newline at end of file diff --git a/src/main/java/org/qortal/controller/DevProxyManager.java b/src/main/java/org/qortal/controller/DevProxyManager.java new file mode 100644 index 00000000..a04e87ac --- /dev/null +++ b/src/main/java/org/qortal/controller/DevProxyManager.java @@ -0,0 +1,74 @@ +package org.qortal.controller; + +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; +import org.qortal.api.DevProxyService; +import org.qortal.repository.DataException; +import org.qortal.settings.Settings; + +public class DevProxyManager { + + protected static final Logger LOGGER = LogManager.getLogger(DevProxyManager.class); + + private static DevProxyManager instance; + + private boolean running = false; + + private String sourceHostAndPort = "127.0.0.1:5173"; // Default for React/Vite + + private DevProxyManager() { + + } + + public static DevProxyManager getInstance() { + if (instance == null) + instance = new DevProxyManager(); + + return instance; + } + + public void start() throws DataException { + synchronized(this) { + if (this.running) { + // Already running + return; + } + + LOGGER.info(String.format("Starting developer proxy service on port %d", Settings.getInstance().getDevProxyPort())); + DevProxyService devProxyService = DevProxyService.getInstance(); + devProxyService.start(); + this.running = true; + } + } + + public void stop() { + synchronized(this) { + if (!this.running) { + // Not running + return; + } + + LOGGER.info(String.format("Shutting down developer proxy service")); + DevProxyService devProxyService = DevProxyService.getInstance(); + devProxyService.stop(); + this.running = false; + } + } + + public void setSourceHostAndPort(String sourceHostAndPort) { + this.sourceHostAndPort = sourceHostAndPort; + } + + public String getSourceHostAndPort() { + return this.sourceHostAndPort; + } + + public Integer getPort() { + return Settings.getInstance().getDevProxyPort(); + } + + public boolean isRunning() { + return this.running; + } + +} diff --git a/src/main/java/org/qortal/settings/Settings.java b/src/main/java/org/qortal/settings/Settings.java index cce3f441..c3d5a0c8 100644 --- a/src/main/java/org/qortal/settings/Settings.java +++ b/src/main/java/org/qortal/settings/Settings.java @@ -47,6 +47,9 @@ public class Settings { private static final int MAINNET_GATEWAY_PORT = 80; private static final int TESTNET_GATEWAY_PORT = 8080; + private static final int MAINNET_DEV_PROXY_PORT = 12393; + private static final int TESTNET_DEV_PROXY_PORT = 62393; + private static final Logger LOGGER = LogManager.getLogger(Settings.class); private static final String SETTINGS_FILENAME = "settings.json"; @@ -107,6 +110,11 @@ public class Settings { private boolean gatewayLoggingEnabled = false; private boolean gatewayLoopbackEnabled = false; + // Developer Proxy + private Integer devProxyPort; + private boolean devProxyLoggingEnabled = false; + + // Specific to this node private boolean wipeUnconfirmedOnStart = false; /** Maximum number of unconfirmed transactions allowed per account */ @@ -649,6 +657,18 @@ public class Settings { } + public int getDevProxyPort() { + if (this.devProxyPort != null) + return this.devProxyPort; + + return this.isTestNet ? TESTNET_DEV_PROXY_PORT : MAINNET_DEV_PROXY_PORT; + } + + public boolean isDevProxyLoggingEnabled() { + return this.devProxyLoggingEnabled; + } + + public boolean getWipeUnconfirmedOnStart() { return this.wipeUnconfirmedOnStart; } From d628b3ab2a303feee7a03a85240a853c3135822c Mon Sep 17 00:00:00 2001 From: CalDescent Date: Sat, 17 Jun 2023 13:04:46 +0100 Subject: [PATCH 12/12] Renamed the "usePrefix" parameter to "includeResourceIdInPrefix", and slightly modified its functionality. --- src/main/java/org/qortal/api/HTMLParser.java | 10 +++++----- .../api/domainmap/resource/DomainMapResource.java | 4 ++-- .../qortal/api/gateway/resource/GatewayResource.java | 4 ++-- .../qortal/api/restricted/resource/RenderResource.java | 4 ++-- .../org/qortal/arbitrary/ArbitraryDataRenderer.java | 8 ++++---- 5 files changed, 15 insertions(+), 15 deletions(-) diff --git a/src/main/java/org/qortal/api/HTMLParser.java b/src/main/java/org/qortal/api/HTMLParser.java index cc3102e8..f1794594 100644 --- a/src/main/java/org/qortal/api/HTMLParser.java +++ b/src/main/java/org/qortal/api/HTMLParser.java @@ -24,11 +24,11 @@ public class HTMLParser { private String theme; private boolean usingCustomRouting; - public HTMLParser(String resourceId, String inPath, String prefix, boolean usePrefix, byte[] data, + public HTMLParser(String resourceId, String inPath, String prefix, boolean includeResourceIdInPrefix, byte[] data, String qdnContext, Service service, String identifier, String theme, boolean usingCustomRouting) { - String inPathWithoutFilename = inPath.contains("/") ? inPath.substring(0, inPath.lastIndexOf('/')) : ""; - this.qdnBase = usePrefix ? String.format("%s/%s", prefix, resourceId) : ""; - this.qdnBaseWithPath = usePrefix ? String.format("%s/%s%s", prefix, resourceId, inPathWithoutFilename) : ""; + String inPathWithoutFilename = inPath.contains("/") ? inPath.substring(0, inPath.lastIndexOf('/')) : String.format("/%s",inPath); + this.qdnBase = includeResourceIdInPrefix ? String.format("%s/%s", prefix, resourceId) : prefix; + this.qdnBaseWithPath = includeResourceIdInPrefix ? String.format("%s/%s%s", prefix, resourceId, inPathWithoutFilename) : String.format("%s%s", prefix, inPathWithoutFilename); this.data = data; this.qdnContext = qdnContext; this.resourceId = resourceId; @@ -82,7 +82,7 @@ public class HTMLParser { } public static boolean isHtmlFile(String path) { - if (path.endsWith(".html") || path.endsWith(".htm")) { + if (path.endsWith(".html") || path.endsWith(".htm") || path.equals("")) { return true; } return false; diff --git a/src/main/java/org/qortal/api/domainmap/resource/DomainMapResource.java b/src/main/java/org/qortal/api/domainmap/resource/DomainMapResource.java index 4cb9f8e5..019fb753 100644 --- a/src/main/java/org/qortal/api/domainmap/resource/DomainMapResource.java +++ b/src/main/java/org/qortal/api/domainmap/resource/DomainMapResource.java @@ -48,10 +48,10 @@ public class DomainMapResource { } private HttpServletResponse get(String resourceId, ResourceIdType resourceIdType, Service service, String identifier, - String inPath, String secret58, String prefix, boolean usePrefix, boolean async) { + String inPath, String secret58, String prefix, boolean includeResourceIdInPrefix, boolean async) { ArbitraryDataRenderer renderer = new ArbitraryDataRenderer(resourceId, resourceIdType, service, identifier, inPath, - secret58, prefix, usePrefix, async, "domainMap", request, response, context); + secret58, prefix, includeResourceIdInPrefix, async, "domainMap", request, response, context); return renderer.render(); } diff --git a/src/main/java/org/qortal/api/gateway/resource/GatewayResource.java b/src/main/java/org/qortal/api/gateway/resource/GatewayResource.java index 9c77753f..5d056f30 100644 --- a/src/main/java/org/qortal/api/gateway/resource/GatewayResource.java +++ b/src/main/java/org/qortal/api/gateway/resource/GatewayResource.java @@ -90,7 +90,7 @@ public class GatewayResource { } - private HttpServletResponse parsePath(String inPath, String qdnContext, String secret58, boolean usePrefix, boolean async) { + private HttpServletResponse parsePath(String inPath, String qdnContext, String secret58, boolean includeResourceIdInPrefix, boolean async) { if (inPath == null || inPath.equals("")) { // Assume not a real file @@ -157,7 +157,7 @@ public class GatewayResource { } ArbitraryDataRenderer renderer = new ArbitraryDataRenderer(name, ResourceIdType.NAME, service, identifier, outPath, - secret58, prefix, usePrefix, async, qdnContext, request, response, context); + secret58, prefix, includeResourceIdInPrefix, async, qdnContext, request, response, context); return renderer.render(); } diff --git a/src/main/java/org/qortal/api/restricted/resource/RenderResource.java b/src/main/java/org/qortal/api/restricted/resource/RenderResource.java index 7a772f9f..92f72032 100644 --- a/src/main/java/org/qortal/api/restricted/resource/RenderResource.java +++ b/src/main/java/org/qortal/api/restricted/resource/RenderResource.java @@ -157,10 +157,10 @@ public class RenderResource { private HttpServletResponse get(String resourceId, ResourceIdType resourceIdType, Service service, String identifier, - String inPath, String secret58, String prefix, boolean usePrefix, boolean async, String theme) { + String inPath, String secret58, String prefix, boolean includeResourceIdInPrefix, boolean async, String theme) { ArbitraryDataRenderer renderer = new ArbitraryDataRenderer(resourceId, resourceIdType, service, identifier, inPath, - secret58, prefix, usePrefix, async, "render", request, response, context); + secret58, prefix, includeResourceIdInPrefix, async, "render", request, response, context); if (theme != null) { renderer.setTheme(theme); diff --git a/src/main/java/org/qortal/arbitrary/ArbitraryDataRenderer.java b/src/main/java/org/qortal/arbitrary/ArbitraryDataRenderer.java index 089a99ca..704533c8 100644 --- a/src/main/java/org/qortal/arbitrary/ArbitraryDataRenderer.java +++ b/src/main/java/org/qortal/arbitrary/ArbitraryDataRenderer.java @@ -40,7 +40,7 @@ public class ArbitraryDataRenderer { private String inPath; private final String secret58; private final String prefix; - private final boolean usePrefix; + private final boolean includeResourceIdInPrefix; private final boolean async; private final String qdnContext; private final HttpServletRequest request; @@ -48,7 +48,7 @@ public class ArbitraryDataRenderer { private final ServletContext context; public ArbitraryDataRenderer(String resourceId, ResourceIdType resourceIdType, Service service, String identifier, - String inPath, String secret58, String prefix, boolean usePrefix, boolean async, String qdnContext, + String inPath, String secret58, String prefix, boolean includeResourceIdInPrefix, boolean async, String qdnContext, HttpServletRequest request, HttpServletResponse response, ServletContext context) { this.resourceId = resourceId; @@ -58,7 +58,7 @@ public class ArbitraryDataRenderer { this.inPath = inPath; this.secret58 = secret58; this.prefix = prefix; - this.usePrefix = usePrefix; + this.includeResourceIdInPrefix = includeResourceIdInPrefix; this.async = async; this.qdnContext = qdnContext; this.request = request; @@ -159,7 +159,7 @@ public class ArbitraryDataRenderer { if (HTMLParser.isHtmlFile(filename)) { // HTML file - needs to be parsed byte[] data = Files.readAllBytes(filePath); // TODO: limit file size that can be read into memory - HTMLParser htmlParser = new HTMLParser(resourceId, inPath, prefix, usePrefix, data, qdnContext, service, identifier, theme, usingCustomRouting); + HTMLParser htmlParser = new HTMLParser(resourceId, inPath, prefix, includeResourceIdInPrefix, data, qdnContext, service, identifier, theme, usingCustomRouting); htmlParser.addAdditionalHeaderTags(); response.addHeader("Content-Security-Policy", "default-src 'self' 'unsafe-inline' 'unsafe-eval'; media-src 'self' data: blob:; img-src 'self' data: blob:;"); response.setContentType(context.getMimeType(filename));