Compare commits

...

39 commits

Author SHA1 Message Date
fb159c17a0 i18n: chinese
Some checks failed
Build / Linux Build (push) Has been cancelled
Build / Windows Build (push) Has been cancelled
Build / MacOS Build (push) Has been cancelled
2025-04-27 21:22:08 +03:00
f7eb6580cc tor: trim address on send
Some checks are pending
Build / Linux Build (push) Waiting to run
Build / Windows Build (push) Waiting to run
Build / MacOS Build (push) Waiting to run
2025-04-27 19:51:06 +03:00
43720b34ba fix: external connection deletion
Some checks failed
Build / Linux Build (push) Has been cancelled
Build / Windows Build (push) Has been cancelled
Build / MacOS Build (push) Has been cancelled
2025-04-23 15:10:48 +03:00
f1f0f002ce fix: content redraw at connections 2025-04-23 13:09:31 +03:00
86afa21a60 node: do not remove lock file on cleanup
Some checks are pending
Build / Linux Build (push) Waiting to run
Build / Windows Build (push) Waiting to run
Build / MacOS Build (push) Waiting to run
2025-04-23 12:38:23 +03:00
0169acba81 build: use zig linker for macos and linux for arm on x86
Some checks failed
Build / Linux Build (push) Has been cancelled
Build / Windows Build (push) Has been cancelled
Build / MacOS Build (push) Has been cancelled
2025-04-02 22:30:59 +03:00
073d950d41 github: disable release build
Some checks are pending
Build / Linux Build (push) Waiting to run
Build / Windows Build (push) Waiting to run
Build / MacOS Build (push) Waiting to run
2025-04-02 21:10:45 +03:00
4eaaebd739 release: v0.2.4
Some checks failed
Build / Linux Build (push) Has been cancelled
Build / Windows Build (push) Has been cancelled
Build / MacOS Build (push) Has been cancelled
2025-04-02 20:48:58 +03:00
a9e2106fda git: ignore cargo parse result file
Some checks are pending
Build / Linux Build (push) Waiting to run
Build / Windows Build (push) Waiting to run
Build / MacOS Build (push) Waiting to run
2025-04-02 20:48:11 +03:00
8b427989c5 github: disable release build
Some checks are pending
Build / Linux Build (push) Waiting to run
Build / Windows Build (push) Waiting to run
Build / MacOS Build (push) Waiting to run
2025-04-02 20:37:47 +03:00
f16ce3c69b fix: transparent background on desktop 2025-04-02 20:37:23 +03:00
a1b3330e5e async: use tokio for thread block calls
Some checks are pending
Build / Linux Build (push) Waiting to run
Build / Windows Build (push) Waiting to run
Build / MacOS Build (push) Waiting to run
2025-04-02 19:15:20 +03:00
3da8f5420b build: update tor arti 0.29.0
Some checks are pending
Build / Linux Build (push) Waiting to run
Build / Windows Build (push) Waiting to run
Build / MacOS Build (push) Waiting to run
2025-04-02 17:05:20 +03:00
109e896506 tor: clean error after start
Some checks are pending
Build / Linux Build (push) Waiting to run
Build / Windows Build (push) Waiting to run
Build / MacOS Build (push) Waiting to run
2025-04-02 16:47:07 +03:00
8ad38f381e ui: change values on enter press at node settings modals
Some checks are pending
Build / Linux Build (push) Waiting to run
Build / Windows Build (push) Waiting to run
Build / MacOS Build (push) Waiting to run
2025-04-02 15:49:07 +03:00
1e32315346 win: use system window frame
Some checks are pending
Build / Linux Build (push) Waiting to run
Build / Windows Build (push) Waiting to run
Build / MacOS Build (push) Waiting to run
2025-04-02 15:22:15 +03:00
ef8c645a6a win: allow downgrade install
Some checks are pending
Build / Linux Build (push) Waiting to run
Build / Windows Build (push) Waiting to run
Build / MacOS Build (push) Waiting to run
2025-04-02 14:32:00 +03:00
15ecdf1e57 build: update guid for win installer
Some checks are pending
Build / Linux Build (push) Waiting to run
Build / Windows Build (push) Waiting to run
Build / MacOS Build (push) Waiting to run
2025-04-02 13:31:04 +03:00
587b00c93a build: version for windows
Some checks failed
Build / Windows Build (push) Has been cancelled
Build / Linux Build (push) Has been cancelled
Build / MacOS Build (push) Has been cancelled
2025-04-01 00:26:59 +03:00
aba2bead27 build: update package info, other dependencies
Some checks are pending
Build / Linux Build (push) Waiting to run
Build / Windows Build (push) Waiting to run
Build / MacOS Build (push) Waiting to run
2025-03-31 21:21:51 +03:00
85ce58f69c fix: parse result from scan on top panel
Some checks are pending
Build / Linux Build (push) Waiting to run
Build / Windows Build (push) Waiting to run
Build / MacOS Build (push) Waiting to run
2025-03-31 20:46:23 +03:00
bb7e00b0eb fix: initial color theme setup
Some checks are pending
Build / Linux Build (push) Waiting to run
Build / Windows Build (push) Waiting to run
Build / MacOS Build (push) Waiting to run
2025-03-29 21:52:10 +03:00
d60b35ebef Merge pull request 'macos: use nokhwa camera dependency' (#16) from macos_camera_fix into master
Some checks are pending
Build / Linux Build (push) Waiting to run
Build / Windows Build (push) Waiting to run
Build / MacOS Build (push) Waiting to run
Reviewed-on: https://gri.mw/code/code/GUI/grim/pulls/16
2025-03-29 21:36:25 +03:00
eb60c52224 macos: use nokhwa camera dependency
Some checks failed
Build / Linux Build (push) Has been cancelled
Build / Windows Build (push) Has been cancelled
Build / MacOS Build (push) Has been cancelled
Build / Linux Build (pull_request) Has been cancelled
Build / Windows Build (pull_request) Has been cancelled
Build / MacOS Build (pull_request) Has been cancelled
2025-03-29 21:18:53 +03:00
61828ea2db build: update tor lib
Some checks failed
Build / Windows Build (push) Has been cancelled
Build / MacOS Build (push) Has been cancelled
Build / Linux Build (push) Has been cancelled
2025-03-15 20:41:30 +03:00
7e819e14d1 node: fix peers config saving
Some checks are pending
Build / MacOS Build (push) Waiting to run
Build / Linux Build (push) Waiting to run
Build / Windows Build (push) Waiting to run
2025-03-15 20:35:10 +03:00
1d9b7d9698 wallet: do not lock whole balance on send
Some checks failed
Build / Linux Build (push) Has been cancelled
Build / Windows Build (push) Has been cancelled
Build / MacOS Build (push) Has been cancelled
2025-01-14 17:55:50 +03:00
82c05588bc readme: update title
Some checks are pending
Build / Linux Build (push) Waiting to run
Build / Windows Build (push) Waiting to run
Build / MacOS Build (push) Waiting to run
2025-01-13 21:59:22 +03:00
1cddd05bc0 readme: update img tag
Some checks are pending
Build / Linux Build (push) Waiting to run
Build / Windows Build (push) Waiting to run
Build / MacOS Build (push) Waiting to run
2025-01-13 21:58:29 +03:00
8ad0d1c461 readme: update images
Some checks are pending
Build / Linux Build (push) Waiting to run
Build / Windows Build (push) Waiting to run
Build / MacOS Build (push) Waiting to run
2025-01-13 21:56:48 +03:00
a22a75913c img: add grin logo
Some checks are pending
Build / Linux Build (push) Waiting to run
Build / Windows Build (push) Waiting to run
Build / MacOS Build (push) Waiting to run
2025-01-13 21:55:55 +03:00
e797da0ed8 img: add cover
Some checks are pending
Build / Linux Build (push) Waiting to run
Build / Windows Build (push) Waiting to run
Build / MacOS Build (push) Waiting to run
2025-01-13 21:26:00 +03:00
6936c14ed2 tor: remove macos tls fix
Some checks are pending
Build / Linux Build (push) Waiting to run
Build / Windows Build (push) Waiting to run
Build / MacOS Build (push) Waiting to run
2025-01-13 21:06:34 +03:00
c626ed5a48 tor: clear data on launch, update arti to 0.26.0
Some checks failed
Build / Linux Build (push) Has been cancelled
Build / Windows Build (push) Has been cancelled
Build / MacOS Build (push) Has been cancelled
2025-01-13 19:40:09 +03:00
d79d05ef5a android: debug build without keystore 2025-01-13 16:54:27 +03:00
ardocrat
094a5b8969 release: v0.2.3 2024-10-27 20:12:12 +03:00
ardocrat
12a75f8370 macos: future version update 2024-10-27 19:45:00 +03:00
ardocrat
1c14b9aa93 tx: fix confirmation status for new block, do not show Slatepack message after finalization 2024-10-27 19:02:17 +03:00
ardocrat
8ea388554a github: macos target 11.0 2024-10-27 18:07:22 +03:00
44 changed files with 1892 additions and 1391 deletions

View file

@ -109,11 +109,11 @@ jobs:
- name: Install cargo-zigbuild
run: cargo install cargo-zigbuild
- name: Download SDK
run: wget https://github.com/phracker/MacOSX-SDKs/releases/download/11.3/MacOSX10.15.sdk.tar.xz
run: wget https://github.com/phracker/MacOSX-SDKs/releases/download/11.3/MacOSX11.0.sdk.tar.xz
- name: Setup SDK env
run: tar xf ${{ github.workspace }}/MacOSX10.15.sdk.tar.xz && echo "SDKROOT=${{ github.workspace }}/MacOSX10.15.sdk" >> $GITHUB_ENV
run: tar xf ${{ github.workspace }}/MacOSX11.0.sdk.tar.xz && echo "SDKROOT=${{ github.workspace }}/MacOSX11.0.sdk" >> $GITHUB_ENV
- name: Setup platform env
run: echo "MACOSX_DEPLOYMENT_TARGET=10.15" >> $GITHUB_ENV
run: echo "MACOSX_DEPLOYMENT_TARGET=11.0" >> $GITHUB_ENV
- name: Release x86
run: |
rustup target add x86_64-apple-darwin
@ -129,12 +129,6 @@ jobs:
working-directory: target/x86_64-apple-darwin/release
shell: bash
run: sha256sum grim-${{ github.ref_name }}-macos-x86_64.zip > grim-${{ github.ref_name }}-macos-x86_64-sha256sum.txt
- name: Download SDK
run: wget https://github.com/phracker/MacOSX-SDKs/releases/download/11.3/MacOSX11.0.sdk.tar.xz
- name: Setup SDK env
run: tar xf ${{ github.workspace }}/MacOSX11.0.sdk.tar.xz && echo "SDKROOT=${{ github.workspace }}/MacOSX11.0.sdk" >> $GITHUB_ENV
- name: Setup platform env
run: echo "MACOSX_DEPLOYMENT_TARGET=11.0" >> $GITHUB_ENV
- name: Release ARM
run: |
rustup target add aarch64-apple-darwin

3
.gitignore vendored
View file

@ -18,4 +18,5 @@ target
app/src/main/jniLibs
macos/cert.pem
linux/Grim.AppDir/AppRun
.intentionally-empty-file.o
.intentionally-empty-file.o
Cargo.toml-e

2183
Cargo.lock generated

File diff suppressed because it is too large Load diff

View file

@ -1,10 +1,10 @@
[package]
name = "grim"
version = "0.2.2"
authors = ["Ardocrat <ardocrat@proton.me>"]
version = "0.2.4"
authors = ["Ardocrat <ardocrat@gri.mw>"]
description = "Cross-platform GUI for Grin with focus on usability and availability to be used by anyone, anywhere."
license = "Apache-2.0"
repository = "https://github.com/ardocrat/grim"
repository = "https://gri.mw/code/GUI/grim"
keywords = [ "crypto", "grin", "mimblewimble" ]
edition = "2021"
@ -25,10 +25,9 @@ codegen-units = 1
panic = "abort"
[dependencies]
log = "0.4.22"
log = "0.4.27"
## node
openssl-sys = { version = "0.9.103", features = ["vendored"] }
grin_api = "5.3.3"
grin_chain = "5.3.3"
grin_config = "5.3.3"
@ -51,12 +50,12 @@ egui_extras = { version = "0.29.1", features = ["image", "svg"] }
rust-i18n = "2.3.1"
## other
anyhow = "1.0.89"
pin-project = "1.1.6"
anyhow = "1.0.97"
pin-project = "1.1.10"
backtrace = "0.3.74"
thiserror = "1.0.64"
futures = "0.3.31"
dirs = "5.0.1"
dirs = "6.0.0"
sys-locale = "0.3.1"
chrono = "0.4.38"
parking_lot = "0.12.3"
@ -65,11 +64,11 @@ toml = "0.8.19"
serde = "1.0.210"
local-ip-address = "0.6.3"
url = "2.5.2"
rand = "0.8.5"
serde_derive = "1.0.210"
serde_json = "1.0.128"
tokio = { version = "1.40.0", features = ["full"] }
image = "0.25.2"
rand = "0.9.0"
serde_derive = "1.0.219"
serde_json = "1.0.140"
tokio = { version = "1.44.1", features = ["full"] }
image = "0.25.6"
rqrr = "0.8.0"
qrcodegen = "1.8.0"
qrcode = "0.14.1"
@ -78,23 +77,23 @@ gif = "0.13.1"
rkv = { version = "0.19.0", features = ["lmdb"] }
## tor
arti-client = { version = "0.23.0", features = ["pt-client", "static", "onion-service-service", "onion-service-client"] }
tor-rtcompat = { version = "0.23.0", features = ["static"] }
tor-config = "0.23.0"
fs-mistrust = "0.8.0"
tor-hsservice = "0.23.0"
tor-hsrproxy = "0.23.0"
tor-keymgr = "0.23.0"
tor-llcrypto = "0.23.0"
tor-hscrypto = "0.23.0"
tor-error = "0.23.0"
arti-client = { version = "0.29.0", features = ["pt-client", "static", "onion-service-service", "onion-service-client"] }
tor-rtcompat = { version = "0.29.0", features = ["static"] }
tor-config = "0.29.0"
fs-mistrust = "0.9.1"
tor-hsservice = "0.29.0"
tor-hsrproxy = "0.29.0"
tor-keymgr = "0.29.0"
tor-llcrypto = "0.29.0"
tor-hscrypto = "0.29.0"
tor-error = "0.29.0"
sha2 = "0.10.8"
ed25519-dalek = "2.1.1"
curve25519-dalek = "4.1.3"
hyper = { version = "0.14.30", features = ["full"] }
hyper = { version = "0.14.31", features = ["full"] }
hyper-tls = "0.5.0"
tls-api = "0.9.0"
tls-api-native-tls = "0.9.0"
tls-api = "0.12.0"
tls-api-native-tls = "0.12.1"
## stratum server
tokio-old = {version = "0.2", features = ["full"], package = "tokio" }
@ -107,8 +106,7 @@ nokhwa = { version = "0.10.5", default-features = false, features = ["input-v4l"
nokhwa = { version = "0.10.5", default-features = false, features = ["input-msmf"] }
[target.'cfg(target_os = "macos")'.dependencies]
eye = { git = "https://github.com/raymanfx/eye-rs", rev = "5b7e3f7a1e79966091692896c568aab042e449ef", default-features = false }
tls-api-openssl = "0.9.0"
nokhwa-mac = { git = "https://github.com/l1npengtul/nokhwa", rev = "612c861ef153cf0ee575d8dd1413b960e4e19dd6", features = ["input-avfoundation", "output-threaded"], package = "nokhwa" }
[target.'cfg(not(target_os = "android"))'.dependencies]
env_logger = "0.11.3"
@ -127,7 +125,6 @@ winit = { version = "0.30.5", features = ["android-game-activity"] }
eframe = { version = "0.29.1", features = ["wgpu", "android-game-activity"] }
[patch.crates-io]
openpnp_capture = { git = "https://github.com/ardocrat/openpnp-capture-rs", rev = "f9b06f627c5e5d42c672d117650af700846ca6cf" }
egui_extras = { git = "https://github.com/emilk/egui", rev = "5b846b4554fe47269affb43efef2cad8710a8a47" }
egui = { git = "https://github.com/emilk/egui", rev = "5b846b4554fe47269affb43efef2cad8710a8a47" }
eframe = { git = "https://github.com/emilk/egui", rev = "5b846b4554fe47269affb43efef2cad8710a8a47" }

View file

@ -1,11 +1,11 @@
# <img height="22" src="https://github.com/ardocrat/grim/blob/master/android/app/src/main/ic_launcher-playstore.png?raw=true"> Grim <img height="20" src="https://github.com/mimblewimble/site/blob/master/assets/images/grin-logo.png?raw=true"> <img height="20" src="https://github.com/ardocrat/grim/blob/master/img/logo.png?raw=true">
# Grim <img height="20" src="https://gri.mw/code/GUI/grim/raw/branch/master/img/grin-logo.png"/> <img height="20" src="https://gri.mw/code/GUI/grim/raw/branch/master/img/logo.png"/>
Cross-platform GUI for [GRiN ツ](https://grin.mw) in [Rust](https://www.rust-lang.org/)
for maximum compatibility with original [Mimblewimble](https://github.com/mimblewimble/grin) implementation.
Initially supported platforms are Linux, Mac, Windows, limited Android and possible web support with help of [egui](https://github.com/emilk/egui) - immediate mode GUI library in pure Rust.
Named by the character [Grim](http://harrypotter.wikia.com/wiki/Grim) - the shape of a large, black, menacing, spectral giant dog.
![image](https://github.com/user-attachments/assets/a925b1c8-02c9-4b08-b888-0315d11138b6)
![image](https://gri.mw/code/GUI/grim/raw/branch/master/img/cover.png)
## Build instructions

View file

@ -11,7 +11,7 @@ android {
minSdk 24
targetSdk 33
versionCode 3
versionName "0.2.2"
versionName "0.2.4"
}
def keystorePropertiesFile = rootProject.file("keystore.properties")

BIN
img/cover.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 181 KiB

BIN
img/grin-logo.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 37 KiB

View file

@ -4,7 +4,7 @@ case $2 in
x86_64|arm)
;;
*)
echo "Usage: release_linux.sh [version] [platform]\n - platform: 'x86_64', 'arm'" >&2
echo "Usage: release_linux.sh [platform] [version]\n - platform: 'x86_64', 'arm'" >&2
exit 1
esac
@ -17,9 +17,11 @@ cd ..
[[ $2 == "x86_64" ]] && arch+=(x86_64-unknown-linux-gnu)
[[ $2 == "arm" ]] && arch+=(aarch64-unknown-linux-gnu)
cargo build --release --target ${arch}
rustup target add ${arch}
cargo install cargo-zigbuild
cargo zigbuild --release --target ${arch}
# Create AppImage with https://github.com/AppImage/appimagetool
cp target/${arch}/release/grim linux/Grim.AppDir/AppRun
rm target/${arch}/release/*.AppImage
appimagetool linux/Grim.AppDir target/${arch}/release/grim-v$1-linux-$2.AppImage
appimagetool linux/Grim.AppDir target/${arch}/release/grim-v$2-linux-$1.AppImage

294
locales/zh-CN.yml Normal file
View file

@ -0,0 +1,294 @@
lang_name: 英语
copy: 复制
paste: 粘贴
continue: 继续
complete: 完成
error: 错误
retry: 重试
close: 关闭
change: 更改
show: 显示
delete: 删除
clear: 清楚
create: 创建
id: 标识
kernel: 核心
settings: 设置
language: 语言
scan: 扫描
qr_code: 二维码
scan_qr: 扫描二维码
repeat: 重复
scan_result: 扫描结果
back: 返回
share: 分享
theme: '主题:'
dark: 深色
light: 淡色
choose_file: 选择文件
crash_report: 崩溃报告
crash_report_warning: 上次应用程序意外关闭,您可以报告开发人员崩溃事件.
confirmation: 确认
wallets:
await_conf_amount: 等待确认中
await_fin_amount: 等待确定中
locked_amount: 锁定帐户
txs_empty: '手动接收资金或通过传输接收资金 %{message} or %{transport} 更改钱包设置, 请按屏幕底部的按钮 %{settings} 按钮.'
title: 钱包
create_desc: 创建或种子单词导入已有钱包.
add: 添加钱包
name: '用户名:'
pass: '密码:'
pass_empty: 输入钱包的密码
current_pass: '目前密码:'
new_pass: '新密码:'
min_tx_conf_count: '确认交易的最低数量:'
recover: 恢复
recovery_phrase: 助记词
words_count: '字数:'
enter_word: '输入单词 #%{number}:'
not_valid_word: 输入的单词无效
not_valid_phrase: 输入的助记词无效
create_phrase_desc: 已安全地写下并保存助记词.
restore_phrase_desc: 从已保存的助记词中输入.
setup_conn_desc: 选择钱包连接到网络的方式.
conn_method: 连接方式
ext_conn: '外部连接:'
add_node: 添加节点
node_url: '节点网址:'
node_secret: 'API 密钥 (可选):'
invalid_url: 输入的网址无效
open: 打开钱包
wrong_pass: 输入的密码错误
locked: 已锁定
unlocked: 解锁
enable_node: '通过选择屏幕底部的按钮 %{settings} 启用集成节点以使用钱包或更改连接设置.'
node_loading: '集成节点同步后钱包会加载,你可选择屏幕底部的按钮 %{settings} 更改连接.'
loading: 正在加载
closing: 正在关闭
checking: 检查中
default_wallet: 默认钱包
new_account_desc: '输入新帐户的名称:'
wallet_loading: 加载钱包
wallet_closing: 关闭钱包
wallet_checking: 检查钱包
tx_loading: 加载事务
default_account: 默认账户
accounts: 账户
tx_sent: 已发送
tx_received: 已接收
tx_sending: 发送中
tx_receiving: 接收中
tx_confirming: 等待确认
tx_canceled: 已取消
tx_cancelling: 取消
tx_finalizing: 完成
tx_confirmed: 已确认
txs: 所有交易
tx: 交易
messages: 消息
transport: 传输
input_slatepack_desc: '输入收到的 Slatepack 消息创建响应或完成的请求:'
parse_slatepack_err: '读取消息时出错,请检查输入:'
pay_balance_error: '账户余额不足以支付 %{amount} ツ 和网络费用.'
parse_i1_slatepack_desc: '要支付 %{amount} ツ 请将此消息发送给接收者:'
parse_i2_slatepack_desc: '完成交易以接收 %{amount} ツ:'
parse_i3_slatepack_desc: '发布交易以完成 %{amount} ツ的接收 ツ:'
parse_s1_slatepack_desc: '要接收 %{amount} ツ 请将此消息发送给发件人:'
parse_s2_slatepack_desc: '完成交易以发送 %{amount} ツ:'
parse_s3_slatepack_desc: '发布交易以完成 %{amount} ツ的发送:'
resp_slatepack_err: '创建响应时出错,请检查输入数据或重试:'
resp_exists_err: 此交易已存在.
resp_canceled_err: 此交易已被取消.
create_request_desc: '创建发送或接收资金的请求:'
send_request_desc: '您已创建发送请求 %{amount} ツ. 将此消息发送给接收者:'
send_slatepack_err: 创建发送资金请求时出错,请检查输入数据或重试.
invoice_desc: '您已创建接收请求 %{amount} ツ. 将此消息发送给发送者:'
invoice_slatepack_err: 发票开具时出错,请检查输入数据或重试.
finalize_slatepack_err: '完结时出错,请检查输入数据或重试:'
finalize: 完成
use_dandelion: 使用蒲公英
enter_amount_send: '你有 %{amount} ツ. 输入要发送的金额:'
enter_amount_receive: '输入要接收的金额:'
recovery: 恢复
repair_wallet: 修复钱包
repair_desc: 检查钱包,必要时修复和恢复丢失的输出. 此操作需要时间.
repair_unavailable: 您需要与节点建立有效连接并完成钱包同步.
delete: 删除钱包
delete_conf: 您确定要删除钱包吗?
delete_desc: 确保您已保存恢复助记语,以便日后使用资金。.
wallet_loading_err: '同步钱包时出错,你可以通过选择屏幕底部的按钮 %{settings} 来重试或更改连接设置.'
wallet: 钱包
send: 发送
receive: 接收
settings: 钱包设置
tx_send_cancel_conf: '您确定要取消 %{amount} ツ的发送吗?'
tx_receive_cancel_conf: '您确定要取消 %{amount} ツ的接收吗?'
rec_phrase_not_found: 找不到恢复助记词.
restore_wallet_desc: 如果常规修复没有帮助,通过删除所有文件来恢复钱包.您将需要重新打开您的钱包.
transport:
desc: '使用传输同步接收或发送消息:'
tor_network: Tor 网络
connected: 已连接
connecting: 正在连接
disconnecting: 断开连接
conn_error: 连接错误
disconnected: 已断开连接
receiver_address: '接收者的地址:'
incorrect_addr_err: '输入的地址不正确:'
tor_send_error: 通过 Tor 发送时出错,请确保接收方在线, 交易已取消.
tor_autorun_desc: 是否在开钱包时启动 Tor 服务以同步接收交易.
tor_sending: '通过 Tor 发送%{amount} ツ'
tor_settings: Tor 设置
bridges: 桥梁
bridges_desc: 如果常规连接不正常,设置网桥,可以绕过 Tor 网络审查.
bin_file: '二进制文件:'
conn_line: '连接线:'
bridges_disabled: 网桥已禁用
bridge_name: '网桥%{b}'
network:
self: 网络
type: '网络类型:'
mainnet: 主网
testnet: 测试网
connections: 连接
node: 集成节点
metrics: 指标
mining: 挖矿
settings: 节点设置
enable_node: 启用节点
autorun: 自动运行
disabled_server: '按屏幕左上角的按钮 %{dots}启用集成节点或添加其他连接方法.'
no_ips: T您的系统上没有可用的 IP 地址,服务器无法启动,请检查您的网络连接.
available: 可用
not_available: 不可用
availability_check: 检查是否可用
android_warning: Android 用户注意 .要成功同步集成节点,您必须在手机的系统设置中允许访问通知并取消 Grim 应用程序的电池使用限制.这是在后台正确运行应用程序的必要操作.
sync_status:
node_restarting: 节点正在重新启动
node_down: 节点已关闭
initial: 节点正在启动
no_sync: 节点正在运行
awaiting_peers: 等待网络对点
header_sync: 正下载标题
header_sync_percent: '正在下载标题: %{percent}%'
tx_hashset_pibd: 下载状态 (PIBD)
tx_hashset_pibd_percent: '下载状态 (PIBD): %{percent}%'
tx_hashset_download: 正在下载状态
tx_hashset_download_percent: '下载状态: %{percent}%'
tx_hashset_setup_history: '正在准备状态(历史记录): %{percent}%'
tx_hashset_setup_position: '正在准备状态(位置): %{percent}%'
tx_hashset_setup: 正在准备状态
tx_hashset_range_proofs_validation: '验证状态(范围证明): %{percent}%'
tx_hashset_kernels_validation: '正在验证状态(核心): %{percent}%'
tx_hashset_save: 最终确定链状态
body_sync: 下载区块
body_sync_percent: '下载区块中: %{percent}%'
shutdown: 节点正在关闭
network_node:
header: 标题
block: 区块
hash: 哈希值
height: 高度
difficulty: 难度
time: 时间
main_pool: 主池
stem_pool: stem池
data: 数据
size: 大小 (GB)
peers: 网络对点
error_clean: 点数据已损坏,需要重新同步.
resync: 重新同步
error_p2p_api: '%{p2p_api} 服务器初始化时出错,请选择屏幕底部的按钮 %{p2p_api} 来检查 %{settings}设置.'
error_config: '配置初始化时出错,请选择屏幕底部的按钮 %{settings} 检查设置.'
error_unknown: '初始化时出错,请选择屏幕底部的按钮 %{settings} 来检查集成节点设置,或者重新同步.'
network_metrics:
loading: 指标在同步后将可用
emission: 发射
inflation: 通货膨胀
supply: 供应
block_time: Block time
reward: 奖励
difficulty_window: '难度窗口 %{size}'
network_mining:
loading: 同步后即可挖矿
info: '挖矿服务器已启用,您可以通过选择屏幕底部的按钮 %{settings} 来更改其设置。连接设备后,数据会更新.'
restart_server_required: 需要重启服务器才能应用更改.
rewards_wallet: 奖励钱包
server: 阶层服务器
address: 地址
miners: 矿工
devices: 设备
blocks_found: 找到的区块
hashrate: '哈希率 (C%{bits})'
connected: 已连接
disconnected: 已断开连接
network_settings:
change_value: 更改值
stratum_ip: '层 IP 地址:'
stratum_port: '层端口:'
port_unavailable: 指定的端口不可用
restart_node_required: 需要重启节点才能应用更改.
choose_wallet: 选择钱包
stratum_wallet_warning: 必须打开钱包才能获得奖励.
enable: 启用
disable: 禁用
restart: 重新启动
server: 服务器
api_ip: 'API IP 地址:'
api_port: 'API 端口:'
api_secret: '其它API 和 V2 所有者 API 令牌:'
foreign_api_secret: '外部 API 令牌:'
disabled: 已禁用
enabled: 已启用
ftl: '未来时间限制 (FTL):'
ftl_description: 限制未来多长时间, 相对于节点的本地时间,以秒为单位, 新区块的时间戳可以被接受.
not_valid_value: 输入的值无效
full_validation: 完全验证
full_validation_description: 在处理每个区块时是否运行全链验证(同步期间除外).
archive_mode: 存档模式
archive_mode_desc: 以全部存档模式运行全节点(同步需要更多的磁盘空间和时间).
attempt_time: '尝试挖矿时间 (秒):'
attempt_time_desc: 在停止并从池中重新收集交易之前尝试对特定标题进行挖矿的时间
min_share_diff: '可接受的最低份额难度:'
reset_settings_desc: 将节点设置重置为默认值
reset_settings: 重置设置
reset: 重置
tx_pool: 交易池
pool_fee: '接受到矿池的基本费用:'
reorg_period: '重组缓存保留期(以分钟为单位):'
max_tx_pool: '池中的最大交易数:'
max_tx_stempool: 'stem池中的最大交易数:'
max_tx_weight: '可以选择构建区块交易的最大总权重:'
epoch_duration: '纪元持续时间(以秒为单位):'
embargo_timer: '禁止计时器(以秒为单位):'
aggregation_period: '聚合周期(以秒为单位):'
stem_probability: 'stem助记词概率:'
stem_txs: stem交易
p2p_server: P2P 服务器
p2p_port: 'P2P 端口:'
add_seed: 添加 DNS 种子
seed_address: 'DNS 种子地址:'
add_peer: 添加网络对点
peer_address: '网络对点地址:'
peer_address_error: '以正确的格式输入 IP 地址或 DNS 名称确保指定的主机可用例如192.168.0.11234 或 example.com:5678'
default: 默认
allow_list: 允许列表
allow_list_desc: 仅连接到此列表中的网络对点.
deny_list: 拒绝列表
deny_list_desc: 切勿连接到此列表中的网络对点.
favourites: 收藏夹
favourites_desc: 要连接的首选网络对点列表.
ban_window: '被封禁的网络对点应该保持被封禁多长时间(以秒为单位):'
ban_window_desc: 禁止的决定是由节点 根据从网络对点收到的数据的正确性做出的.
max_inbound_count: '入站网络对点连接的最大数量:'
max_outbound_count: '最大出站网络对点连接数:'
reset_peers_desc: 重置网络对点数据。仅当查找网络对点出现问题时,才请谨慎使用它.
reset_peers: 重置网络对点
modal:
cancel: 取消
save: 保存
add: 添加
modal_exit:
description: 您确定要退出应用程序吗?
exit: 退出

View file

@ -21,7 +21,7 @@
<key>CFBundlePackageType</key>
<string>APPL</string>
<key>CFBundleShortVersionString</key>
<string>0.2.2</string>
<string>0.2.3</string>
<key>CFBundleSupportedPlatforms</key>
<array>
<string>MacOSX</string>

View file

@ -27,23 +27,11 @@ cd ..
[[ $1 == "x86_64" ]] && arch+=(x86_64-apple-darwin)
[[ $1 == "arm" ]] && arch+=(aarch64-apple-darwin)
if [[ "$OSTYPE" != "darwin"* ]]; then
# Start release build on non-MacOS with zig linker, requires zig 0.12.1
rustup target add x86_64-apple-darwin
rustup target add aarch64-apple-darwin
[[ $1 == "universal" ]]; arch+=(universal2-apple-darwin)
cargo install cargo-zigbuild
cargo zigbuild --release --target ${arch}
else
rustup target add ${arch}
if [[ $1 == "universal" ]]; then
cargo build --release --target x86_64-apple-darwin
cargo build --release --target aarch64-apple-darwin
lipo -create -output target/grim target/aarch64-apple-darwin/release/grim target/x86_64-apple-darwin/release/grim
else
cargo build --release --target ${arch}
fi
fi
rustup target add x86_64-apple-darwin
rustup target add aarch64-apple-darwin
[[ $1 == "universal" ]]; arch+=(universal2-apple-darwin)
cargo install cargo-zigbuild
cargo zigbuild --release --target ${arch}
rm -f .intentionally-empty-file.o

View file

@ -63,8 +63,8 @@ function build_apk() {
./gradlew clean
# Build signed apk if keystore exists
if [ ! -f keystore.properties ]; then
./gradlew assembleRelease
apk_path=app/build/outputs/apk/release/app-release.apk
./gradlew assembleDebug
apk_path=app/build/outputs/apk/debug/app-debug.apk
else
./gradlew assembleSignedRelease
apk_path=app/build/outputs/apk/signedRelease/app-signedRelease.apk

View file

@ -70,13 +70,11 @@ else
exit 1
fi
# ==================================
# Update Android build.gradle file
# and package version at Cargo.toml
# ==================================
# Update version in build.gradle
# Update version for Windows installer.
sed -i '' -e 's/" Version="[^\"]*"/" Version="'"$VERSION_NEXT"'"/g' wix/main.wxs
sed -i '' -e 's/<Package Id="[^\"]*"/<Package Id="'"$(uuidgen)"'"/g' wix/main.wxs
# Update Android version in build.gradle
sed -i'.bak' -e 's/versionName [0-9a-zA-Z -_]*/versionName "'"$VERSION_NEXT"'"/' android/app/build.gradle
rm -f android/app/build.gradle.bak

50
src/gui/app.rs Normal file → Executable file
View file

@ -16,7 +16,6 @@ use std::sync::atomic::{AtomicBool, Ordering};
use lazy_static::lazy_static;
use egui::{Align, Context, CursorIcon, Layout, Modifiers, ResizeDirection, Rounding, Stroke, UiBuilder, ViewportCommand};
use egui::epaint::{RectShape};
use egui::os::OperatingSystem;
use crate::AppConfig;
use crate::gui::Colors;
@ -108,16 +107,25 @@ impl<Platform: PlatformCallbacks> App<Platform> {
let is_fullscreen = ui.ctx().input(|i| {
i.viewport().fullscreen.unwrap_or(false)
});
if OperatingSystem::from_target_os() != OperatingSystem::Mac {
self.desktop_window_ui(ui, is_fullscreen);
} else {
self.window_title_ui(ui, is_fullscreen);
ui.add_space(-1.0);
Self::title_panel_bg(ui);
self.content.ui(ui, &self.platform);
let os = egui::os::OperatingSystem::from_target_os();
match os {
egui::os::OperatingSystem::Mac => {
self.window_title_ui(ui, is_fullscreen);
ui.add_space(-1.0);
Self::title_panel_bg(ui, true);
self.content.ui(ui, &self.platform);
}
egui::os::OperatingSystem::Windows => {
Self::title_panel_bg(ui, false);
self.content.ui(ui, &self.platform);
}
_ => {
self.custom_frame_ui(ui, is_fullscreen);
}
}
} else {
self.mobile_window_ui(ui);
Self::title_panel_bg(ui, false);
self.content.ui(ui, &self.platform);
}
// Provide incoming data to wallets.
@ -135,14 +143,8 @@ impl<Platform: PlatformCallbacks> App<Platform> {
}
}
/// Draw mobile platform window content.
fn mobile_window_ui(&mut self, ui: &mut egui::Ui) {
Self::title_panel_bg(ui);
self.content.ui(ui, &self.platform);
}
/// Draw desktop platform window content.
fn desktop_window_ui(&mut self, ui: &mut egui::Ui, is_fullscreen: bool) {
/// Draw custom desktop window frame content.
fn custom_frame_ui(&mut self, ui: &mut egui::Ui, is_fullscreen: bool) {
let content_bg_rect = {
let mut r = ui.max_rect();
if !is_fullscreen {
@ -169,7 +171,7 @@ impl<Platform: PlatformCallbacks> App<Platform> {
ui.add_space(-1.0);
// Draw title panel background.
Self::title_panel_bg(ui);
Self::title_panel_bg(ui, true);
let content_rect = {
let mut rect = ui.max_rect();
@ -197,10 +199,10 @@ impl<Platform: PlatformCallbacks> App<Platform> {
}
/// Draw title panel background.
fn title_panel_bg(ui: &mut egui::Ui) {
fn title_panel_bg(ui: &mut egui::Ui, window_title: bool) {
let title_rect = {
let mut rect = ui.max_rect();
if View::is_desktop() {
if window_title {
rect.min.y += Content::WINDOW_TITLE_HEIGHT - 0.5;
}
rect.max.y = rect.min.y + View::get_top_inset() + TitlePanel::HEIGHT;
@ -223,7 +225,7 @@ impl<Platform: PlatformCallbacks> App<Platform> {
r.max.y += TitlePanel::HEIGHT - 1.0;
r
};
let is_mac = OperatingSystem::from_target_os() == OperatingSystem::Mac;
let is_mac = egui::os::OperatingSystem::from_target_os() == egui::os::OperatingSystem::Mac;
let window_title_bg = RectShape::new(title_bg_rect, if is_fullscreen || is_mac {
Rounding::ZERO
} else {
@ -401,8 +403,10 @@ impl<Platform: PlatformCallbacks> eframe::App for App<Platform> {
}
fn clear_color(&self, _visuals: &egui::Visuals) -> [f32; 4] {
let is_mac = OperatingSystem::from_target_os() == OperatingSystem::Mac;
if !View::is_desktop() || is_mac {
let os = egui::os::OperatingSystem::from_target_os();
let is_win = os == egui::os::OperatingSystem::Windows;
let is_mac = os == egui::os::OperatingSystem::Mac;
if !View::is_desktop() || is_win || is_mac {
return Colors::fill_lite().to_normalized_gamma_f32();
}
Colors::TRANSPARENT.to_normalized_gamma_f32()

View file

@ -52,7 +52,7 @@ impl Desktop {
}
}
#[allow(dead_code)]
// #[allow(dead_code)]
#[cfg(not(target_os = "macos"))]
fn start_camera_capture(cameras_amount: Arc<AtomicUsize>,
camera_index: Arc<AtomicUsize>,
@ -109,52 +109,56 @@ impl Desktop {
fn start_camera_capture(cameras_amount: Arc<AtomicUsize>,
camera_index: Arc<AtomicUsize>,
stop_camera: Arc<AtomicBool>) {
use image::{ExtendedColorType, ImageBuffer, ImageEncoder, Rgb};
use eye::hal::{traits::{Context, Device, Stream}, PlatformContext};
use image::codecs::jpeg::JpegEncoder;
use nokhwa_mac::nokhwa_initialize;
use nokhwa_mac::pixel_format::RgbFormat;
use nokhwa_mac::utils::{CameraIndex, RequestedFormat, RequestedFormatType};
use nokhwa_mac::utils::ApiBackend;
use nokhwa_mac::query;
use nokhwa_mac::CallbackCamera;
let index = camera_index.load(Ordering::Relaxed);
let devices = PlatformContext::default().devices().unwrap_or(vec![]);
cameras_amount.store(devices.len(), Ordering::Relaxed);
if devices.is_empty() || index >= devices.len() {
return;
}
// Ask permission to open camera.
nokhwa_initialize(|_| {});
// Capture images at separate thread.
let uri = devices[camera_index.load(Ordering::Relaxed)].uri.clone();
thread::spawn(move || {
if let Ok(dev) = PlatformContext::default().open_device(&uri) {
let streams = dev.streams().unwrap_or(vec![]);
if streams.is_empty() {
return;
}
let stream_desc = streams[0].clone();
let w = stream_desc.width;
let h = stream_desc.height;
if let Ok(mut stream) = dev.start_stream(&stream_desc) {
let cameras = query(ApiBackend::Auto).unwrap();
cameras_amount.store(cameras.len(), Ordering::Relaxed);
let index = camera_index.load(Ordering::Relaxed);
if cameras.is_empty() || index >= cameras.len() {
return;
}
// Start camera.
let camera_index = CameraIndex::Index(camera_index.load(Ordering::Relaxed) as u32);
let camera_callback = CallbackCamera::new(
camera_index,
RequestedFormat::new::<RgbFormat>(RequestedFormatType::AbsoluteHighestFrameRate),
|_| {}
);
if let Ok(mut cb) = camera_callback {
if cb.open_stream().is_ok() {
loop {
// Stop if camera was stopped.
if stop_camera.load(Ordering::Relaxed) {
stop_camera.store(false, Ordering::Relaxed);
// Clear image.
let mut w_image = LAST_CAMERA_IMAGE.write();
*w_image = None;
break;
}
// Get a frame.
let frame = stream.next()
.expect("Stream is dead")
.expect("Failed to capture a frame");
let mut out = vec![];
if let Some(buf) = ImageBuffer::<Rgb<u8>, &[u8]>::from_raw(w, h, &frame) {
JpegEncoder::new(&mut out)
.write_image(buf.as_raw(), w, h, ExtendedColorType::Rgb8)
.unwrap_or_default();
// Get image from camera.
if let Ok(frame) = cb.poll_frame() {
let image = frame.decode_image::<RgbFormat>().unwrap();
let mut bytes: Vec<u8> = Vec::new();
let format = image::ImageFormat::Jpeg;
// Convert image to Jpeg format.
image.write_to(&mut std::io::Cursor::new(&mut bytes), format).unwrap();
let mut w_image = LAST_CAMERA_IMAGE.write();
*w_image = Some((bytes, 0));
} else {
out = frame.to_vec();
// Clear image.
let mut w_image = LAST_CAMERA_IMAGE.write();
*w_image = None;
break;
}
// Save image.
let mut w_image = LAST_CAMERA_IMAGE.write();
*w_image = Some((out, 0));
}
}
}

View file

@ -290,7 +290,7 @@ impl CameraContent {
// Launch scanner at separate thread.
thread::spawn(move || {
tokio::runtime::Builder::new_multi_thread()
tokio::runtime::Builder::new_current_thread()
.enable_all()
.build()
.unwrap()

6
src/gui/views/modal.rs Normal file → Executable file
View file

@ -185,7 +185,8 @@ impl Modal {
});
// Setup background rect.
let bg_rect = if View::is_desktop() {
let is_win = OperatingSystem::Windows == OperatingSystem::from_target_os();
let bg_rect = if View::is_desktop() && !is_win {
let mut r = ctx.screen_rect();
let is_mac = OperatingSystem::Mac == OperatingSystem::from_target_os();
if !is_mac && !is_fullscreen {
@ -255,7 +256,8 @@ impl Modal {
let x_align = View::get_left_inset() - View::get_right_inset();
let is_mac = OperatingSystem::Mac == OperatingSystem::from_target_os();
let extra_y = if View::is_desktop() {
let is_win = OperatingSystem::Windows == OperatingSystem::from_target_os();
let extra_y = if View::is_desktop() && !is_win {
Content::WINDOW_TITLE_HEIGHT + if !is_mac {
Content::WINDOW_FRAME_MARGIN
} else {

View file

@ -105,7 +105,7 @@ impl ConnectionsContent {
let ext_conn_size = ext_conn_list.len();
if ext_conn_size != 0 {
ui.add_space(8.0);
for (index, conn) in ext_conn_list.iter().filter(|c| !c.deleted).enumerate() {
for (index, conn) in ext_conn_list.iter().enumerate() {
ui.horizontal_wrapped(|ui| {
// Draw connection list item.
Self::ext_conn_item_ui(ui, conn, index, ext_conn_size, |ui| {

View file

@ -187,7 +187,7 @@ impl NetworkContent {
});
// Redraw after delay if node is running at non-dual-panel mode.
if !dual_panel && Content::is_network_panel_open() && Node::is_running() {
if ((!dual_panel && Content::is_network_panel_open()) || dual_panel) && Node::is_running() {
ui.ctx().request_repaint_after(Node::STATS_UPDATE_DELAY);
}
}

View file

@ -193,6 +193,11 @@ impl DandelionSetup {
}
};
// Continue on Enter key press.
View::on_enter_key(ui, || {
on_save();
});
ui.columns(2, |columns| {
columns[0].vertical_centered_justified(|ui| {
View::button(ui, t!("modal.cancel"), Colors::white_or_black(false), || {
@ -269,6 +274,11 @@ impl DandelionSetup {
}
};
// Continue on Enter key press.
View::on_enter_key(ui, || {
on_save();
});
ui.columns(2, |columns| {
columns[0].vertical_centered_justified(|ui| {
View::button(ui, t!("modal.cancel"), Colors::white_or_black(false), || {
@ -346,6 +356,11 @@ impl DandelionSetup {
}
};
// Continue on Enter key press.
View::on_enter_key(ui, || {
on_save();
});
ui.columns(2, |columns| {
columns[0].vertical_centered_justified(|ui| {
View::button(ui, t!("modal.cancel"), Colors::white_or_black(false), || {
@ -423,6 +438,11 @@ impl DandelionSetup {
}
};
// Continue on Enter key press.
View::on_enter_key(ui, || {
on_save();
});
ui.columns(2, |columns| {
columns[0].vertical_centered_justified(|ui| {
View::button(ui, t!("modal.cancel"), Colors::white_or_black(false), || {

View file

@ -310,7 +310,7 @@ impl NodeSetup {
ui.spacing_mut().item_spacing = egui::Vec2::new(8.0, 0.0);
// Save button callback.
let on_save = || {
let mut on_save = || {
// Check if port is available.
let (api_ip, _) = NodeConfig::get_api_ip_port();
let available = NodeConfig::is_api_port_available(&api_ip, &self.api_port_edit);
@ -330,6 +330,11 @@ impl NodeSetup {
}
};
// Continue on Enter key press.
View::on_enter_key(ui, || {
on_save();
});
ui.columns(2, |columns| {
columns[0].vertical_centered_justified(|ui| {
View::button(ui, t!("modal.cancel"), Colors::white_or_black(false), || {
@ -427,6 +432,11 @@ impl NodeSetup {
modal.close();
};
// Continue on Enter key press.
View::on_enter_key(ui, || {
on_save();
});
ui.columns(2, |columns| {
columns[0].vertical_centered_justified(|ui| {
View::button(ui, t!("modal.cancel"), Colors::white_or_black(false), || {
@ -509,6 +519,11 @@ impl NodeSetup {
}
};
// Continue on Enter key press.
View::on_enter_key(ui, || {
on_save();
});
ui.columns(2, |columns| {
columns[0].vertical_centered_justified(|ui| {
View::button(ui, t!("modal.cancel"), Colors::white_or_black(false), || {

View file

@ -249,16 +249,16 @@ impl P2PSetup {
View::button(ui,
format!("{} {}", PLUG, &port),
Colors::white_or_black(false), || {
// Setup values for modal.
self.port_edit = port;
self.port_available_edit = self.is_port_available;
// Show p2p port modal.
Modal::new(PORT_MODAL)
.position(ModalPosition::CenterTop)
.title(t!("network_settings.change_value"))
.show();
cb.show_keyboard();
});
// Setup values for modal.
self.port_edit = port;
self.port_available_edit = self.is_port_available;
// Show p2p port modal.
Modal::new(PORT_MODAL)
.position(ModalPosition::CenterTop)
.title(t!("network_settings.change_value"))
.show();
cb.show_keyboard();
});
ui.add_space(6.0);
// Show error when p2p port is unavailable.
@ -300,7 +300,7 @@ impl P2PSetup {
ui.spacing_mut().item_spacing = egui::Vec2::new(8.0, 0.0);
// Save button callback.
let on_save = || {
let mut on_save = || {
// Check if port is available.
let available = NodeConfig::is_p2p_port_available(&self.port_edit);
self.port_available_edit = available;
@ -317,6 +317,11 @@ impl P2PSetup {
}
};
// Continue on Enter key press.
View::on_enter_key(ui, || {
on_save();
});
ui.columns(2, |columns| {
columns[0].vertical_centered_justified(|ui| {
View::button(ui, t!("modal.cancel"), Colors::white_or_black(false), || {
@ -370,8 +375,10 @@ impl P2PSetup {
ui.label(RichText::new(desc)
.size(16.0)
.color(Colors::inactive_text()));
ui.add_space(12.0);
} else if !peers.is_empty() {
ui.add_space(12.0);
}
ui.add_space(12.0);
let add_text = if peer_type == &PeerType::CustomSeed {
format!("{} {}", PLUS_CIRCLE, t!("network_settings.add_seed"))
@ -438,7 +445,7 @@ impl P2PSetup {
ui.spacing_mut().item_spacing = egui::Vec2::new(8.0, 0.0);
// Save button callback.
let on_save = || {
let mut on_save = || {
// Check if peer is correct and/or available.
let peer = self.peer_edit.clone();
let is_correct_address = PeersConfig::peer_to_addr(peer.clone()).is_some();
@ -460,6 +467,11 @@ impl P2PSetup {
}
};
// Continue on Enter key press.
View::on_enter_key(ui, || {
on_save();
});
ui.columns(2, |columns| {
columns[0].vertical_centered_justified(|ui| {
View::button(ui, t!("modal.cancel"), Colors::white_or_black(false), || {
@ -509,15 +521,15 @@ impl P2PSetup {
View::button(ui,
format!("{} {}", PROHIBIT_INSET, &ban_window),
Colors::white_or_black(false), || {
// Setup values for modal.
self.ban_window_edit = ban_window;
// Show ban window period setup modal.
Modal::new(BAN_WINDOW_MODAL)
.position(ModalPosition::CenterTop)
.title(t!("network_settings.change_value"))
.show();
cb.show_keyboard();
});
// Setup values for modal.
self.ban_window_edit = ban_window;
// Show ban window period setup modal.
Modal::new(BAN_WINDOW_MODAL)
.position(ModalPosition::CenterTop)
.title(t!("network_settings.change_value"))
.show();
cb.show_keyboard();
});
ui.add_space(6.0);
ui.label(RichText::new(t!("network_settings.ban_window_desc"))
.size(16.0)
@ -565,6 +577,11 @@ impl P2PSetup {
}
};
// Continue on Enter key press.
View::on_enter_key(ui, || {
on_save();
});
ui.columns(2, |columns| {
columns[0].vertical_centered_justified(|ui| {
View::button(ui, t!("modal.cancel"), Colors::white_or_black(false), || {
@ -593,15 +610,15 @@ impl P2PSetup {
View::button(ui,
format!("{} {}", ARROW_FAT_LINES_DOWN, &max_inbound),
Colors::white_or_black(false), || {
// Setup values for modal.
self.max_inbound_count = max_inbound;
// Show maximum number of inbound peers setup modal.
Modal::new(MAX_INBOUND_MODAL)
.position(ModalPosition::CenterTop)
.title(t!("network_settings.change_value"))
.show();
cb.show_keyboard();
});
// Setup values for modal.
self.max_inbound_count = max_inbound;
// Show maximum number of inbound peers setup modal.
Modal::new(MAX_INBOUND_MODAL)
.position(ModalPosition::CenterTop)
.title(t!("network_settings.change_value"))
.show();
cb.show_keyboard();
});
ui.add_space(6.0);
}
@ -644,6 +661,11 @@ impl P2PSetup {
}
};
// Continue on Enter key press.
View::on_enter_key(ui, || {
on_save();
});
ui.columns(2, |columns| {
columns[0].vertical_centered_justified(|ui| {
View::button(ui, t!("modal.cancel"), Colors::white_or_black(false), || {
@ -671,15 +693,15 @@ impl P2PSetup {
View::button(ui,
format!("{} {}", ARROW_FAT_LINES_UP, &max_outbound),
Colors::white_or_black(false), || {
// Setup values for modal.
self.max_outbound_count = max_outbound;
// Show maximum number of outbound peers setup modal.
Modal::new(MAX_OUTBOUND_MODAL)
.position(ModalPosition::CenterTop)
.title(t!("network_settings.change_value"))
.show();
cb.show_keyboard();
});
// Setup values for modal.
self.max_outbound_count = max_outbound;
// Show maximum number of outbound peers setup modal.
Modal::new(MAX_OUTBOUND_MODAL)
.position(ModalPosition::CenterTop)
.title(t!("network_settings.change_value"))
.show();
cb.show_keyboard();
});
ui.add_space(6.0);
}
@ -722,6 +744,11 @@ impl P2PSetup {
}
};
// Continue on Enter key press.
View::on_enter_key(ui, || {
on_save();
});
ui.columns(2, |columns| {
columns[0].vertical_centered_justified(|ui| {
View::button(ui, t!("modal.cancel"), Colors::white_or_black(false), || {
@ -745,9 +772,9 @@ impl P2PSetup {
format!("{} {}", TRASH, t!("network_settings.reset_peers")),
Colors::red(),
Colors::white_or_black(false), || {
Node::reset_peers(false);
self.peers_reset = true;
});
Node::reset_peers(false);
self.peers_reset = true;
});
ui.add_space(6.0);
ui.label(RichText::new(t!("network_settings.reset_peers_desc"))
.size(16.0)

View file

@ -195,6 +195,12 @@ impl PoolSetup {
modal.close();
}
};
// Continue on Enter key press.
View::on_enter_key(ui, || {
on_save();
});
ui.columns(2, |columns| {
columns[0].vertical_centered_justified(|ui| {
View::button(ui, t!("modal.cancel"), Colors::white_or_black(false), || {
@ -273,6 +279,11 @@ impl PoolSetup {
}
};
// Continue on Enter key press.
View::on_enter_key(ui, || {
on_save();
});
ui.columns(2, |columns| {
columns[0].vertical_centered_justified(|ui| {
View::button(ui, t!("modal.cancel"), Colors::white_or_black(false), || {
@ -350,6 +361,11 @@ impl PoolSetup {
}
};
// Continue on Enter key press.
View::on_enter_key(ui, || {
on_save();
});
ui.columns(2, |columns| {
columns[0].vertical_centered_justified(|ui| {
View::button(ui, t!("modal.cancel"), Colors::white_or_black(false), || {
@ -429,6 +445,11 @@ impl PoolSetup {
}
};
// Continue on Enter key press.
View::on_enter_key(ui, || {
on_save();
});
ui.columns(2, |columns| {
columns[0].vertical_centered_justified(|ui| {
View::button(ui, t!("modal.cancel"), Colors::white_or_black(false), || {
@ -508,6 +529,11 @@ impl PoolSetup {
}
};
// Continue on Enter key press.
View::on_enter_key(ui, || {
on_save();
});
ui.columns(2, |columns| {
columns[0].vertical_centered_justified(|ui| {
View::button(ui, t!("modal.cancel"), Colors::white_or_black(false), || {

View file

@ -316,7 +316,7 @@ impl StratumSetup {
ui.spacing_mut().item_spacing = egui::Vec2::new(8.0, 0.0);
// Save button callback.
let on_save = || {
let mut on_save = || {
// Check if port is available.
let (stratum_ip, _) = NodeConfig::get_stratum_address();
let available = NodeConfig::is_stratum_port_available(
@ -335,6 +335,11 @@ impl StratumSetup {
}
};
// Continue on Enter key press.
View::on_enter_key(ui, || {
on_save();
});
ui.columns(2, |columns| {
columns[0].vertical_centered_justified(|ui| {
View::button(ui, t!("modal.cancel"), Colors::white_or_black(false), || {
@ -419,6 +424,11 @@ impl StratumSetup {
}
};
// Continue on Enter key press.
View::on_enter_key(ui, || {
on_save();
});
ui.columns(2, |columns| {
columns[0].vertical_centered_justified(|ui| {
View::button(ui, t!("modal.cancel"), Colors::white_or_black(false), || {
@ -497,6 +507,11 @@ impl StratumSetup {
}
};
// Continue on Enter key press.
View::on_enter_key(ui, || {
on_save();
});
ui.columns(2, |columns| {
columns[0].vertical_centered_justified(|ui| {
View::button(ui, t!("modal.cancel"), Colors::white_or_black(false), || {

View file

@ -104,10 +104,7 @@ impl WalletConnectionModal {
if !ext_conn_list.is_empty() {
ui.add_space(8.0);
for (index, conn) in ext_conn_list.iter().filter(|c| !c.deleted).enumerate() {
if conn.deleted {
continue;
}
for (index, conn) in ext_conn_list.iter().enumerate() {
ui.horizontal_wrapped(|ui| {
let len = ext_conn_list.len();
ConnectionsContent::ext_conn_item_ui(ui, conn, index, len, |ui| {

View file

@ -23,7 +23,7 @@ use crate::gui::Colors;
use crate::gui::icons::{ARROWS_CLOCKWISE, BRIDGE, CAMERA_ROTATE, CHAT_CIRCLE_TEXT, FOLDER_USER, GEAR_FINE, GRAPH, PACKAGE, POWER, SCAN, SPINNER, USERS_THREE};
use crate::gui::platform::PlatformCallbacks;
use crate::gui::views::{Modal, Content, View, CameraContent};
use crate::gui::views::types::{LinePosition, ModalContainer, ModalPosition};
use crate::gui::views::types::{LinePosition, ModalContainer, ModalPosition, QrScanResult};
use crate::gui::views::wallets::{WalletTransactions, WalletMessages, WalletTransport};
use crate::gui::views::wallets::types::{GRIN, WalletTab, WalletTabType};
use crate::gui::views::wallets::wallet::modals::WalletAccountsModal;
@ -134,17 +134,33 @@ impl WalletContent {
.show_animated_inside(ui, show_account, |ui| {
let rect = ui.available_rect_before_wrap();
if show_qr_scan {
View::max_width_ui(ui, Content::SIDE_PANEL_WIDTH, |ui| {
self.qr_scan_content.as_mut().unwrap().ui(ui, cb);
ui.add_space(6.0);
ui.vertical_centered_justified(|ui| {
View::button(ui, t!("close"), Colors::white_or_black(false), || {
cb.stop_camera();
self.qr_scan_content = None;
if let Some(result) = self.qr_scan_content.as_ref().unwrap().qr_scan_result() {
match result {
QrScanResult::Address(address) => {
self.current_tab =
Box::new(WalletTransport::new(Some(address.to_string()), cb));
}
_ => {
self.current_tab =
Box::new(WalletMessages::new(Some(result.text())))
}
}
// Stop camera and close scanning.
cb.stop_camera();
self.qr_scan_content = None;
} else {
View::max_width_ui(ui, Content::SIDE_PANEL_WIDTH, |ui| {
self.qr_scan_content.as_mut().unwrap().ui(ui, cb);
ui.add_space(6.0);
ui.vertical_centered_justified(|ui| {
View::button(ui, t!("close"), Colors::white_or_black(false), || {
cb.stop_camera();
self.qr_scan_content = None;
});
});
ui.add_space(6.0);
});
ui.add_space(6.0);
});
}
} else {
View::max_width_ui(ui, Content::SIDE_PANEL_WIDTH * 1.3, |ui| {
self.account_ui(ui, data.unwrap(), cb);
@ -393,7 +409,7 @@ impl WalletContent {
});
columns[2].vertical_centered_justified(|ui| {
View::tab_button(ui, BRIDGE, current_type == WalletTabType::Transport, |_| {
self.current_tab = Box::new(WalletTransport::default());
self.current_tab = Box::new(WalletTransport::new(None, cb));
});
});
columns[3].vertical_centered_justified(|ui| {
@ -502,4 +518,4 @@ fn sync_progress_ui(ui: &mut egui::Ui, wallet: &Wallet) {
ui.label(RichText::new(text).size(16.0).color(Colors::inactive_text()));
});
});
}
}

View file

@ -74,7 +74,7 @@ impl ConnectionSettings {
/// Draw existing wallet connection setup content.
pub fn wallet_ui(&mut self, ui: &mut egui::Ui, wallet: &Wallet, cb: &dyn PlatformCallbacks) {
self.method = wallet.get_current_connection();
self.method = wallet.get_current_connection();
// Draw setup content.
let changed = self.ui(ui, cb);
@ -133,31 +133,39 @@ impl ConnectionSettings {
});
ui.add_space(4.0);
// Check if it's current method.
let is_current = |m: &ConnectionMethod, c: &ExternalConnection| -> Option<bool> {
match m {
ConnectionMethod::External(id, _) => if c.deleted && *id == c.id {
None
} else {
Some(*id == c.id)
},
_ => Some(false)
// Check for removed active connection.
let cur_method = &self.method.clone();
let mut ext_conn_list = ConnectionsConfig::ext_conn_list();
let has_method = !ext_conn_list.iter().filter(|c| {
match cur_method {
ConnectionMethod::Integrated => true,
ConnectionMethod::External(id, url) => id == &c.id || url == &c.url
}
};
}).collect::<Vec<&ExternalConnection>>().is_empty();
if !has_method {
match cur_method {
ConnectionMethod::External(id, url) => {
ext_conn_list.push(ExternalConnection {
id: *id,
url: url.clone(),
secret: None,
available: Some(true),
})
}
_ => {}
}
}
let method = &self.method.clone();
let ext_conn_list = ConnectionsConfig::ext_conn_list();
let ext_list = ext_conn_list.iter().filter(|c| {
!c.deleted || is_current(method, c).unwrap_or(true)
}).collect::<Vec<&ExternalConnection>>();
let ext_size = ext_list.len();
let ext_size = ext_conn_list.len();
if ext_size != 0 {
ui.add_space(8.0);
for (i, c) in ext_list.iter().enumerate() {
for (i, c) in ext_conn_list.iter().enumerate() {
ui.horizontal_wrapped(|ui| {
// Draw external connection item.
let is_current = is_current(method, c);
let is_current = match cur_method {
ConnectionMethod::External(id, url) => id == &c.id || url == &c.url,
_ => false
};
Self::ext_conn_item_ui(ui, c, is_current, i, ext_size, || {
self.method = ConnectionMethod::External(c.id, c.url.clone());
changed = true;
@ -172,7 +180,7 @@ impl ConnectionSettings {
/// Draw external connection item content.
fn ext_conn_item_ui(ui: &mut egui::Ui,
conn: &ExternalConnection,
is_current: Option<bool>,
is_current: bool,
index: usize,
len: usize,
mut on_select: impl FnMut()) {
@ -187,7 +195,7 @@ impl ConnectionSettings {
ui.vertical(|ui| {
ui.allocate_ui_with_layout(rect.size(), Layout::right_to_left(Align::Center), |ui| {
if is_current.unwrap_or(true) {
if is_current {
ui.add_space(12.0);
ui.label(RichText::new(CHECK_FAT).size(20.0).color(Colors::green()));
} else {
@ -210,11 +218,7 @@ impl ConnectionSettings {
// Setup connection status text.
let status_text = if let Some(available) = conn.available {
if available {
format!("{} {}", CHECK_CIRCLE, if is_current.is_none() {
t!("transport.connected")
} else {
t!("network.available")
})
format!("{} {}", CHECK_CIRCLE, t!("network.available"))
} else {
format!("{} {}", X_CIRCLE, t!("network.not_available"))
}

View file

@ -54,24 +54,25 @@ impl WalletTab for WalletTransport {
/// Identifier for [`Modal`] to send amount over Tor.
const SEND_TOR_MODAL: &'static str = "send_tor_modal";
/// Identifier for [`Modal`] to setup Tor service.
const TOR_SETTINGS_MODAL: &'static str = "tor_settings_modal";
/// Identifier for [`Modal`] to show QR code address image.
const QR_ADDRESS_MODAL: &'static str = "qr_address_modal";
impl Default for WalletTransport {
fn default() -> Self {
Self {
impl WalletTransport {
/// Create new transport content instance, opening sending `Modal` if address was provided.
pub fn new(address: Option<String>, cb: &dyn PlatformCallbacks) -> Self {
let mut content = Self {
send_modal_content: None,
qr_address_content: None,
settings_modal_content: None,
};
if address.is_some() {
content.show_send_tor_modal(cb, address)
}
content
}
}
impl WalletTransport {
/// Draw wallet transport content.
fn transport_ui(&mut self, ui: &mut egui::Ui, wallet: &Wallet, cb: &dyn PlatformCallbacks) {
ui.add_space(3.0);
@ -352,7 +353,7 @@ impl WalletTransport {
}
/// Show [`Modal`] to send over Tor.
pub fn show_send_tor_modal(&mut self, cb: &dyn PlatformCallbacks, address: Option<String>) {
fn show_send_tor_modal(&mut self, cb: &dyn PlatformCallbacks, address: Option<String>) {
self.send_modal_content = Some(TransportSendModal::new(address));
// Show modal.
Modal::new(SEND_TOR_MODAL)

View file

@ -18,11 +18,9 @@ use egui::{Id, RichText};
use grin_core::core::{amount_from_hr_string, amount_to_hr_string};
use grin_wallet_libwallet::{Error, SlatepackAddress};
use parking_lot::RwLock;
use tor_rtcompat::BlockOn;
use tor_rtcompat::tokio::TokioNativeTlsRuntime;
use crate::gui::Colors;
use crate::gui::platform::PlatformCallbacks;
use crate::gui::views::{CameraContent, Modal, View};
use crate::gui::views::types::TextEditOptions;
use crate::gui::views::wallets::wallet::WalletTransactionModal;
@ -302,7 +300,7 @@ impl TransportSendModal {
return;
}
let addr_str = self.address_edit.as_str();
if let Ok(addr) = SlatepackAddress::try_from(addr_str) {
if let Ok(addr) = SlatepackAddress::try_from(addr_str.trim()) {
if let Ok(a) = amount_from_hr_string(self.amount_edit.as_str()) {
cb.hide_keyboard();
modal.disable_closing();
@ -311,8 +309,10 @@ impl TransportSendModal {
let res = self.send_result.clone();
self.sending = true;
thread::spawn(move || {
let runtime = TokioNativeTlsRuntime::create().unwrap();
runtime
tokio::runtime::Builder::new_current_thread()
.enable_all()
.build()
.unwrap()
.block_on(async {
let result = wallet.send_tor(a, &addr).await;
let mut w_res = res.write();

View file

@ -339,6 +339,30 @@ impl WalletTransactions {
DOTS_THREE_CIRCLE,
t!("wallets.tx_sending"))
},
TxLogEntryType::ConfirmedCoinbase => {
let tx_h = tx.height.unwrap_or(1) - 1;
if tx_h != 0 {
let left_conf = height - tx_h;
if height >= tx_h && left_conf < COINBASE_MATURITY {
let conf_info = format!("{}/{}",
left_conf,
COINBASE_MATURITY);
format!("{} {} {}",
DOTS_THREE_CIRCLE,
t!("wallets.tx_confirming"),
conf_info
)
} else {
format!("{} {}",
DOTS_THREE_CIRCLE,
t!("wallets.tx_confirming"))
}
} else {
format!("{} {}",
DOTS_THREE_CIRCLE,
t!("wallets.tx_confirming"))
}
},
_ => {
format!("{} {}",
DOTS_THREE_CIRCLE,
@ -353,17 +377,20 @@ impl WalletTransactions {
let tx_h = tx.height.unwrap_or(1) - 1;
if tx_h != 0 {
let left_conf = height - tx_h;
let conf_info = if tx_h != 0 && height >= tx_h &&
left_conf < COINBASE_MATURITY {
format!("{}/{}", left_conf, COINBASE_MATURITY)
if height >= tx_h && left_conf < COINBASE_MATURITY {
let conf_info = format!("{}/{}",
left_conf,
COINBASE_MATURITY);
format!("{} {} {}",
DOTS_THREE_CIRCLE,
t!("wallets.tx_confirming"),
conf_info
)
} else {
"".to_string()
};
format!("{} {} {}",
DOTS_THREE_CIRCLE,
t!("wallets.tx_confirming"),
conf_info
)
format!("{} {}",
DOTS_THREE_CIRCLE,
t!("wallets.tx_confirmed"))
}
} else {
format!("{} {}",
DOTS_THREE_CIRCLE,

View file

@ -203,6 +203,7 @@ impl WalletTransactionModal {
if let Ok(_) = res {
self.show_finalization = false;
self.finalize_edit = "".to_string();
self.response_edit = "".to_string();
} else {
self.finalize_error = true;
}

View file

@ -128,7 +128,9 @@ pub fn start(options: NativeOptions, app_creator: eframe::AppCreator) -> eframe:
/// Setup application [`egui::Style`] and [`egui::Visuals`].
pub fn setup_visuals(ctx: &Context) {
let use_dark = AppConfig::dark_theme().unwrap_or_else(|| {
ctx.system_theme().unwrap_or(Theme::Dark) == Theme::Dark
let use_dark = ctx.system_theme().unwrap_or(Theme::Dark) == Theme::Dark;
AppConfig::set_dark_theme(use_dark);
use_dark
});
let mut style = (*ctx.style()).clone();

25
src/main.rs Normal file → Executable file
View file

@ -111,7 +111,6 @@ fn start_desktop_gui(platform: grim::gui::platform::Desktop) {
let os = egui::os::OperatingSystem::from_target_os();
let (width, height) = AppConfig::window_size();
let mut viewport = egui::ViewportBuilder::default()
.with_min_inner_size([AppConfig::MIN_WIDTH, AppConfig::MIN_HEIGHT])
.with_inner_size([width, height]);
@ -125,21 +124,21 @@ fn start_desktop_gui(platform: grim::gui::platform::Desktop) {
}
// Setup window decorations.
let is_mac = os == egui::os::OperatingSystem::Mac;
let is_win = os == egui::os::OperatingSystem::Windows;
viewport = viewport
.with_fullsize_content_view(true)
.with_window_level(egui::WindowLevel::Normal)
.with_title_shown(false)
.with_titlebar_buttons_shown(false)
.with_titlebar_shown(false)
.with_title_shown(is_win)
.with_titlebar_buttons_shown(is_win)
.with_titlebar_shown(is_win)
.with_transparent(true)
.with_decorations(is_mac);
.with_decorations(is_mac || is_win);
let mut options = eframe::NativeOptions {
viewport,
..Default::default()
};
// Use Glow renderer for Windows.
let is_win = os == egui::os::OperatingSystem::Windows;
options.renderer = if is_win {
eframe::Renderer::Glow
} else {
@ -172,9 +171,10 @@ fn start_desktop_gui(platform: grim::gui::platform::Desktop) {
#[allow(dead_code)]
#[cfg(not(target_os = "android"))]
fn is_app_running(data: &Option<String>) -> bool {
use tor_rtcompat::BlockOn;
let runtime = tor_rtcompat::tokio::TokioNativeTlsRuntime::create().unwrap();
let res: Result<(), Box<dyn std::error::Error>> = runtime
let res: Result<(), Box<dyn std::error::Error>> = tokio::runtime::Builder::new_current_thread()
.enable_all()
.build()
.unwrap()
.block_on(async {
use interprocess::local_socket::{
tokio::{prelude::*, Stream}
@ -211,9 +211,10 @@ fn is_app_running(data: &Option<String>) -> bool {
#[cfg(not(target_os = "android"))]
fn start_app_socket(platform: grim::gui::platform::Desktop) {
std::thread::spawn(move || {
use tor_rtcompat::BlockOn;
let runtime = tor_rtcompat::tokio::TokioNativeTlsRuntime::create().unwrap();
let _: Result<_, _> = runtime
let _ = tokio::runtime::Builder::new_current_thread()
.enable_all()
.build()
.unwrap()
.block_on(async {
use interprocess::local_socket::{
tokio::{prelude::*, Stream},

View file

@ -69,62 +69,58 @@ impl PeersConfig {
}
/// Load saved peers to node server [`ConfigMembers`] config.
pub fn load_to_server_config() {
let mut w_config = Settings::node_config_to_update();
pub fn load_to_server_config(config: &mut ConfigMembers) {
// Load seeds.
for seed in w_config.peers.seeds.clone() {
let r_config = Settings::node_config_to_read();
for seed in r_config.peers.seeds.clone() {
if let Some(p) = Self::peer_to_addr(seed.to_string()) {
let mut seeds = w_config
.node
let mut seeds = config
.server
.p2p_config
.seeds
.clone()
.unwrap_or(PeerAddrs::default());
seeds.peers.insert(seeds.peers.len(), p);
w_config.node.server.p2p_config.seeds = Some(seeds);
config.server.p2p_config.seeds = Some(seeds);
}
}
// Load allowed peers.
for peer in w_config.peers.allowed.clone() {
for peer in r_config.peers.allowed.clone() {
if let Some(p) = Self::peer_to_addr(peer.clone()) {
let mut allowed = w_config
.node
let mut allowed = config
.server
.p2p_config
.peers_allow
.clone()
.unwrap_or(PeerAddrs::default());
allowed.peers.insert(allowed.peers.len(), p);
w_config.node.server.p2p_config.peers_allow = Some(allowed);
config.server.p2p_config.peers_allow = Some(allowed);
}
}
// Load denied peers.
for peer in w_config.peers.denied.clone() {
for peer in r_config.peers.denied.clone() {
if let Some(p) = Self::peer_to_addr(peer.clone()) {
let mut denied = w_config
.node
let mut denied = config
.server
.p2p_config
.peers_deny
.clone()
.unwrap_or(PeerAddrs::default());
denied.peers.insert(denied.peers.len(), p);
w_config.node.server.p2p_config.peers_deny = Some(denied);
config.server.p2p_config.peers_deny = Some(denied);
}
}
// Load preferred peers.
for peer in &w_config.peers.preferred.clone() {
for peer in &r_config.peers.preferred.clone() {
if let Some(p) = Self::peer_to_addr(peer.clone()) {
let mut preferred = w_config
.node
let mut preferred = config
.server
.p2p_config
.peers_preferred
.clone()
.unwrap_or(PeerAddrs::default());
preferred.peers.insert(preferred.peers.len(), p);
w_config.node.server.p2p_config.peers_preferred = Some(preferred);
config.server.p2p_config.peers_preferred = Some(preferred);
}
}
}
@ -194,13 +190,13 @@ impl NodeConfig {
fn setup_default_ports(config: &mut ConfigMembers) {
let (api, p2p) = match config.server.chain_type {
ChainTypes::Mainnet => {
let api = rand::thread_rng().gen_range(30000..33000);
let p2p = rand::thread_rng().gen_range(33000..37000);
let api = rand::rng().random_range(30000..33000);
let p2p = rand::rng().random_range(33000..37000);
(api, p2p)
},
_ => {
let api = rand::thread_rng().gen_range(40000..43000);
let p2p = rand::thread_rng().gen_range(43000..47000);
let api = rand::rng().random_range(40000..43000);
let p2p = rand::rng().random_range(43000..47000);
(api, p2p)
}
};

View file

@ -16,7 +16,7 @@
//! them into a block and returns it.
use chrono::prelude::{DateTime, Utc};
use rand::{thread_rng, Rng};
use rand::{rng, Rng};
use serde_json::{json, Value};
use std::sync::Arc;
use std::thread;
@ -174,7 +174,7 @@ fn build_block(
// making sure we're not spending time mining a useless block
b.validate(&head.total_kernel_offset)?;
b.header.pow.nonce = thread_rng().gen();
b.header.pow.nonce = rng().random();
b.header.pow.secondary_scaling = difficulty.secondary_scaling;
b.header.timestamp = DateTime::from_timestamp(now_sec, 0).unwrap();

View file

@ -366,15 +366,6 @@ impl Node {
}
let config = NodeConfig::node_server_config();
let server_config = config.server.clone();
// Remove lock file if exists.
let mut lock_path = PathBuf::from(&server_config.db_root);
lock_path.push("grin.lock");
if lock_path.exists() {
fs::remove_file(lock_path).unwrap();
}
// Remove chain data.
let dirs_to_remove: Vec<&str> = vec!["header", "lmdb", "txhashset"];
for dir in dirs_to_remove {
let mut path = PathBuf::from(&server_config.db_root);
@ -529,8 +520,8 @@ impl Node {
/// Start the node [`Server`].
fn start_node_server() -> Result<Server, Error> {
// Setup server config.
PeersConfig::load_to_server_config();
let config = NodeConfig::node_server_config();
let mut config = NodeConfig::node_server_config();
PeersConfig::load_to_server_config(&mut config);
let mut server_config = config.server.clone();
// Setup Mainnet DNSSeed

View file

@ -31,6 +31,7 @@ use grin_util::secp::SecretKey;
use hyper::{Body, Uri};
use parking_lot::RwLock;
use sha2::Sha512;
use tls_api_native_tls::TlsConnector;
use tls_api::{TlsConnector as TlsConnectorTrait, TlsConnectorBuilder};
use tokio::time::sleep;
use tor_hscrypto::pk::{HsIdKey, HsIdKeypair};
@ -47,14 +48,6 @@ use tor_llcrypto::pk::ed25519::ExpandedKeypair;
use tor_rtcompat::tokio::TokioNativeTlsRuntime;
use tor_rtcompat::Runtime;
// On aarch64-apple-darwin targets there is an issue with the native and rustls
// tls implementation so this makes it fall back to the openssl variant.
//
// https://gitlab.torproject.org/tpo/core/arti/-/issues/715
#[cfg(not(all(target_vendor = "apple", target_arch = "aarch64")))]
use tls_api_native_tls::TlsConnector;
#[cfg(all(target_vendor = "apple", target_arch = "aarch64"))]
use tls_api_openssl::TlsConnector;
use crate::tor::http::ArtiHttpConnector;
use crate::tor::TorConfig;
@ -80,20 +73,16 @@ pub struct Tor {
impl Default for Tor {
fn default() -> Self {
// Cleanup keys, state and cache on start.
fs::remove_dir_all(TorConfig::keystore_path()).unwrap_or_default();
fs::remove_dir_all(TorConfig::state_path()).unwrap_or_default();
fs::remove_dir_all(TorConfig::cache_path()).unwrap_or_default();
// Create Tor client.
let runtime = TokioNativeTlsRuntime::create().unwrap();
let config = Self::build_config();
let client = if let Ok(c) = TorClient::with_runtime(runtime)
let client = TorClient::with_runtime(runtime)
.config(config.clone())
.create_unbootstrapped() {
c
} else {
fs::remove_dir_all(TorConfig::state_path()).unwrap();
fs::remove_dir_all(TorConfig::cache_path()).unwrap();
let runtime = TokioNativeTlsRuntime::create().unwrap();
TorClient::with_runtime(runtime)
.config(config.clone())
.create_unbootstrapped().unwrap()
};
.create_unbootstrapped().unwrap();
Self {
running_services: Arc::new(RwLock::new(BTreeMap::new())),
starting_services: Arc::new(RwLock::new(BTreeSet::new())),
@ -274,7 +263,8 @@ impl Tor {
return;
}
let client_check = client_thread.clone();
let url = format!("http://{}/", service.onion_name().unwrap().to_string());
let addr = service.onion_address().unwrap().to_string();
let url = format!("http://{}/", addr);
thread::spawn(move || {
// Wait 1 second to start.
thread::sleep(Duration::from_millis(1000));
@ -313,6 +303,10 @@ impl Tor {
let mut w_services =
TOR_SERVER_STATE.starting_services.write();
w_services.remove(&service_id);
// Remove service from failed.
let mut w_services =
TOR_SERVER_STATE.failed_services.write();
w_services.remove(&service_id);
// Check again after 50 seconds.
Duration::from_millis(50000)
}

View file

@ -81,7 +81,7 @@ impl WalletConfig {
min_confirmations: MIN_CONFIRMATIONS_DEFAULT,
use_dandelion: Some(true),
enable_tor_listener: Some(false),
api_port: Some(rand::thread_rng().gen_range(10000..30000)),
api_port: Some(rand::rng().random_range(10000..30000)),
};
Settings::write_to_file(&config, config_path);
config
@ -121,9 +121,7 @@ impl WalletConfig {
pub fn connection(&self) -> ConnectionMethod {
if let Some(ext_conn_id) = self.ext_conn_id {
if let Some(conn) = ConnectionsConfig::ext_conn(ext_conn_id) {
if !conn.deleted {
return ConnectionMethod::External(conn.id, conn.url);
}
return ConnectionMethod::External(conn.id, conn.url);
}
}
ConnectionMethod::Integrated

View file

@ -49,13 +49,7 @@ impl ConnectionsConfig {
/// Save connections configuration.
pub fn save(&mut self) {
// Check deleted external connections.
let mut config = self.clone();
config.external = config.external.iter()
.map(|c| c.clone())
.filter(|c| !c.deleted)
.collect::<Vec<ExternalConnection>>();
let config = self.clone();
let sub_dir = Some(AppConfig::chain_type().shortname());
Settings::write_to_file(&config, Settings::config_path(Self::FILE_NAME, sub_dir));
}
@ -106,13 +100,7 @@ impl ConnectionsConfig {
/// Remove [`ExternalConnection`] with provided identifier.
pub fn remove_ext_conn(id: i64) {
let mut w_config = Settings::conn_config_to_update();
if let Some(pos) = w_config.external.iter().position(|c| {
c.id == id
}) {
if let Some(conn) = w_config.external.get_mut(pos) {
conn.deleted = true;
w_config.save();
}
}
w_config.external = w_config.external.iter().filter(|c| c.id != id).cloned().collect();
w_config.save();
}
}

View file

@ -31,10 +31,6 @@ pub struct ExternalConnection {
/// Flag to check if server is available.
#[serde(skip_serializing, skip_deserializing)]
pub available: Option<bool>,
/// Flag to check if connection was deleted.
#[serde(skip_serializing, skip_deserializing)]
pub deleted: bool
}
/// Default external node URL for main network.
@ -61,7 +57,6 @@ impl ExternalConnection {
url: url.to_string(),
secret: None,
available: None,
deleted: false,
}
}).collect::<Vec<ExternalConnection>>()
}
@ -74,11 +69,10 @@ impl ExternalConnection {
url,
secret,
available: None,
deleted: false
}
}
/// Check external connections availability.
/// Check external connection availability.
pub fn check(id: Option<i64>, ui_ctx: &egui::Context) {
let conn_list = ConnectionsConfig::ext_conn_list();
for conn in conn_list {
@ -99,7 +93,7 @@ fn check_ext_conn(conn: &ExternalConnection, ui_ctx: &egui::Context) {
let ui_ctx = ui_ctx.clone();
ConnectionsConfig::update_ext_conn_status(conn.id, None);
std::thread::spawn(move || {
tokio::runtime::Builder::new_multi_thread()
tokio::runtime::Builder::new_current_thread()
.enable_all()
.build()
.unwrap()

View file

@ -14,7 +14,7 @@
use grin_keychain::mnemonic::{from_entropy, search, to_entropy};
use grin_util::ZeroingString;
use rand::{Rng, thread_rng};
use rand::Rng;
use crate::wallet::types::{PhraseMode, PhraseSize, PhraseWord};
@ -100,10 +100,10 @@ impl Mnemonic {
fn generate_words(mode: &PhraseMode, size: &PhraseSize) -> Vec<PhraseWord> {
match mode {
PhraseMode::Generate => {
let mut rng = thread_rng();
let mut rng = rand::rng();
let mut entropy: Vec<u8> = Vec::with_capacity(size.entropy_size());
for _ in 0..size.entropy_size() {
entropy.push(rng.gen());
entropy.push(rng.random());
}
from_entropy(&entropy).unwrap()
.split(" ")

View file

@ -617,7 +617,7 @@ impl Wallet {
let r_inst = self.instance.as_ref().read();
let instance = r_inst.clone().unwrap();
let mut api = Owner::new(instance, None);
return match parse_slatepack(&mut api, None, None, Some(text.clone())) {
match parse_slatepack(&mut api, None, None, Some(text.clone())) {
Ok(s) => Ok(s.0),
Err(e) => Err(e)
}
@ -714,7 +714,7 @@ impl Wallet {
amount,
minimum_confirmations: config.min_confirmations,
num_change_outputs: 1,
selection_strategy_is_use_all: true,
selection_strategy_is_use_all: false,
..Default::default()
};
let r_inst = self.instance.as_ref().read();
@ -857,7 +857,7 @@ impl Wallet {
src_acct_name: None,
amount: slate.amount,
minimum_confirmations: config.min_confirmations,
selection_strategy_is_use_all: true,
selection_strategy_is_use_all: false,
..Default::default()
};
let r_inst = self.instance.as_ref().read();
@ -1415,7 +1415,7 @@ fn sync_wallet_data(wallet: &Wallet, from_node: bool) {
/// Start Foreign API server to receive txs over transport and mining rewards.
fn start_api_server(wallet: &Wallet) -> Result<(ApiServer, u16), Error> {
let host = "127.0.0.1";
let port = wallet.get_config().api_port.unwrap_or(rand::thread_rng().gen_range(10000..30000));
let port = wallet.get_config().api_port.unwrap_or(rand::rng().random_range(10000..30000));
let free_port = (port..).find(|port| {
return match TcpListener::bind((host, port.to_owned())) {
Ok(_) => {

View file

@ -7,14 +7,11 @@
<?endif ?>
<Wix xmlns="http://schemas.microsoft.com/wix/2006/wi">
<Product Id="*" UpgradeCode="C19F9B41-CD13-4F0E-B27D-E0EF8CF1CE91" Version="0.1.0" Language="1033" Name="Grim" Manufacturer="Ardocrat">
<Package Id="7a18ee67-b049-4462-b18f-9e7748685781" InstallerVersion="300" Compressed="yes"/>
<Product Id="*" Version="0.2.4" UpgradeCode="C19F9B41-CD13-4F0E-B27D-E0EF8CF1CE91" Language="1033" Name="Grim" Manufacturer="Ardocrat">
<Package Id="FA6823B7-7FB1-49A4-BF64-0442BCD2724B" InstallerVersion="300" Compressed="yes"/>
<Media Id="1" Cabinet="grim.cab" EmbedCab="yes" />
<MajorUpgrade
DowngradeErrorMessage = "A newer version of [ProductName] is already installed."
AllowSameVersionUpgrades = "yes"
/>
<MajorUpgrade AllowDowngrades = "yes"/>
<Icon Id='Product.ico' SourceFile='wix\Product.ico'/>
<Property Id='ARPPRODUCTICON' Value='Product.ico' />