Crash fixes, reorg handling, and sync performance improvements
Some checks failed
Rust / Build on macOS-latest (push) Has been cancelled
Rust / Build on ubuntu-16.04 (push) Has been cancelled
Rust / Build on windows-latest (push) Has been cancelled
Rust / Linux ARMv7 (push) Has been cancelled
Rust / Linux ARM64 (push) Has been cancelled
Rust / Build on ubuntu-latest (push) Has been cancelled

- Fix FFI panics with catch_unwind and safe CString construction
- Handle poisoned mutex/RwLock after prior panics instead of crashing
- Fix empty block list panics in clear_blocks and invalidate_block
- Reuse Tokio runtime across block fetch batches to reduce overhead
- Add fetch_blocks_with_runtime for caller-managed runtime lifecycle
- Update branding, dependencies, and checkpoints for DragonX
This commit is contained in:
2026-03-21 03:55:18 -05:00
parent 505fac6b6e
commit a6a80dc224
34 changed files with 14509 additions and 14481 deletions

View File

@@ -1,88 +1,88 @@
name: Rust name: Rust
on: [push, pull_request] on: [push, pull_request]
jobs: jobs:
build: build:
name: Build on ${{ matrix.os }} name: Build on ${{ matrix.os }}
runs-on: ${{ matrix.os }} runs-on: ${{ matrix.os }}
strategy: strategy:
matrix: matrix:
os: [ubuntu-16.04, windows-latest, macOS-latest] os: [ubuntu-16.04, windows-latest, macOS-latest]
steps: steps:
- uses: actions/checkout@v1 - uses: actions/checkout@v1
- uses: actions-rs/toolchain@v1 - uses: actions-rs/toolchain@v1
with: with:
toolchain: 1.38.0 toolchain: 1.38.0
override: true override: true
- name: cargo fetch - name: cargo fetch
uses: actions-rs/cargo@v1 uses: actions-rs/cargo@v1
with: with:
command: fetch command: fetch
- name: Build - name: Build
uses: actions-rs/cargo@v1 uses: actions-rs/cargo@v1
with: with:
command: build command: build
args: --verbose --release --all args: --verbose --release --all
- name: Run tests - name: Run tests
uses: actions-rs/cargo@v1 uses: actions-rs/cargo@v1
with: with:
command: test command: test
args: --verbose --release --all args: --verbose --release --all
- name: Upload ubuntu/macos - name: Upload ubuntu/macos
uses: actions/upload-artifact@v1 uses: actions/upload-artifact@v1
if: contains(matrix.os, 'macos') || contains(matrix.os, 'ubuntu') if: contains(matrix.os, 'macos') || contains(matrix.os, 'ubuntu')
with: with:
name: ${{ matrix.os }}-silentdragonlite-cli name: ${{ matrix.os }}-silentdragonlite-cli
path: target/release/silentdragonlite-cli path: target/release/silentdragonlite-cli
- name: Upload windows - name: Upload windows
uses: actions/upload-artifact@v1 uses: actions/upload-artifact@v1
if: contains(matrix.os, 'windows') if: contains(matrix.os, 'windows')
with: with:
name: ${{ matrix.os }}-silentdragonlite-cli.exe name: ${{ matrix.os }}-silentdragonlite-cli.exe
path: target/release/silentdragonlite-cli.exe path: target/release/silentdragonlite-cli.exe
linux_arm7: linux_arm7:
name: Linux ARMv7 name: Linux ARMv7
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- uses: actions/checkout@master - uses: actions/checkout@master
- uses: actions-rs/toolchain@v1 - uses: actions-rs/toolchain@v1
with: with:
toolchain: stable toolchain: stable
target: armv7-unknown-linux-gnueabihf target: armv7-unknown-linux-gnueabihf
override: true override: true
- uses: actions-rs/cargo@v1 - uses: actions-rs/cargo@v1
with: with:
use-cross: true use-cross: true
command: build command: build
args: --release --target armv7-unknown-linux-gnueabihf args: --release --target armv7-unknown-linux-gnueabihf
- name: Upload - name: Upload
uses: actions/upload-artifact@v1 uses: actions/upload-artifact@v1
with: with:
name: linux_armv7-silentdragonlite-cli name: linux_armv7-silentdragonlite-cli
path: target/armv7-unknown-linux-gnueabihf/release/silentdragonlite-cli path: target/armv7-unknown-linux-gnueabihf/release/silentdragonlite-cli
linux_aarch64: linux_aarch64:
name: Linux ARM64 name: Linux ARM64
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- uses: actions/checkout@master - uses: actions/checkout@master
- uses: actions-rs/toolchain@v1 - uses: actions-rs/toolchain@v1
with: with:
toolchain: stable toolchain: stable
target: aarch64-unknown-linux-gnu target: aarch64-unknown-linux-gnu
override: true override: true
- uses: actions-rs/cargo@v1 - uses: actions-rs/cargo@v1
with: with:
use-cross: true use-cross: true
command: build command: build
args: --release --target aarch64-unknown-linux-gnu args: --release --target aarch64-unknown-linux-gnu
- name: Upload - name: Upload
uses: actions/upload-artifact@v1 uses: actions/upload-artifact@v1
with: with:
name: linux_aarch64-silentdragonlite-cli name: linux_aarch64-silentdragonlite-cli
path: target/aarch64-unknown-linux-gnu/release/silentdragonlite-cli path: target/aarch64-unknown-linux-gnu/release/silentdragonlite-cli

View File

@@ -1,104 +1,104 @@
name: Rust name: Rust
on: [push, pull_request] on: [push, pull_request]
jobs: jobs:
build: build:
name: Build on ${{ matrix.os }} name: Build on ${{ matrix.os }}
runs-on: ${{ matrix.os }} runs-on: ${{ matrix.os }}
strategy: strategy:
matrix: matrix:
os: [ubuntu-latest, windows-latest, macOS-latest] os: [ubuntu-latest, windows-latest, macOS-latest]
steps: steps:
- uses: actions/checkout@v1 - uses: actions/checkout@v1
- uses: actions-rs/toolchain@v1 - uses: actions-rs/toolchain@v1
with: with:
toolchain: 1.38.0 toolchain: 1.38.0
override: true override: true
- name: cargo fetch - name: cargo fetch
uses: actions-rs/cargo@v1 uses: actions-rs/cargo@v1
with: with:
command: fetch command: fetch
- name: Build - name: Build
uses: actions-rs/cargo@v1 uses: actions-rs/cargo@v1
with: with:
command: build command: build
args: --verbose --release --all args: --verbose --release --all
- name: Run tests - name: Run tests
uses: actions-rs/cargo@v1 uses: actions-rs/cargo@v1
with: with:
command: test command: test
args: --verbose --release --all args: --verbose --release --all
- name: Upload ubuntu/macos - name: Upload ubuntu/macos
uses: actions/upload-artifact@v1 uses: actions/upload-artifact@v1
if: contains(matrix.os, 'macos') || contains(matrix.os, 'ubuntu') if: contains(matrix.os, 'macos') || contains(matrix.os, 'ubuntu')
with: with:
<<<<<<< HEAD <<<<<<< HEAD
<<<<<<< HEAD <<<<<<< HEAD
name: ${{ matrix.os }}-silentdragonlite-cli name: ${{ matrix.os }}-silentdragonlite-cli
path: target/release/silentdragonlite-cli path: target/release/silentdragonlite-cli
======= =======
name: ${{ matrix.os }}-silentdragonlite-cli name: ${{ matrix.os }}-silentdragonlite-cli
path: target/release/silentdragonlite-cli path: target/release/silentdragonlite-cli
======= =======
name: ${{ matrix.os }}-zecwallet-cli name: ${{ matrix.os }}-zecwallet-cli
path: target/release/zecwallet-cli path: target/release/zecwallet-cli
>>>>>>> 959755d705b18d13a8dbdf0502c47818fe7a4e94 >>>>>>> 959755d705b18d13a8dbdf0502c47818fe7a4e94
- name: Upload windows - name: Upload windows
uses: actions/upload-artifact@v1 uses: actions/upload-artifact@v1
if: contains(matrix.os, 'windows') if: contains(matrix.os, 'windows')
with: with:
<<<<<<< HEAD <<<<<<< HEAD
name: ${{ matrix.os }}-silentdragonlite-cli.exe name: ${{ matrix.os }}-silentdragonlite-cli.exe
path: target/release/silentdragonlite-cli.exe path: target/release/silentdragonlite-cli.exe
>>>>>>> 959755d705b18d13a8dbdf0502c47818fe7a4e94 >>>>>>> 959755d705b18d13a8dbdf0502c47818fe7a4e94
======= =======
name: ${{ matrix.os }}-zecwallet-cli.exe name: ${{ matrix.os }}-zecwallet-cli.exe
path: target/release/zecwallet-cli.exe path: target/release/zecwallet-cli.exe
>>>>>>> 959755d705b18d13a8dbdf0502c47818fe7a4e94 >>>>>>> 959755d705b18d13a8dbdf0502c47818fe7a4e94
linux_arm7: linux_arm7:
name: Linux ARMv7 name: Linux ARMv7
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- uses: actions/checkout@master - uses: actions/checkout@master
- uses: actions-rs/toolchain@v1 - uses: actions-rs/toolchain@v1
with: with:
toolchain: stable toolchain: stable
target: armv7-unknown-linux-gnueabihf target: armv7-unknown-linux-gnueabihf
override: true override: true
- uses: actions-rs/cargo@v1 - uses: actions-rs/cargo@v1
with: with:
use-cross: true use-cross: true
command: build command: build
args: --release --target armv7-unknown-linux-gnueabihf args: --release --target armv7-unknown-linux-gnueabihf
- name: Upload - name: Upload
uses: actions/upload-artifact@v1 uses: actions/upload-artifact@v1
with: with:
name: linux_armv7-silentdragonlite-cli name: linux_armv7-silentdragonlite-cli
path: target/armv7-unknown-linux-gnueabihf/release/silentdragonlite-cli path: target/armv7-unknown-linux-gnueabihf/release/silentdragonlite-cli
linux_aarch64: linux_aarch64:
name: Linux ARM64 name: Linux ARM64
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- uses: actions/checkout@master - uses: actions/checkout@master
- uses: actions-rs/toolchain@v1 - uses: actions-rs/toolchain@v1
with: with:
toolchain: stable toolchain: stable
target: aarch64-unknown-linux-gnu target: aarch64-unknown-linux-gnu
override: true override: true
- uses: actions-rs/cargo@v1 - uses: actions-rs/cargo@v1
with: with:
use-cross: true use-cross: true
command: build command: build
args: --release --target aarch64-unknown-linux-gnu args: --release --target aarch64-unknown-linux-gnu
- name: Upload - name: Upload
uses: actions/upload-artifact@v1 uses: actions/upload-artifact@v1
with: with:
name: linux_aarch64-silentdragonlite-cli name: linux_aarch64-silentdragonlite-cli
path: target/aarch64-unknown-linux-gnu/release/silentdragonlite-cli path: target/aarch64-unknown-linux-gnu/release/silentdragonlite-cli

View File

@@ -1,80 +1,80 @@
name: Rust name: Rust
on: [push, pull_request] on: [push, pull_request]
jobs: jobs:
build: build:
name: Build on ${{ matrix.os }} name: Build on ${{ matrix.os }}
runs-on: ${{ matrix.os }} runs-on: ${{ matrix.os }}
strategy: strategy:
matrix: matrix:
os: [ubuntu-latest, windows-latest, macOS-latest] os: [ubuntu-latest, windows-latest, macOS-latest]
steps: steps:
- uses: actions/checkout@v1 - uses: actions/checkout@v1
- uses: actions-rs/toolchain@v1 - uses: actions-rs/toolchain@v1
with: with:
toolchain: 1.38.0 toolchain: 1.38.0
override: true override: true
- name: cargo fetch - name: cargo fetch
uses: actions-rs/cargo@v1 uses: actions-rs/cargo@v1
with: with:
command: fetch command: fetch
- name: Build - name: Build
uses: actions-rs/cargo@v1 uses: actions-rs/cargo@v1
with: with:
command: build command: build
args: --verbose --release --all args: --verbose --release --all
- name: Run tests - name: Run tests
uses: actions-rs/cargo@v1 uses: actions-rs/cargo@v1
with: with:
command: test command: test
args: --verbose --release --all args: --verbose --release --all
- name: Upload - name: Upload
uses: actions/upload-artifact@v1 uses: actions/upload-artifact@v1
with: with:
name: ${{ matrix.os }}-zecwallet-cli name: ${{ matrix.os }}-zecwallet-cli
path: target/release/zecwallet-cli path: target/release/zecwallet-cli
linux_arm7: linux_arm7:
name: Linux ARMv7 name: Linux ARMv7
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- uses: actions/checkout@master - uses: actions/checkout@master
- uses: actions-rs/toolchain@v1 - uses: actions-rs/toolchain@v1
with: with:
toolchain: stable toolchain: stable
target: armv7-unknown-linux-gnueabihf target: armv7-unknown-linux-gnueabihf
override: true override: true
- uses: actions-rs/cargo@v1 - uses: actions-rs/cargo@v1
with: with:
use-cross: true use-cross: true
command: build command: build
args: --release --target armv7-unknown-linux-gnueabihf args: --release --target armv7-unknown-linux-gnueabihf
- name: Upload - name: Upload
uses: actions/upload-artifact@v1 uses: actions/upload-artifact@v1
with: with:
name: linux_armv7-zecwallet-cli name: linux_armv7-zecwallet-cli
path: target/armv7-unknown-linux-gnueabihf/release/zecwallet-cli path: target/armv7-unknown-linux-gnueabihf/release/zecwallet-cli
linux_aarch64: linux_aarch64:
name: Linux ARM64 name: Linux ARM64
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- uses: actions/checkout@master - uses: actions/checkout@master
- uses: actions-rs/toolchain@v1 - uses: actions-rs/toolchain@v1
with: with:
toolchain: stable toolchain: stable
target: aarch64-unknown-linux-gnu target: aarch64-unknown-linux-gnu
override: true override: true
- uses: actions-rs/cargo@v1 - uses: actions-rs/cargo@v1
with: with:
use-cross: true use-cross: true
command: build command: build
args: --release --target aarch64-unknown-linux-gnu args: --release --target aarch64-unknown-linux-gnu
- name: Upload - name: Upload
uses: actions/upload-artifact@v1 uses: actions/upload-artifact@v1
with: with:
name: linux_aarch64-zecwallet-cli name: linux_aarch64-zecwallet-cli
path: target/aarch64-unknown-linux-gnu/release/zecwallet-cli path: target/aarch64-unknown-linux-gnu/release/zecwallet-cli

View File

@@ -1,93 +1,93 @@
name: Rust name: Rust
on: [push, pull_request] on: [push, pull_request]
jobs: jobs:
build: build:
name: Build on ${{ matrix.os }} name: Build on ${{ matrix.os }}
runs-on: ${{ matrix.os }} runs-on: ${{ matrix.os }}
strategy: strategy:
matrix: matrix:
os: [ubuntu-latest, windows-latest, macOS-latest] os: [ubuntu-latest, windows-latest, macOS-latest]
steps: steps:
- uses: actions/checkout@v1 - uses: actions/checkout@v1
- uses: actions-rs/toolchain@v1 - uses: actions-rs/toolchain@v1
with: with:
toolchain: 1.38.0 toolchain: 1.38.0
override: true override: true
- name: cargo fetch - name: cargo fetch
uses: actions-rs/cargo@v1 uses: actions-rs/cargo@v1
with: with:
command: fetch command: fetch
- name: Build - name: Build
uses: actions-rs/cargo@v1 uses: actions-rs/cargo@v1
with: with:
command: build command: build
args: --verbose --release --all args: --verbose --release --all
- name: Run tests - name: Run tests
uses: actions-rs/cargo@v1 uses: actions-rs/cargo@v1
with: with:
command: test command: test
args: --verbose --release --all args: --verbose --release --all
- name: Upload ubuntu/macos - name: Upload ubuntu/macos
uses: actions/upload-artifact@v1 uses: actions/upload-artifact@v1
if: contains(matrix.os, 'macos') || contains(matrix.os, 'ubuntu') if: contains(matrix.os, 'macos') || contains(matrix.os, 'ubuntu')
with: with:
<<<<<<< HEAD <<<<<<< HEAD
name: ${{ matrix.os }}-silentdragonlite-cli name: ${{ matrix.os }}-silentdragonlite-cli
path: target/release/silentdragonlite-cli path: target/release/silentdragonlite-cli
======= =======
name: ${{ matrix.os }}-silentdragonlite-cli name: ${{ matrix.os }}-silentdragonlite-cli
path: target/release/silentdragonlite-cli path: target/release/silentdragonlite-cli
- name: Upload windows - name: Upload windows
uses: actions/upload-artifact@v1 uses: actions/upload-artifact@v1
if: contains(matrix.os, 'windows') if: contains(matrix.os, 'windows')
with: with:
name: ${{ matrix.os }}-silentdragonlite-cli.exe name: ${{ matrix.os }}-silentdragonlite-cli.exe
path: target/release/silentdragonlite-cli.exe path: target/release/silentdragonlite-cli.exe
>>>>>>> 959755d705b18d13a8dbdf0502c47818fe7a4e94 >>>>>>> 959755d705b18d13a8dbdf0502c47818fe7a4e94
linux_arm7: linux_arm7:
name: Linux ARMv7 name: Linux ARMv7
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- uses: actions/checkout@master - uses: actions/checkout@master
- uses: actions-rs/toolchain@v1 - uses: actions-rs/toolchain@v1
with: with:
toolchain: stable toolchain: stable
target: armv7-unknown-linux-gnueabihf target: armv7-unknown-linux-gnueabihf
override: true override: true
- uses: actions-rs/cargo@v1 - uses: actions-rs/cargo@v1
with: with:
use-cross: true use-cross: true
command: build command: build
args: --release --target armv7-unknown-linux-gnueabihf args: --release --target armv7-unknown-linux-gnueabihf
- name: Upload - name: Upload
uses: actions/upload-artifact@v1 uses: actions/upload-artifact@v1
with: with:
name: linux_armv7-silentdragonlite-cli name: linux_armv7-silentdragonlite-cli
path: target/armv7-unknown-linux-gnueabihf/release/silentdragonlite-cli path: target/armv7-unknown-linux-gnueabihf/release/silentdragonlite-cli
linux_aarch64: linux_aarch64:
name: Linux ARM64 name: Linux ARM64
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- uses: actions/checkout@master - uses: actions/checkout@master
- uses: actions-rs/toolchain@v1 - uses: actions-rs/toolchain@v1
with: with:
toolchain: stable toolchain: stable
target: aarch64-unknown-linux-gnu target: aarch64-unknown-linux-gnu
override: true override: true
- uses: actions-rs/cargo@v1 - uses: actions-rs/cargo@v1
with: with:
use-cross: true use-cross: true
command: build command: build
args: --release --target aarch64-unknown-linux-gnu args: --release --target aarch64-unknown-linux-gnu
- name: Upload - name: Upload
uses: actions/upload-artifact@v1 uses: actions/upload-artifact@v1
with: with:
name: linux_aarch64-silentdragonlite-cli name: linux_aarch64-silentdragonlite-cli
path: target/aarch64-unknown-linux-gnu/release/silentdragonlite-cli path: target/aarch64-unknown-linux-gnu/release/silentdragonlite-cli

View File

@@ -1,88 +1,88 @@
name: Rust name: Rust
on: [push, pull_request] on: [push, pull_request]
jobs: jobs:
build: build:
name: Build on ${{ matrix.os }} name: Build on ${{ matrix.os }}
runs-on: ${{ matrix.os }} runs-on: ${{ matrix.os }}
strategy: strategy:
matrix: matrix:
os: [ubuntu-latest, windows-latest, macOS-latest] os: [ubuntu-latest, windows-latest, macOS-latest]
steps: steps:
- uses: actions/checkout@v1 - uses: actions/checkout@v1
- uses: actions-rs/toolchain@v1 - uses: actions-rs/toolchain@v1
with: with:
toolchain: 1.38.0 toolchain: 1.38.0
override: true override: true
- name: cargo fetch - name: cargo fetch
uses: actions-rs/cargo@v1 uses: actions-rs/cargo@v1
with: with:
command: fetch command: fetch
- name: Build - name: Build
uses: actions-rs/cargo@v1 uses: actions-rs/cargo@v1
with: with:
command: build command: build
args: --verbose --release --all args: --verbose --release --all
- name: Run tests - name: Run tests
uses: actions-rs/cargo@v1 uses: actions-rs/cargo@v1
with: with:
command: test command: test
args: --verbose --release --all args: --verbose --release --all
- name: Upload ubuntu/macos - name: Upload ubuntu/macos
uses: actions/upload-artifact@v1 uses: actions/upload-artifact@v1
if: contains(matrix.os, 'macos') || contains(matrix.os, 'ubuntu') if: contains(matrix.os, 'macos') || contains(matrix.os, 'ubuntu')
with: with:
name: ${{ matrix.os }}-zecwallet-cli name: ${{ matrix.os }}-zecwallet-cli
path: target/release/zecwallet-cli path: target/release/zecwallet-cli
- name: Upload windows - name: Upload windows
uses: actions/upload-artifact@v1 uses: actions/upload-artifact@v1
if: contains(matrix.os, 'windows') if: contains(matrix.os, 'windows')
with: with:
name: ${{ matrix.os }}-zecwallet-cli.exe name: ${{ matrix.os }}-zecwallet-cli.exe
path: target/release/zecwallet-cli.exe path: target/release/zecwallet-cli.exe
linux_arm7: linux_arm7:
name: Linux ARMv7 name: Linux ARMv7
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- uses: actions/checkout@master - uses: actions/checkout@master
- uses: actions-rs/toolchain@v1 - uses: actions-rs/toolchain@v1
with: with:
toolchain: stable toolchain: stable
target: armv7-unknown-linux-gnueabihf target: armv7-unknown-linux-gnueabihf
override: true override: true
- uses: actions-rs/cargo@v1 - uses: actions-rs/cargo@v1
with: with:
use-cross: true use-cross: true
command: build command: build
args: --release --target armv7-unknown-linux-gnueabihf args: --release --target armv7-unknown-linux-gnueabihf
- name: Upload - name: Upload
uses: actions/upload-artifact@v1 uses: actions/upload-artifact@v1
with: with:
name: linux_armv7-zecwallet-cli name: linux_armv7-zecwallet-cli
path: target/armv7-unknown-linux-gnueabihf/release/zecwallet-cli path: target/armv7-unknown-linux-gnueabihf/release/zecwallet-cli
linux_aarch64: linux_aarch64:
name: Linux ARM64 name: Linux ARM64
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- uses: actions/checkout@master - uses: actions/checkout@master
- uses: actions-rs/toolchain@v1 - uses: actions-rs/toolchain@v1
with: with:
toolchain: stable toolchain: stable
target: aarch64-unknown-linux-gnu target: aarch64-unknown-linux-gnu
override: true override: true
- uses: actions-rs/cargo@v1 - uses: actions-rs/cargo@v1
with: with:
use-cross: true use-cross: true
command: build command: build
args: --release --target aarch64-unknown-linux-gnu args: --release --target aarch64-unknown-linux-gnu
- name: Upload - name: Upload
uses: actions/upload-artifact@v1 uses: actions/upload-artifact@v1
with: with:
name: linux_aarch64-zecwallet-cli name: linux_aarch64-zecwallet-cli
path: target/aarch64-unknown-linux-gnu/release/zecwallet-cli path: target/aarch64-unknown-linux-gnu/release/zecwallet-cli

14
.gitignore vendored
View File

@@ -1,7 +1,7 @@
target/ target/
.vscode/ .vscode/
history.txt history.txt
/.idea/ /.idea/
tarpaulin-report.html tarpaulin-report.html
/log* /log*
silentdragonlite-cli silentdragonlite-cli

5766
Cargo.lock generated

File diff suppressed because it is too large Load Diff

View File

@@ -1,8 +1,8 @@
[workspace] [workspace]
members = [ members = [
"lib", "lib",
"cli", "cli",
] ]
[profile.release] [profile.release]
debug = false debug = false

View File

@@ -1,23 +1,23 @@
.PHONY: format help .PHONY: format help
# Help system from https://marmelab.com/blog/2016/02/29/auto-documented-makefile.html # Help system from https://marmelab.com/blog/2016/02/29/auto-documented-makefile.html
.DEFAULT_GOAL := help .DEFAULT_GOAL := help
# Copyright (c) 2019-2024 Jahway603 & The Hush Developers # Copyright (c) 2019-2024 Jahway603 & The Hush Developers
# Released under the GPLv3 # Released under the GPLv3
# #
# DragonX Silentdragonlite-cli Makefile # DragonX Silentdragonlite-cli Makefile
PROJECT_NAME := "silentdragonxlite-cli" PROJECT_NAME := "silentdragonxlite-cli"
help: help:
@grep -E '^[a-zA-Z0-9_-]+:.*?## .*$$' $(MAKEFILE_LIST) | sort | awk 'BEGIN {FS = ":.*?## "}; {printf "\033[36m%-20s\033[0m %s\n", $$1, $$2}' @grep -E '^[a-zA-Z0-9_-]+:.*?## .*$$' $(MAKEFILE_LIST) | sort | awk 'BEGIN {FS = ":.*?## "}; {printf "\033[36m%-20s\033[0m %s\n", $$1, $$2}'
about: ## Display release info about: ## Display release info
printf "DragonX Silentdragonlite-cli Makefile by jahway603\n" printf "DragonX Silentdragonlite-cli Makefile by jahway603\n"
build: ## Build the release build: ## Build the release
./util/build.sh ./util/build.sh
cp `pwd`/target/release/$(PROJECT_NAME) . cp `pwd`/target/release/$(PROJECT_NAME) .
printf "DragonX silentdragonxlite-cli is ready for you\n" printf "DragonX silentdragonxlite-cli is ready for you\n"
clean: ## Clean the repo clean: ## Clean the repo
cargo clean cargo clean
rm $(PROJECT_NAME) rm $(PROJECT_NAME)

200
README.md
View File

@@ -1,100 +1,100 @@
# SilentDragonXLite CLI # SilentDragonXLite CLI
`silentdragonxlite-cli` is a command line SilentDragonXLite light client. To use it, download the latest binary from the releases page and run `./silentdragonxlite-cli` or compile it yourself as documented below. `silentdragonxlite-cli` is a command line SilentDragonXLite light client. To use it, download the latest binary from the releases page and run `./silentdragonxlite-cli` or compile it yourself as documented below.
This will launch the interactive prompt. Type `help` to get a list of commands This will launch the interactive prompt. Type `help` to get a list of commands
## Running in non-interactive mode: ## Running in non-interactive mode:
You can also run `silentdragonxlite-cli` in non-interactive mode by passing the command you want to run as an argument. For example, `silentdragonxlite-cli addresses` will list all wallet addresses and exit. You can also run `silentdragonxlite-cli` in non-interactive mode by passing the command you want to run as an argument. For example, `silentdragonxlite-cli addresses` will list all wallet addresses and exit.
Run `silentdragonxlite-cli help` to see a list of all commands. Run `silentdragonxlite-cli help` to see a list of all commands.
## Privacy ## Privacy
* While all the keys and transaction detection happens on the client, the server can learn what blocks contain your shielded transactions. * While all the keys and transaction detection happens on the client, the server can learn what blocks contain your shielded transactions.
* The server also learns other metadata about you like your ip address etc... * The server also learns other metadata about you like your ip address etc...
* Also remember that t-addresses don't provide any privacy protection. * Also remember that t-addresses don't provide any privacy protection.
## Notes: ## Notes:
* If you want to run your own server, please see [SilentDragonXLite-cli lightwalletd](https://git.hush.is/hush/lightwalletd), and then run `./silentdragonxlite-cli --server http://127.0.0.1:9067`. You might also need to pass `--dangerous` if you are using a self-signed TLS certificate. * If you want to run your own server, please see [SilentDragonXLite-cli lightwalletd](https://git.hush.is/hush/lightwalletd), and then run `./silentdragonxlite-cli --server http://127.0.0.1:9067`. You might also need to pass `--dangerous` if you are using a self-signed TLS certificate.
* The log file is in `~/.silentdragonxlite/silentdragonxlite-cli.debug.log`. Wallet is stored in `~/.silentdragonxlite/silentdragonxlite-cli.dat` * The log file is in `~/.silentdragonxlite/silentdragonxlite-cli.debug.log`. Wallet is stored in `~/.silentdragonxlite/silentdragonxlite-cli.dat`
### Note Management ### Note Management
silentdragonxlite does automatic note and utxo management, which means it doesn't allow you to manually select which address to send outgoing transactions from. It follows these principles: silentdragonxlite does automatic note and utxo management, which means it doesn't allow you to manually select which address to send outgoing transactions from. It follows these principles:
* Defaults to sending shielded transactions, even if you're sending to a transparent address * Defaults to sending shielded transactions, even if you're sending to a transparent address
* Sapling funds need at least 2 confirmations before they can be spent * Sapling funds need at least 2 confirmations before they can be spent
* Can select funds from multiple shielded addresses in the same transaction * Can select funds from multiple shielded addresses in the same transaction
* Will automatically shield your transparent funds at the first opportunity * Will automatically shield your transparent funds at the first opportunity
* When sending an outgoing transaction to a shielded address, silentdragonxlite can decide to use the transaction to additionally shield your transparent funds (i.e., send your transparent funds to your own shielded address in the same transaction) * When sending an outgoing transaction to a shielded address, silentdragonxlite can decide to use the transaction to additionally shield your transparent funds (i.e., send your transparent funds to your own shielded address in the same transaction)
## Compiling from source ## Compiling from source
#### Pre-requisites #### Pre-requisites
* You need Rust and how you install it will depend on your version of Linux. Below are well known rust versions tested on common Linux distributions. * You need Rust and how you install it will depend on your version of Linux. Below are well known rust versions tested on common Linux distributions.
| Linux Version | Rust Version Tested | Command to install | | Linux Version | Rust Version Tested | Command to install |
|---------------|--------|---------------------------| |---------------|--------|---------------------------|
| Ubuntu 18.04 | 1.47.0 | [USE RUSTUP](https://www.rust-lang.org/tools/install) | | Ubuntu 18.04 | 1.47.0 | [USE RUSTUP](https://www.rust-lang.org/tools/install) |
| Ubuntu 20.04 | 1.57.0 | sudo apt install rust-all | | Ubuntu 20.04 | 1.57.0 | sudo apt install rust-all |
| Debian 11 | 1.50.0 | [USE RUSTUP](https://www.rust-lang.org/tools/install) | | Debian 11 | 1.50.0 | [USE RUSTUP](https://www.rust-lang.org/tools/install) |
| Arch Linux | 1.56.0 | pacman -S rustc cargo | | Arch Linux | 1.56.0 | pacman -S rustc cargo |
* Debian 11 comes with a much older rust compiler (1.48.0) and so you want to use rustup with Debian and install at least 1.50.0. * Debian 11 comes with a much older rust compiler (1.48.0) and so you want to use rustup with Debian and install at least 1.50.0.
* If you're using another Linux distro, then consult their package manager for rustc and cargo, but if it's tool old then you want to [use Rustup](https://www.rust-lang.org/tools/install) to install at least 1.50.0. * If you're using another Linux distro, then consult their package manager for rustc and cargo, but if it's tool old then you want to [use Rustup](https://www.rust-lang.org/tools/install) to install at least 1.50.0.
* The build will fail if you do not have `rustfmt` binary, which is included when you use `rustup` but may not be included in via operating system packages. Using `rustup` is recommended * The build will fail if you do not have `rustfmt` binary, which is included when you use `rustup` but may not be included in via operating system packages. Using `rustup` is recommended
To securely install rustup by compiling it yourself: To securely install rustup by compiling it yourself:
``` ```
git clone https://github.com/rust-lang/rustup git clone https://github.com/rust-lang/rustup
cd rustup cd rustup
cargo run --release cargo run --release
``` ```
The above avoids piping the output of curl to bash (bad idea) and avoids using binaries. It will take a few minutes longer but is the better solution. The above avoids piping the output of curl to bash (bad idea) and avoids using binaries. It will take a few minutes longer but is the better solution.
#### The compilation #### The compilation
Run the following commands to compile on your computer. Run the following commands to compile on your computer.
```shell script ```shell script
git clone https://git.hush.is/dragonx/silentdragonxlite-cli git clone https://git.hush.is/dragonx/silentdragonxlite-cli
cd silentdragonxlite-cli cd silentdragonxlite-cli
cargo build --release cargo build --release
./target/release/silentdragonxlite-cli ./target/release/silentdragonxlite-cli
``` ```
#### Or build with make #### Or build with make
Alternatively, you can use the new makefile to build Alternatively, you can use the new makefile to build
```shell script ```shell script
make help make help
make build make build
``` ```
## Options ## Options
Here are some CLI arguments you can pass to `silentdragonxlite-cli`. Please run `silentdragonxlite-cli --help` for the full list. Here are some CLI arguments you can pass to `silentdragonxlite-cli`. Please run `silentdragonxlite-cli --help` for the full list.
* `--server`: Connect to a custom SilentDragonXLite lightwalletd server. * `--server`: Connect to a custom SilentDragonXLite lightwalletd server.
* Example: `./silentdragonxlite-cli --server 127.0.0.1:9067` * Example: `./silentdragonxlite-cli --server 127.0.0.1:9067`
* Example: `./silentdragonxlite-cli --server lite.dragonx.is` * Example: `./silentdragonxlite-cli --server lite.dragonx.is`
* `--seed`: Restore a wallet from a seed phrase. **Note** that this will fail if there is an existing wallet. Delete (or move) any existing wallet to restore from the 24-word seed phrase * `--seed`: Restore a wallet from a seed phrase. **Note** that this will fail if there is an existing wallet. Delete (or move) any existing wallet to restore from the 24-word seed phrase
* Example: `./silentdragonxlite-cli --seed "twenty four words seed phrase"` * Example: `./silentdragonxlite-cli --seed "twenty four words seed phrase"`
* `--recover`: Attempt to recover the seed phrase from a corrupted wallet * `--recover`: Attempt to recover the seed phrase from a corrupted wallet
* `-n, --nosync`: By default, silentdragonxlite-cli will sync the wallet at startup, so use this option to prevent the automatic sync at startup * `-n, --nosync`: By default, silentdragonxlite-cli will sync the wallet at startup, so use this option to prevent the automatic sync at startup
### Support ### Support
For support or other questions, join us on [Telegram](https://dragonx.is/tg) or [file an issue](https://git.hush.is/dragonx/silentdragonxlite-cli/issues). For support or other questions, join us on [Telegram](https://dragonx.is/tg) or [file an issue](https://git.hush.is/dragonx/silentdragonxlite-cli/issues).
## Copyright ## Copyright
Copyright The Hush Developers 2019-2024 Copyright The Hush Developers 2019-2024
## License ## License
GPLv3 or later GPLv3 or later

View File

@@ -1,18 +1,18 @@
[package] [package]
name = "silentdragonxlite-cli" name = "silentdragonxlite-cli"
version = "1.0.0" version = "1.0.0"
edition = "2018" edition = "2018"
[dependencies] [dependencies]
rustyline = "5.0.2" rustyline = "5.0.2"
clap = "2.33" clap = "2.33"
log = "0.4" log = "0.4"
log4rs = "0.8.3" log4rs = "0.8.3"
shellwords = "1.0.0" shellwords = "1.0.0"
json = "0.12.0" json = "0.12.0"
http = "0.2" http = "0.2"
byteorder = "1" byteorder = "1"
tiny-bip39 = "0.6.2" tiny-bip39 = "0.6.2"
silentdragonxlitelib = { path = "../lib/" } silentdragonxlitelib = { path = "../lib/" }

View File

@@ -1,262 +1,262 @@
use std::io::{self}; use std::io::{self};
use std::sync::Arc; use std::sync::Arc;
use std::sync::mpsc::{channel, Sender, Receiver}; use std::sync::mpsc::{channel, Sender, Receiver};
use log::{info, error}; use log::{info, error};
use silentdragonxlitelib::{commands, use silentdragonxlitelib::{commands,
lightclient::{LightClient, LightClientConfig}, lightclient::{LightClient, LightClientConfig},
}; };
#[macro_export] #[macro_export]
macro_rules! configure_clapapp { macro_rules! configure_clapapp {
( $freshapp: expr ) => { ( $freshapp: expr ) => {
$freshapp.version("1.0.0") $freshapp.version("1.0.0")
.arg(Arg::with_name("dangerous") .arg(Arg::with_name("dangerous")
.long("dangerous") .long("dangerous")
.help("Disable server TLS certificate verification. Use this if you're running a local lightwalletd with a self-signed certificate. WARNING: This is dangerous, don't use it with a server that is not your own.") .help("Disable server TLS certificate verification. Use this if you're running a local lightwalletd with a self-signed certificate. WARNING: This is dangerous, don't use it with a server that is not your own.")
.takes_value(false)) .takes_value(false))
.arg(Arg::with_name("nosync") .arg(Arg::with_name("nosync")
.help("By default, Silentdragonlite-cli will sync the wallet at startup. Pass --nosync to prevent the automatic sync at startup.") .help("By default, Silentdragonlite-cli will sync the wallet at startup. Pass --nosync to prevent the automatic sync at startup.")
.long("nosync") .long("nosync")
.short("n") .short("n")
.takes_value(false)) .takes_value(false))
.arg(Arg::with_name("recover") .arg(Arg::with_name("recover")
.long("recover") .long("recover")
.help("Attempt to recover the seed from the wallet") .help("Attempt to recover the seed from the wallet")
.takes_value(false)) .takes_value(false))
.arg(Arg::with_name("password") .arg(Arg::with_name("password")
.long("password") .long("password")
.help("When recovering seed, specify a password for the encrypted wallet") .help("When recovering seed, specify a password for the encrypted wallet")
.takes_value(true)) .takes_value(true))
.arg(Arg::with_name("seed") .arg(Arg::with_name("seed")
.short("s") .short("s")
.long("seed") .long("seed")
.value_name("seed_phrase") .value_name("seed_phrase")
.help("Create a new wallet with the given 24-word seed phrase. Will fail if wallet already exists") .help("Create a new wallet with the given 24-word seed phrase. Will fail if wallet already exists")
.takes_value(true)) .takes_value(true))
.arg(Arg::with_name("birthday") .arg(Arg::with_name("birthday")
.long("birthday") .long("birthday")
.value_name("birthday") .value_name("birthday")
.help("Specify wallet birthday when restoring from seed. This is the earlist block height where the wallet has a transaction.") .help("Specify wallet birthday when restoring from seed. This is the earlist block height where the wallet has a transaction.")
.takes_value(true)) .takes_value(true))
.arg(Arg::with_name("server") .arg(Arg::with_name("server")
.long("server") .long("server")
.value_name("server") .value_name("server")
.help("Lightwalletd server to connect to.") .help("Lightwalletd server to connect to.")
.takes_value(true) .takes_value(true)
.default_value(lightclient::DEFAULT_SERVER)) .default_value(lightclient::DEFAULT_SERVER))
.arg(Arg::with_name("COMMAND") .arg(Arg::with_name("COMMAND")
.help("Command to execute. If a command is not specified, Silentdragonlite-cli will start in interactive mode.") .help("Command to execute. If a command is not specified, Silentdragonlite-cli will start in interactive mode.")
.required(false) .required(false)
.index(1)) .index(1))
.arg(Arg::with_name("PARAMS") .arg(Arg::with_name("PARAMS")
.help("Params to execute command with. Run the 'help' command to get usage help.") .help("Params to execute command with. Run the 'help' command to get usage help.")
.required(false) .required(false)
.multiple(true)) .multiple(true))
}; };
} }
/// This function is only tested against Linux. /// This function is only tested against Linux.
pub fn report_permission_error() { pub fn report_permission_error() {
let user = std::env::var("USER").expect( let user = std::env::var("USER").expect(
"Unexpected error reading value of $USER!"); "Unexpected error reading value of $USER!");
let home = std::env::var("HOME").expect( let home = std::env::var("HOME").expect(
"Unexpected error reading value of $HOME!"); "Unexpected error reading value of $HOME!");
let current_executable = std::env::current_exe() let current_executable = std::env::current_exe()
.expect("Unexpected error reporting executable path!"); .expect("Unexpected error reporting executable path!");
eprintln!("USER: {}", user); eprintln!("USER: {}", user);
eprintln!("HOME: {}", home); eprintln!("HOME: {}", home);
eprintln!("Executable: {}", current_executable.display()); eprintln!("Executable: {}", current_executable.display());
if home == "/" { if home == "/" {
eprintln!("User {} must have permission to write to '{}.silentdragonxlite/' .", eprintln!("User {} must have permission to write to '{}.silentdragonxlite/' .",
user, user,
home); home);
} else { } else {
eprintln!("User {} must have permission to write to '{}/.silentdragonxlite/' .", eprintln!("User {} must have permission to write to '{}/.silentdragonxlite/' .",
user, user,
home); home);
} }
} }
pub fn startup(server: http::Uri, dangerous: bool, seed: Option<String>, birthday: u64, first_sync: bool, print_updates: bool) pub fn startup(server: http::Uri, dangerous: bool, seed: Option<String>, birthday: u64, first_sync: bool, print_updates: bool)
-> io::Result<(Sender<(String, Vec<String>)>, Receiver<String>)> { -> io::Result<(Sender<(String, Vec<String>)>, Receiver<String>)> {
// Try to get the configuration // Try to get the configuration
let (config, latest_block_height) = LightClientConfig::create(server.clone(), dangerous)?; let (config, latest_block_height) = LightClientConfig::create(server.clone(), dangerous)?;
let lightclient = match seed { let lightclient = match seed {
Some(phrase) => Arc::new(LightClient::new_from_phrase(phrase, &config, birthday,0, false)?), Some(phrase) => Arc::new(LightClient::new_from_phrase(phrase, &config, birthday,0, false)?),
None => { None => {
if config.wallet_exists() { if config.wallet_exists() {
Arc::new(LightClient::read_from_disk(&config)?) Arc::new(LightClient::read_from_disk(&config)?)
} else { } else {
println!("Creating a new wallet"); println!("Creating a new wallet");
Arc::new(LightClient::new(&config, latest_block_height)?) Arc::new(LightClient::new(&config, latest_block_height)?)
} }
} }
}; };
// Initialize logging // Initialize logging
lightclient.init_logging()?; lightclient.init_logging()?;
// Print startup Messages // Print startup Messages
info!(""); // Blank line info!(""); // Blank line
info!("Starting Silentdragonlite-CLI"); info!("Starting Silentdragonlite-CLI");
info!("Light Client config {:?}", config); info!("Light Client config {:?}", config);
if print_updates { if print_updates {
println!("Lightclient connecting to {}", config.server); println!("Lightclient connecting to {}", config.server);
} }
// At startup, run a sync. // At startup, run a sync.
if first_sync { if first_sync {
let update = lightclient.do_sync(true); let update = lightclient.do_sync(true);
if print_updates { if print_updates {
match update { match update {
Ok(j) => { Ok(j) => {
println!("{}", j.pretty(2)); println!("{}", j.pretty(2));
}, },
Err(e) => println!("{}", e) Err(e) => println!("{}", e)
} }
} }
} }
// Start the command loop // Start the command loop
let (command_tx, resp_rx) = command_loop(lightclient.clone()); let (command_tx, resp_rx) = command_loop(lightclient.clone());
Ok((command_tx, resp_rx)) Ok((command_tx, resp_rx))
} }
pub fn start_interactive(command_tx: Sender<(String, Vec<String>)>, resp_rx: Receiver<String>) { pub fn start_interactive(command_tx: Sender<(String, Vec<String>)>, resp_rx: Receiver<String>) {
// `()` can be used when no completer is required // `()` can be used when no completer is required
let mut rl = rustyline::Editor::<()>::new(); let mut rl = rustyline::Editor::<()>::new();
println!("Ready!"); println!("Ready!");
let send_command = |cmd: String, args: Vec<String>| -> String { let send_command = |cmd: String, args: Vec<String>| -> String {
command_tx.send((cmd.clone(), args)).unwrap(); command_tx.send((cmd.clone(), args)).unwrap();
match resp_rx.recv() { match resp_rx.recv() {
Ok(s) => s, Ok(s) => s,
Err(e) => { Err(e) => {
let e = format!("Error executing command {}: {}", cmd, e); let e = format!("Error executing command {}: {}", cmd, e);
eprintln!("{}", e); eprintln!("{}", e);
error!("{}", e); error!("{}", e);
return "".to_string() return "".to_string()
} }
} }
}; };
let info = send_command("info".to_string(), vec![]); let info = send_command("info".to_string(), vec![]);
let chain_name = json::parse(&info).unwrap()["chain_name"].as_str().unwrap().to_string(); let chain_name = json::parse(&info).unwrap()["chain_name"].as_str().unwrap().to_string();
loop { loop {
// Read the height first // Read the height first
let height = json::parse(&send_command("height".to_string(), vec!["false".to_string()])).unwrap()["height"].as_i64().unwrap(); let height = json::parse(&send_command("height".to_string(), vec!["false".to_string()])).unwrap()["height"].as_i64().unwrap();
let readline = rl.readline(&format!("({}) Block:{} (type 'help') >> ", let readline = rl.readline(&format!("({}) Block:{} (type 'help') >> ",
chain_name, height)); chain_name, height));
match readline { match readline {
Ok(line) => { Ok(line) => {
rl.add_history_entry(line.as_str()); rl.add_history_entry(line.as_str());
// Parse command line arguments // Parse command line arguments
let mut cmd_args = match shellwords::split(&line) { let mut cmd_args = match shellwords::split(&line) {
Ok(args) => args, Ok(args) => args,
Err(_) => { Err(_) => {
println!("Mismatched Quotes"); println!("Mismatched Quotes");
continue; continue;
} }
}; };
if cmd_args.is_empty() { if cmd_args.is_empty() {
continue; continue;
} }
let cmd = cmd_args.remove(0); let cmd = cmd_args.remove(0);
let args: Vec<String> = cmd_args; let args: Vec<String> = cmd_args;
println!("{}", send_command(cmd, args)); println!("{}", send_command(cmd, args));
// Special check for Quit command. // Special check for Quit command.
if line == "quit" { if line == "quit" {
break; break;
} }
}, },
Err(rustyline::error::ReadlineError::Interrupted) => { Err(rustyline::error::ReadlineError::Interrupted) => {
println!("CTRL-C"); println!("CTRL-C");
info!("CTRL-C"); info!("CTRL-C");
println!("{}", send_command("save".to_string(), vec![])); println!("{}", send_command("save".to_string(), vec![]));
break break
}, },
Err(rustyline::error::ReadlineError::Eof) => { Err(rustyline::error::ReadlineError::Eof) => {
println!("CTRL-D"); println!("CTRL-D");
info!("CTRL-D"); info!("CTRL-D");
println!("{}", send_command("save".to_string(), vec![])); println!("{}", send_command("save".to_string(), vec![]));
break break
}, },
Err(err) => { Err(err) => {
println!("Error: {:?}", err); println!("Error: {:?}", err);
break break
} }
} }
} }
} }
pub fn command_loop(lightclient: Arc<LightClient>) -> (Sender<(String, Vec<String>)>, Receiver<String>) { pub fn command_loop(lightclient: Arc<LightClient>) -> (Sender<(String, Vec<String>)>, Receiver<String>) {
let (command_tx, command_rx) = channel::<(String, Vec<String>)>(); let (command_tx, command_rx) = channel::<(String, Vec<String>)>();
let (resp_tx, resp_rx) = channel::<String>(); let (resp_tx, resp_rx) = channel::<String>();
let lc = lightclient.clone(); let lc = lightclient.clone();
std::thread::spawn(move || { std::thread::spawn(move || {
//start mempool_monitor //start mempool_monitor
match LightClient::start_mempool_monitor(lc.clone()) { match LightClient::start_mempool_monitor(lc.clone()) {
Ok(_) => {}, Ok(_) => {},
Err(e) => { Err(e) => {
error!("Error starting mempool: {:?}", e); error!("Error starting mempool: {:?}", e);
} }
} }
loop { loop {
match command_rx.recv_timeout(std::time::Duration::from_secs(5 * 60)) { match command_rx.recv_timeout(std::time::Duration::from_secs(5 * 60)) {
Ok((cmd, args)) => { Ok((cmd, args)) => {
let args = args.iter().map(|s| s.as_ref()).collect(); let args = args.iter().map(|s| s.as_ref()).collect();
let cmd_response = commands::do_user_command(&cmd, &args, lc.as_ref()); let cmd_response = commands::do_user_command(&cmd, &args, lc.as_ref());
resp_tx.send(cmd_response).unwrap(); resp_tx.send(cmd_response).unwrap();
if cmd == "quit" { if cmd == "quit" {
info!("Quit"); info!("Quit");
break; break;
} }
}, },
Err(_) => { Err(_) => {
// Timeout. Do a sync to keep the wallet up-to-date. False to whether to print updates on the console // Timeout. Do a sync to keep the wallet up-to-date. False to whether to print updates on the console
info!("Timeout, doing a sync"); info!("Timeout, doing a sync");
match lc.do_sync(false) { match lc.do_sync(false) {
Ok(_) => {}, Ok(_) => {},
Err(e) => {error!("{}", e)} Err(e) => {error!("{}", e)}
} }
} }
} }
} }
}); });
(command_tx, resp_rx) (command_tx, resp_rx)
} }
pub fn attempt_recover_seed(password: Option<String>) { pub fn attempt_recover_seed(password: Option<String>) {
// Create a Light Client Config in an attempt to recover the file. // Create a Light Client Config in an attempt to recover the file.
let config = LightClientConfig { let config = LightClientConfig {
server: "0.0.0.0:0".parse().unwrap(), server: "0.0.0.0:0".parse().unwrap(),
chain_name: "main".to_string(), chain_name: "main".to_string(),
sapling_activation_height: 0, sapling_activation_height: 0,
consensus_branch_id: "000000".to_string(), consensus_branch_id: "000000".to_string(),
anchor_offset: 0, anchor_offset: 0,
no_cert_verification: false, no_cert_verification: false,
data_dir: None, data_dir: None,
}; };
match LightClient::attempt_recover_seed(&config, password) { match LightClient::attempt_recover_seed(&config, password) {
Ok(_seed) => println!("Recovered seed "), Ok(_seed) => println!("Recovered seed "),
Err(e) => eprintln!("Failed to recover seed. Error: {}", e) Err(e) => eprintln!("Failed to recover seed. Error: {}", e)
}; };
} }

View File

@@ -1,93 +1,93 @@
use silentdragonxlitelib::lightclient::{self, LightClientConfig}; use silentdragonxlitelib::lightclient::{self, LightClientConfig};
use silentdragonxlite_cli::{configure_clapapp, use silentdragonxlite_cli::{configure_clapapp,
report_permission_error, report_permission_error,
startup, startup,
start_interactive, start_interactive,
attempt_recover_seed}; attempt_recover_seed};
//version::VERSION //version::VERSION
use log::error; use log::error;
pub fn main() { pub fn main() {
// Get command line arguments // Get command line arguments
use clap::{App, Arg}; use clap::{App, Arg};
let fresh_app = App::new("SilentDragonXLite CLI"); let fresh_app = App::new("SilentDragonXLite CLI");
let configured_app = configure_clapapp!(fresh_app); let configured_app = configure_clapapp!(fresh_app);
let matches = configured_app.get_matches(); let matches = configured_app.get_matches();
if matches.is_present("recover") { if matches.is_present("recover") {
// Create a Light Client Config in an attempt to recover the file. // Create a Light Client Config in an attempt to recover the file.
attempt_recover_seed(matches.value_of("password").map(|s| s.to_string())); attempt_recover_seed(matches.value_of("password").map(|s| s.to_string()));
return; return;
} }
let command = matches.value_of("COMMAND"); let command = matches.value_of("COMMAND");
let params = matches.values_of("PARAMS").map(|v| v.collect()).or(Some(vec![])).unwrap(); let params = matches.values_of("PARAMS").map(|v| v.collect()).or(Some(vec![])).unwrap();
let maybe_server = matches.value_of("server").map(|s| s.to_string()); let maybe_server = matches.value_of("server").map(|s| s.to_string());
let seed = matches.value_of("seed").map(|s| s.to_string()); let seed = matches.value_of("seed").map(|s| s.to_string());
let maybe_birthday = matches.value_of("birthday"); let maybe_birthday = matches.value_of("birthday");
if seed.is_some() && maybe_birthday.is_none() { if seed.is_some() && maybe_birthday.is_none() {
eprintln!("ERROR!"); eprintln!("ERROR!");
eprintln!("Please specify the wallet birthday (eg. '--birthday 600000') to restore from seed."); eprintln!("Please specify the wallet birthday (eg. '--birthday 600000') to restore from seed.");
eprintln!("This should be the block height where the wallet was created. If you don't remember the block height, you can pass '--birthday 0' to scan from the start of the blockchain."); eprintln!("This should be the block height where the wallet was created. If you don't remember the block height, you can pass '--birthday 0' to scan from the start of the blockchain.");
return; return;
} }
let birthday = match maybe_birthday.unwrap_or("0").parse::<u64>() { let birthday = match maybe_birthday.unwrap_or("0").parse::<u64>() {
Ok(b) => b, Ok(b) => b,
Err(e) => { Err(e) => {
eprintln!("Couldn't parse birthday. This should be a block number. Error={}", e); eprintln!("Couldn't parse birthday. This should be a block number. Error={}", e);
return; return;
} }
}; };
let server = LightClientConfig::get_server_or_default(maybe_server); let server = LightClientConfig::get_server_or_default(maybe_server);
// Test to make sure the server has all of scheme, host and port // Test to make sure the server has all of scheme, host and port
if server.scheme_str().is_none() || server.host().is_none() || server.port().is_none() { if server.scheme_str().is_none() || server.host().is_none() || server.port().is_none() {
eprintln!("Please provide the --server parameter as [scheme]://[host]:[port].\nYou provided: {}", server); eprintln!("Please provide the --server parameter as [scheme]://[host]:[port].\nYou provided: {}", server);
return; return;
} }
let dangerous = matches.is_present("dangerous"); let dangerous = matches.is_present("dangerous");
let nosync = matches.is_present("nosync"); let nosync = matches.is_present("nosync");
let (command_tx, resp_rx) = match startup(server, dangerous, seed, birthday, !nosync, command.is_none()) { let (command_tx, resp_rx) = match startup(server, dangerous, seed, birthday, !nosync, command.is_none()) {
Ok(c) => c, Ok(c) => c,
Err(e) => { Err(e) => {
let emsg = format!("Error during startup:{}\nIf you repeatedly run into this issue, you might have to restore your wallet from your seed phrase.", e); let emsg = format!("Error during startup:{}\nIf you repeatedly run into this issue, you might have to restore your wallet from your seed phrase.", e);
eprintln!("{}", emsg); eprintln!("{}", emsg);
error!("{}", emsg); error!("{}", emsg);
if cfg!(target_os = "unix" ) { if cfg!(target_os = "unix" ) {
match e.raw_os_error() { match e.raw_os_error() {
Some(13) => report_permission_error(), Some(13) => report_permission_error(),
_ => {}, _ => {},
} }
}; };
return; return;
} }
}; };
if command.is_none() { if command.is_none() {
start_interactive(command_tx, resp_rx); start_interactive(command_tx, resp_rx);
} else { } else {
command_tx.send( command_tx.send(
(command.unwrap().to_string(), (command.unwrap().to_string(),
params.iter().map(|s| s.to_string()).collect::<Vec<String>>())) params.iter().map(|s| s.to_string()).collect::<Vec<String>>()))
.unwrap(); .unwrap();
match resp_rx.recv() { match resp_rx.recv() {
Ok(s) => println!("{}", s), Ok(s) => println!("{}", s),
Err(e) => { Err(e) => {
let e = format!("Error executing command {}: {}", command.unwrap(), e); let e = format!("Error executing command {}: {}", command.unwrap(), e);
eprintln!("{}", e); eprintln!("{}", e);
error!("{}", e); error!("{}", e);
} }
} }
// Save before exit // Save before exit
command_tx.send(("save".to_string(), vec![])).unwrap(); command_tx.send(("save".to_string(), vec![])).unwrap();
resp_rx.recv().unwrap(); resp_rx.recv().unwrap();
} }
} }

View File

@@ -1 +1 @@
pub const VERSION:&str = "1.1.2"; pub const VERSION:&str = "1.1.2";

View File

@@ -1,43 +1,43 @@
FROM ubuntu:16.04 FROM ubuntu:16.04
LABEL Description="Rust compile env for Linux + Windows (cross)" LABEL Description="Rust compile env for Linux + Windows (cross)"
RUN apt update RUN apt update
RUN apt install -y build-essential mingw-w64 gcc-aarch64-linux-gnu gcc-arm-linux-gnueabihf curl vim wget RUN apt install -y build-essential mingw-w64 gcc-aarch64-linux-gnu gcc-arm-linux-gnueabihf curl vim wget
# Get Rust # Get Rust
RUN curl https://sh.rustup.rs -sSf | bash -s -- -y RUN curl https://sh.rustup.rs -sSf | bash -s -- -y
ENV PATH="/root/.cargo/bin:${PATH}" ENV PATH="/root/.cargo/bin:${PATH}"
RUN rustup target add x86_64-pc-windows-gnu RUN rustup target add x86_64-pc-windows-gnu
RUN rustup target add aarch64-unknown-linux-gnu RUN rustup target add aarch64-unknown-linux-gnu
RUN rustup target add armv7-unknown-linux-gnueabihf RUN rustup target add armv7-unknown-linux-gnueabihf
# Append the linker to the cargo config for Windows cross compile # Append the linker to the cargo config for Windows cross compile
RUN echo "[target.x86_64-pc-windows-gnu]" >> /root/.cargo/config && \ RUN echo "[target.x86_64-pc-windows-gnu]" >> /root/.cargo/config && \
echo "linker = '/usr/bin/x86_64-w64-mingw32-gcc'" >> /root/.cargo/config echo "linker = '/usr/bin/x86_64-w64-mingw32-gcc'" >> /root/.cargo/config
RUN echo "[target.aarch64-unknown-linux-gnu]" >> /root/.cargo/config && \ RUN echo "[target.aarch64-unknown-linux-gnu]" >> /root/.cargo/config && \
echo "linker = '/usr/bin/aarch64-linux-gnu-gcc'" >> /root/.cargo/config echo "linker = '/usr/bin/aarch64-linux-gnu-gcc'" >> /root/.cargo/config
RUN echo "[target.armv7-unknown-linux-gnueabihf]" >> /root/.cargo/config && \ RUN echo "[target.armv7-unknown-linux-gnueabihf]" >> /root/.cargo/config && \
echo "linker = '/usr/bin/arm-linux-gnueabihf-gcc'" >> /root/.cargo/config echo "linker = '/usr/bin/arm-linux-gnueabihf-gcc'" >> /root/.cargo/config
ENV CC_x86_64_unknown_linux_musl="gcc" ENV CC_x86_64_unknown_linux_musl="gcc"
ENV CC_aarch64_unknown_linux_gnu="aarch64-linux-gnu-gcc" ENV CC_aarch64_unknown_linux_gnu="aarch64-linux-gnu-gcc"
ENV CC_armv7_unknown_linux_gnueabhihf="arm-linux-gnueabihf-gcc" ENV CC_armv7_unknown_linux_gnueabhihf="arm-linux-gnueabihf-gcc"
# This is a bug fix for the windows cross compiler for Rust. # This is a bug fix for the windows cross compiler for Rust.
RUN cp /usr/x86_64-w64-mingw32/lib/crt2.o /root/.rustup/toolchains/stable-x86_64-unknown-linux-gnu/lib/rustlib/x86_64-pc-windows-gnu/lib/crt2.o RUN cp /usr/x86_64-w64-mingw32/lib/crt2.o /root/.rustup/toolchains/stable-x86_64-unknown-linux-gnu/lib/rustlib/x86_64-pc-windows-gnu/lib/crt2.o
# For windows cross compilation, use a pre-build binary. Remember to set the # For windows cross compilation, use a pre-build binary. Remember to set the
# SODIUM_LIB_DIR for windows cross compilation # SODIUM_LIB_DIR for windows cross compilation
RUN cd /opt && wget https://download.libsodium.org/libsodium/releases/libsodium-1.0.17-mingw.tar.gz && \ RUN cd /opt && wget https://download.libsodium.org/libsodium/releases/libsodium-1.0.17-mingw.tar.gz && \
tar xvf libsodium-1.0.17-mingw.tar.gz tar xvf libsodium-1.0.17-mingw.tar.gz
RUN apt install -y git RUN apt install -y git
# Cargo fetch the dependencies so we don't download them over and over again # Cargo fetch the dependencies so we don't download them over and over again
RUN cd /tmp && git clone https://github.com/adityapk00/silentdragonlite-light-cli.git && \ RUN cd /tmp && git clone https://github.com/adityapk00/silentdragonlite-light-cli.git && \
cd silentdragonlite-light-cli && \ cd silentdragonlite-light-cli && \
cargo fetch && \ cargo fetch && \
cd /tmp && rm -rf silentdragonlite-light-cli cd /tmp && rm -rf silentdragonlite-light-cli

View File

@@ -1,80 +1,80 @@
[package] [package]
name = "silentdragonxlitelib" name = "silentdragonxlitelib"
version = "0.1.0" version = "0.1.0"
edition = "2018" edition = "2018"
[features] [features]
default = ["embed_params"] default = ["embed_params"]
embed_params = [] embed_params = []
[dependencies] [dependencies]
base58 = "0.1.0" base58 = "0.1.0"
bs58 = { version = "0.2", features = ["check"] } bs58 = { version = "0.2", features = ["check"] }
log = "0.4" log = "0.4"
log4rs = "0.8.3" log4rs = "0.8.3"
dirs = "2.0.2" dirs = "2.0.2"
http = "0.2" http = "0.2"
hex = "0.3" hex = "0.3"
protobuf = "2" protobuf = "2"
byteorder = "1" byteorder = "1"
json = "0.12.0" json = "0.12.0"
tiny-bip39 = "0.6.2" tiny-bip39 = "0.6.2"
secp256k1 = "=0.15.0" secp256k1 = "=0.15.0"
sha2 = "0.8.0" sha2 = "0.8.0"
ripemd160 = "0.8.0" ripemd160 = "0.8.0"
lazy_static = "1.2.0" lazy_static = "1.2.0"
rust-embed = { version = "5.1.0", features = ["debug-embed"] } rust-embed = { version = "5.1.0", features = ["debug-embed"] }
rand = "0.7.2" rand = "0.7.2"
sodiumoxide = "0.2.5" sodiumoxide = "0.2.5"
ring = "0.16.9" ring = "0.16.9"
libflate = "0.1" libflate = "0.1"
subtle = "2" subtle = "2"
threadpool = "1.8.0" threadpool = "1.8.0"
num_cpus = "1.13.0" num_cpus = "1.13.0"
tonic = { version = "0.1.1", features = ["tls", "tls-roots"] } tonic = { version = "0.1.1", features = ["tls", "tls-roots"] }
bytes = "0.4" bytes = "0.4"
prost = "0.6" prost = "0.6"
prost-types = "0.6" prost-types = "0.6"
tokio = { version = "0.2", features = ["rt-threaded", "time", "stream", "fs", "macros", "uds", "full"] } tokio = { version = "0.2", features = ["rt-threaded", "time", "stream", "fs", "macros", "uds", "full"] }
tokio-rustls = { version = "0.12.1", features = ["dangerous_configuration"] } tokio-rustls = { version = "0.12.1", features = ["dangerous_configuration"] }
webpki = "0.21.0" webpki = "0.21.0"
webpki-roots = "0.18.0" webpki-roots = "0.18.0"
[dependencies.bellman] [dependencies.bellman]
git = "https://git.hush.is/hush/librustzcash.git" git = "https://git.hush.is/hush/librustzcash.git"
rev= "acff1444ec373e9c3e37b47ca95bfd358e45255b" rev= "acff1444ec373e9c3e37b47ca95bfd358e45255b"
default-features = false default-features = false
features = ["groth16"] features = ["groth16"]
[dependencies.pairing] [dependencies.pairing]
git = "https://git.hush.is/hush/librustzcash.git" git = "https://git.hush.is/hush/librustzcash.git"
rev= "acff1444ec373e9c3e37b47ca95bfd358e45255b" rev= "acff1444ec373e9c3e37b47ca95bfd358e45255b"
[dependencies.zcash_client_backend] [dependencies.zcash_client_backend]
git = "https://git.hush.is/hush/librustzcash.git" git = "https://git.hush.is/hush/librustzcash.git"
rev= "acff1444ec373e9c3e37b47ca95bfd358e45255b" rev= "acff1444ec373e9c3e37b47ca95bfd358e45255b"
default-features = false default-features = false
[dependencies.zcash_primitives] [dependencies.zcash_primitives]
git = "https://git.hush.is/hush/librustzcash.git" git = "https://git.hush.is/hush/librustzcash.git"
rev= "acff1444ec373e9c3e37b47ca95bfd358e45255b" rev= "acff1444ec373e9c3e37b47ca95bfd358e45255b"
default-features = false default-features = false
features = ["transparent-inputs"] features = ["transparent-inputs"]
[dependencies.zcash_proofs] [dependencies.zcash_proofs]
git = "https://git.hush.is/hush/librustzcash.git" git = "https://git.hush.is/hush/librustzcash.git"
rev= "acff1444ec373e9c3e37b47ca95bfd358e45255b" rev= "acff1444ec373e9c3e37b47ca95bfd358e45255b"
default-features = false default-features = false
[dependencies.ff] [dependencies.ff]
git = "https://git.hush.is/hush/librustzcash.git" git = "https://git.hush.is/hush/librustzcash.git"
rev= "acff1444ec373e9c3e37b47ca95bfd358e45255b" rev= "acff1444ec373e9c3e37b47ca95bfd358e45255b"
features = ["ff_derive"] features = ["ff_derive"]
[build-dependencies] [build-dependencies]
tonic-build = "0.1.1" tonic-build = "0.1.1"
[dev-dependencies] [dev-dependencies]
tempdir = "0.3.7" tempdir = "0.3.7"

View File

@@ -1,13 +1,13 @@
// Copyright The Hush Developers 2019-2022 // Copyright The Hush Developers 2019-2022
// Released under the GPLv3 // Released under the GPLv3
fn main() -> Result<(), Box<dyn std::error::Error>> { fn main() -> Result<(), Box<dyn std::error::Error>> {
tonic_build::configure() tonic_build::configure()
.build_server(false) .build_server(false)
.compile( .compile(
&["proto/service.proto", "proto/compact_formats.proto"], &["proto/service.proto", "proto/compact_formats.proto"],
&["proto"], &["proto"],
)?; )?;
println!("cargo:rerun-if-changed=proto/service.proto"); println!("cargo:rerun-if-changed=proto/service.proto");
Ok(()) Ok(())
} }

View File

@@ -1,65 +1,65 @@
syntax = "proto3"; syntax = "proto3";
package cash.z.wallet.sdk.rpc; package cash.z.wallet.sdk.rpc;
option go_package = "lightwalletd/walletrpc"; option go_package = "lightwalletd/walletrpc";
option swift_prefix = ""; option swift_prefix = "";
// Remember that proto3 fields are all optional. A field that is not present will be set to its zero value. // Remember that proto3 fields are all optional. A field that is not present will be set to its zero value.
// bytes fields of hashes are in canonical little-endian format. // bytes fields of hashes are in canonical little-endian format.
// CompactBlock is a packaging of ONLY the data from a block that's needed to: // CompactBlock is a packaging of ONLY the data from a block that's needed to:
// 1. Detect a payment to your shielded Sapling address // 1. Detect a payment to your shielded Sapling address
// 2. Detect a spend of your shielded Sapling notes // 2. Detect a spend of your shielded Sapling notes
// 3. Update your witnesses to generate new Sapling spend proofs. // 3. Update your witnesses to generate new Sapling spend proofs.
message CompactBlock { message CompactBlock {
uint32 protoVersion = 1; // the version of this wire format, for storage uint32 protoVersion = 1; // the version of this wire format, for storage
uint64 height = 2; // the height of this block uint64 height = 2; // the height of this block
bytes hash = 3; // the ID (hash) of this block, same as in block explorers bytes hash = 3; // the ID (hash) of this block, same as in block explorers
bytes prevHash = 4; // the ID (hash) of this block's predecessor bytes prevHash = 4; // the ID (hash) of this block's predecessor
uint32 time = 5; // Unix epoch time when the block was mined uint32 time = 5; // Unix epoch time when the block was mined
bytes header = 6; // (hash, prevHash, and time) OR (full header) bytes header = 6; // (hash, prevHash, and time) OR (full header)
repeated CompactTx vtx = 7; // compact transactions from this block repeated CompactTx vtx = 7; // compact transactions from this block
} }
message CompactTx { message CompactTx {
// Index and hash will allow the receiver to call out to chain // Index and hash will allow the receiver to call out to chain
// explorers or other data structures to retrieve more information // explorers or other data structures to retrieve more information
// about this transaction. // about this transaction.
uint64 index = 1; // the index within the full block uint64 index = 1; // the index within the full block
bytes hash = 2; // the ID (hash) of this transaction, same as in block explorers bytes hash = 2; // the ID (hash) of this transaction, same as in block explorers
// The transaction fee: present if server can provide. In the case of a // The transaction fee: present if server can provide. In the case of a
// stateless server and a transaction with transparent inputs, this will be // stateless server and a transaction with transparent inputs, this will be
// unset because the calculation requires reference to prior transactions. // unset because the calculation requires reference to prior transactions.
// in a pure-Sapling context, the fee will be calculable as: // in a pure-Sapling context, the fee will be calculable as:
// valueBalance + (sum(vPubNew) - sum(vPubOld) - sum(tOut)) // valueBalance + (sum(vPubNew) - sum(vPubOld) - sum(tOut))
uint32 fee = 3; uint32 fee = 3;
repeated CompactSaplingSpend spends = 4; repeated CompactSaplingSpend spends = 4;
repeated CompactSaplingOutput outputs = 5; repeated CompactSaplingOutput outputs = 5;
} }
// CompactSaplingSpend is a Sapling Spend Description as described in 7.3 of the Zcash // CompactSaplingSpend is a Sapling Spend Description as described in 7.3 of the Zcash
// protocol specification. // protocol specification.
message CompactSaplingSpend { message CompactSaplingSpend {
bytes nf = 1; // nullifier (see the Zcash protocol specification) bytes nf = 1; // nullifier (see the Zcash protocol specification)
} }
// output is a Sapling Output Description as described in section 7.4 of the // output is a Sapling Output Description as described in section 7.4 of the
// Zcash protocol spec. Total size is 948. // Zcash protocol spec. Total size is 948.
message CompactSaplingOutput { message CompactSaplingOutput {
bytes cmu = 1; // note commitment u-coordinate bytes cmu = 1; // note commitment u-coordinate
bytes epk = 2; // ephemeral public key bytes epk = 2; // ephemeral public key
bytes ciphertext = 3; // first 52 bytes of ciphertext bytes ciphertext = 3; // first 52 bytes of ciphertext
} }
/* /*
message CompactSpend { message CompactSpend {
bytes nf = 1; // nullifier (see the Zcash protocol specification) bytes nf = 1; // nullifier (see the Zcash protocol specification)
} }
message CompactOutput { message CompactOutput {
bytes cmu = 1; // note commitment u-coordinate bytes cmu = 1; // note commitment u-coordinate
bytes epk = 2; // ephemeral public key bytes epk = 2; // ephemeral public key
bytes ciphertext = 3; // first 52 bytes of ciphertext bytes ciphertext = 3; // first 52 bytes of ciphertext
} }
*/ */

View File

@@ -1,170 +1,170 @@
syntax = "proto3"; syntax = "proto3";
package cash.z.wallet.sdk.rpc; package cash.z.wallet.sdk.rpc;
option go_package = "lightwalletd/walletrpc"; option go_package = "lightwalletd/walletrpc";
option swift_prefix = ""; option swift_prefix = "";
import "compact_formats.proto"; import "compact_formats.proto";
// A BlockID message contains identifiers to select a block: a height or a // A BlockID message contains identifiers to select a block: a height or a
// hash. If the hash is present it takes precedence. // hash. If the hash is present it takes precedence.
message BlockID { message BlockID {
uint64 height = 1; uint64 height = 1;
bytes hash = 2; bytes hash = 2;
} }
// BlockRange technically allows ranging from hash to hash etc but this is not // BlockRange technically allows ranging from hash to hash etc but this is not
// currently intended for support, though there is no reason you couldn't do // currently intended for support, though there is no reason you couldn't do
// it. Further permutations are left as an exercise. // it. Further permutations are left as an exercise.
message BlockRange { message BlockRange {
BlockID start = 1; BlockID start = 1;
BlockID end = 2; BlockID end = 2;
} }
// A TxFilter contains the information needed to identify a particular // A TxFilter contains the information needed to identify a particular
// transaction: either a block and an index, or a direct transaction hash. // transaction: either a block and an index, or a direct transaction hash.
message TxFilter { message TxFilter {
BlockID block = 1; BlockID block = 1;
uint64 index = 2; uint64 index = 2;
bytes hash = 3; bytes hash = 3;
} }
// RawTransaction contains the complete transaction data. It also optionally includes // RawTransaction contains the complete transaction data. It also optionally includes
// the block height in which the transaction was included // the block height in which the transaction was included
message RawTransaction { message RawTransaction {
bytes data = 1; bytes data = 1;
uint64 height = 2; uint64 height = 2;
} }
message SendResponse { message SendResponse {
int32 errorCode = 1; int32 errorCode = 1;
string errorMessage = 2; string errorMessage = 2;
} }
// Empty placeholder. Someday we may want to specify e.g. a particular chain fork. // Empty placeholder. Someday we may want to specify e.g. a particular chain fork.
message ChainSpec {} message ChainSpec {}
message Empty {} message Empty {}
message LightdInfo { message LightdInfo {
string version = 1; string version = 1;
string vendor = 2; string vendor = 2;
bool taddrSupport = 3; bool taddrSupport = 3;
string chainName = 4; string chainName = 4;
uint64 saplingActivationHeight = 5; uint64 saplingActivationHeight = 5;
string consensusBranchId = 6; // This should really be u32 or []byte, but string for readability string consensusBranchId = 6; // This should really be u32 or []byte, but string for readability
uint64 blockHeight = 7; uint64 blockHeight = 7;
uint64 difficulty = 8; uint64 difficulty = 8;
uint64 longestchain = 9; uint64 longestchain = 9;
uint64 notarized = 10; uint64 notarized = 10;
} }
message Coinsupply { message Coinsupply {
string result = 1; string result = 1;
string coin = 2; string coin = 2;
uint64 height = 3; uint64 height = 3;
uint64 supply = 4; uint64 supply = 4;
uint64 zfunds = 5; uint64 zfunds = 5;
uint64 total = 6; uint64 total = 6;
} }
message TransparentAddress { message TransparentAddress {
string address = 1; string address = 1;
} }
message TransparentAddressBlockFilter { message TransparentAddressBlockFilter {
string address = 1; string address = 1;
BlockRange range = 2; BlockRange range = 2;
} }
message Address { message Address {
string address = 1; string address = 1;
} }
message AddressList { message AddressList {
repeated string addresses = 1; repeated string addresses = 1;
} }
message Balance { message Balance {
int64 valueZat = 1; int64 valueZat = 1;
} }
message Exclude { message Exclude {
repeated bytes txid = 1; repeated bytes txid = 1;
} }
// The TreeState is derived from the Hush getblockmerkletree rpc. // The TreeState is derived from the Hush getblockmerkletree rpc.
// https://faq.hush.is/rpc/getblockmerkletree.html // https://faq.hush.is/rpc/getblockmerkletree.html
message TreeState { message TreeState {
string network = 1; // "main" or "test" string network = 1; // "main" or "test"
uint64 height = 2; // block height uint64 height = 2; // block height
string hash = 3; // block id string hash = 3; // block id
uint32 time = 4; // Unix epoch time when the block was mined uint32 time = 4; // Unix epoch time when the block was mined
string saplingTree = 5; // sapling commitment tree state string saplingTree = 5; // sapling commitment tree state
} }
// Results are sorted by height, which makes it easy to issue another // Results are sorted by height, which makes it easy to issue another
// request that picks up from where the previous left off. // request that picks up from where the previous left off.
message GetAddressUtxosArg { message GetAddressUtxosArg {
repeated string addresses = 1; repeated string addresses = 1;
uint64 startHeight = 2; uint64 startHeight = 2;
uint32 maxEntries = 3; // zero means unlimited uint32 maxEntries = 3; // zero means unlimited
} }
message GetAddressUtxosReply { message GetAddressUtxosReply {
string address = 6; string address = 6;
bytes txid = 1; bytes txid = 1;
int32 index = 2; int32 index = 2;
bytes script = 3; bytes script = 3;
int64 valueZat = 4; int64 valueZat = 4;
uint64 height = 5; uint64 height = 5;
} }
message GetAddressUtxosReplyList { message GetAddressUtxosReplyList {
repeated GetAddressUtxosReply addressUtxos = 1; repeated GetAddressUtxosReply addressUtxos = 1;
} }
service CompactTxStreamer { service CompactTxStreamer {
// Return the height of the tip of the best chain // Return the height of the tip of the best chain
rpc GetLatestBlock(ChainSpec) returns (BlockID) {} rpc GetLatestBlock(ChainSpec) returns (BlockID) {}
// Return the compact block corresponding to the given block identifier // Return the compact block corresponding to the given block identifier
rpc GetBlock(BlockID) returns (CompactBlock) {} rpc GetBlock(BlockID) returns (CompactBlock) {}
// Return a list of consecutive compact blocks // Return a list of consecutive compact blocks
rpc GetBlockRange(BlockRange) returns (stream CompactBlock) {} rpc GetBlockRange(BlockRange) returns (stream CompactBlock) {}
// Return the requested full (not compact) transaction (as from zcashd) // Return the requested full (not compact) transaction (as from zcashd)
rpc GetTransaction(TxFilter) returns (RawTransaction) {} rpc GetTransaction(TxFilter) returns (RawTransaction) {}
// Submit the given transaction to the Zcash network // Submit the given transaction to the Zcash network
rpc SendTransaction(RawTransaction) returns (SendResponse) {} rpc SendTransaction(RawTransaction) returns (SendResponse) {}
// Return the txids corresponding to the given t-address within the given block range // Return the txids corresponding to the given t-address within the given block range
rpc GetTaddressTxids(TransparentAddressBlockFilter) returns (stream RawTransaction) {} rpc GetTaddressTxids(TransparentAddressBlockFilter) returns (stream RawTransaction) {}
// wrapper for GetTaddressTxids // wrapper for GetTaddressTxids
rpc GetAddressTxids(TransparentAddressBlockFilter) returns (stream RawTransaction) {} rpc GetAddressTxids(TransparentAddressBlockFilter) returns (stream RawTransaction) {}
rpc GetTaddressBalance(AddressList) returns (Balance) {} rpc GetTaddressBalance(AddressList) returns (Balance) {}
rpc GetTaddressBalanceStream(stream Address) returns (Balance) {} rpc GetTaddressBalanceStream(stream Address) returns (Balance) {}
// Return the compact transactions currently in the mempool; the results // Return the compact transactions currently in the mempool; the results
// can be a few seconds out of date. If the Exclude list is empty, return // can be a few seconds out of date. If the Exclude list is empty, return
// all transactions; otherwise return all *except* those in the Exclude list // all transactions; otherwise return all *except* those in the Exclude list
// (if any); this allows the client to avoid receiving transactions that it // (if any); this allows the client to avoid receiving transactions that it
// already has (from an earlier call to this rpc). The transaction IDs in the // already has (from an earlier call to this rpc). The transaction IDs in the
// Exclude list can be shortened to any number of bytes to make the request // Exclude list can be shortened to any number of bytes to make the request
// more bandwidth-efficient; if two or more transactions in the mempool // more bandwidth-efficient; if two or more transactions in the mempool
// match a shortened txid, they are all sent (none is excluded). Transactions // match a shortened txid, they are all sent (none is excluded). Transactions
// in the exclude list that don't exist in the mempool are ignored. // in the exclude list that don't exist in the mempool are ignored.
rpc GetMempoolTx(Exclude) returns (stream CompactTx) {} rpc GetMempoolTx(Exclude) returns (stream CompactTx) {}
// Return a stream of current Mempool transactions. This will keep the output stream open while // Return a stream of current Mempool transactions. This will keep the output stream open while
// there are mempool transactions. It will close the returned stream when a new block is mined. // there are mempool transactions. It will close the returned stream when a new block is mined.
rpc GetMempoolStream(Empty) returns (stream RawTransaction) {} rpc GetMempoolStream(Empty) returns (stream RawTransaction) {}
// GetTreeState returns the note commitment tree state corresponding to the given block. // GetTreeState returns the note commitment tree state corresponding to the given block.
// See section 3.7 of the Zcash protocol specification. It returns several other useful // See section 3.7 of the Zcash protocol specification. It returns several other useful
// values also (even though they can be obtained using GetBlock). // values also (even though they can be obtained using GetBlock).
// The block can be specified by either height or hash. // The block can be specified by either height or hash.
rpc GetTreeState(BlockID) returns (TreeState) {} rpc GetTreeState(BlockID) returns (TreeState) {}
rpc GetLatestTreeState(Empty) returns (TreeState) {} rpc GetLatestTreeState(Empty) returns (TreeState) {}
rpc GetAddressUtxos(GetAddressUtxosArg) returns (GetAddressUtxosReplyList) {} rpc GetAddressUtxos(GetAddressUtxosArg) returns (GetAddressUtxosReplyList) {}
rpc GetAddressUtxosStream(GetAddressUtxosArg) returns (stream GetAddressUtxosReply) {} rpc GetAddressUtxosStream(GetAddressUtxosArg) returns (stream GetAddressUtxosReply) {}
// Return information about this lightwalletd instance and the blockchain // Return information about this lightwalletd instance and the blockchain
rpc GetLightdInfo(Empty) returns (LightdInfo) {} rpc GetLightdInfo(Empty) returns (LightdInfo) {}
// Testing-only, requires lightwalletd --ping-very-insecure (do not enable in production) // Testing-only, requires lightwalletd --ping-very-insecure (do not enable in production)
// rpc Ping(Duration) returns (PingResponse) {} // rpc Ping(Duration) returns (PingResponse) {}
rpc GetCoinsupply(Empty) returns (Coinsupply) {} rpc GetCoinsupply(Empty) returns (Coinsupply) {}
} }

File diff suppressed because it is too large Load Diff

View File

@@ -1,366 +1,363 @@
// Copyright The Hush Developers 2019-2022 // Copyright The Hush Developers 2019-2022
// Released under the GPLv3 // Released under the GPLv3
use log::{info,error}; use log::{info,error};
use std::sync::Arc; use std::sync::Arc;
use zcash_primitives::transaction::{TxId}; use zcash_primitives::transaction::{TxId};
use crate::grpc_client::{ChainSpec, BlockId, BlockRange, RawTransaction, CompactBlock, use crate::grpc_client::{ChainSpec, BlockId, BlockRange, RawTransaction, CompactBlock,
TransparentAddressBlockFilter, TxFilter, Empty, LightdInfo, Coinsupply}; TransparentAddressBlockFilter, TxFilter, Empty, LightdInfo, Coinsupply};
use tonic::transport::{Channel, ClientTlsConfig}; use tonic::transport::{Channel, ClientTlsConfig};
use tokio_rustls::{rustls::ClientConfig}; use tokio_rustls::{rustls::ClientConfig};
use tonic::{Request}; use tonic::{Request};
use threadpool::ThreadPool; use threadpool::ThreadPool;
use std::sync::mpsc::channel; use std::sync::mpsc::channel;
use crate::PubCertificate; use crate::PubCertificate;
use crate::grpc_client::compact_tx_streamer_client::CompactTxStreamerClient; use crate::grpc_client::compact_tx_streamer_client::CompactTxStreamerClient;
mod danger { mod danger {
use tokio_rustls::rustls; use tokio_rustls::rustls;
use webpki; use webpki;
pub struct NoCertificateVerification {} pub struct NoCertificateVerification {}
impl rustls::ServerCertVerifier for NoCertificateVerification { impl rustls::ServerCertVerifier for NoCertificateVerification {
fn verify_server_cert(&self, fn verify_server_cert(&self,
_roots: &rustls::RootCertStore, _roots: &rustls::RootCertStore,
_presented_certs: &[rustls::Certificate], _presented_certs: &[rustls::Certificate],
_dns_name: webpki::DNSNameRef<'_>, _dns_name: webpki::DNSNameRef<'_>,
_ocsp: &[u8]) -> Result<rustls::ServerCertVerified, rustls::TLSError> { _ocsp: &[u8]) -> Result<rustls::ServerCertVerified, rustls::TLSError> {
Ok(rustls::ServerCertVerified::assertion()) Ok(rustls::ServerCertVerified::assertion())
} }
} }
} }
async fn get_client(uri: &http::Uri, no_cert: bool) -> Result<CompactTxStreamerClient<Channel>, Box<dyn std::error::Error>> { async fn get_client(uri: &http::Uri, no_cert: bool) -> Result<CompactTxStreamerClient<Channel>, Box<dyn std::error::Error>> {
let channel = if uri.scheme_str() == Some("http") { let channel = if uri.scheme_str() == Some("http") {
Channel::builder(uri.clone()).connect().await? Channel::builder(uri.clone()).connect().await?
} else { } else {
let mut config = ClientConfig::new(); let mut config = ClientConfig::new();
config.alpn_protocols.push(b"h2".to_vec()); config.alpn_protocols.push(b"h2".to_vec());
config.root_store.add_server_trust_anchors(&webpki_roots::TLS_SERVER_ROOTS); config.root_store.add_server_trust_anchors(&webpki_roots::TLS_SERVER_ROOTS);
config.root_store.add_pem_file( config.root_store.add_pem_file(
&mut PubCertificate::get("lightwalletd-lite.myhush.pem").unwrap().as_ref()).unwrap(); &mut PubCertificate::get("lightwalletd-lite.myhush.pem").unwrap().as_ref()).unwrap();
if no_cert { if no_cert {
config.dangerous() config.dangerous()
.set_certificate_verifier(Arc::new(danger::NoCertificateVerification {})); .set_certificate_verifier(Arc::new(danger::NoCertificateVerification {}));
} }
let tls = ClientTlsConfig::new() let tls = ClientTlsConfig::new()
.rustls_client_config(config) .rustls_client_config(config)
.domain_name(uri.host().unwrap()); .domain_name(uri.host().unwrap());
Channel::builder(uri.clone()) Channel::builder(uri.clone())
.tls_config(tls) .tls_config(tls)
.connect() .connect()
.await? .await?
}; };
Ok(CompactTxStreamerClient::new(channel)) Ok(CompactTxStreamerClient::new(channel))
} }
// ============== // ==============
// GRPC code // GRPC code
// ============== // ==============
async fn get_lightd_info(uri: &http::Uri, no_cert: bool) -> Result<LightdInfo, Box<dyn std::error::Error>> { async fn get_lightd_info(uri: &http::Uri, no_cert: bool) -> Result<LightdInfo, Box<dyn std::error::Error>> {
let mut client = get_client(uri, no_cert).await?; let mut client = get_client(uri, no_cert).await?;
let request = Request::new(Empty {}); let request = Request::new(Empty {});
let response = client.get_lightd_info(request).await?; let response = client.get_lightd_info(request).await?;
Ok(response.into_inner()) Ok(response.into_inner())
} }
pub fn get_info(uri: &http::Uri, no_cert: bool) -> Result<LightdInfo, String> { pub fn get_info(uri: &http::Uri, no_cert: bool) -> Result<LightdInfo, String> {
let mut rt = tokio::runtime::Runtime::new().map_err(|e| e.to_string())?; let mut rt = tokio::runtime::Runtime::new().map_err(|e| e.to_string())?;
rt.block_on(get_lightd_info(uri, no_cert)).map_err( |e| e.to_string()) rt.block_on(get_lightd_info(uri, no_cert)).map_err( |e| e.to_string())
} }
async fn get_coinsupply_info(uri: &http::Uri, no_cert: bool) -> Result<Coinsupply, Box<dyn std::error::Error>> { async fn get_coinsupply_info(uri: &http::Uri, no_cert: bool) -> Result<Coinsupply, Box<dyn std::error::Error>> {
let mut client = get_client(uri, no_cert).await?; let mut client = get_client(uri, no_cert).await?;
let request = Request::new(Empty {}); let request = Request::new(Empty {});
let response = client.get_coinsupply(request).await?; let response = client.get_coinsupply(request).await?;
Ok(response.into_inner()) Ok(response.into_inner())
} }
pub fn get_coinsupply(uri: http::Uri, no_cert: bool) -> Result<Coinsupply, String> { pub fn get_coinsupply(uri: http::Uri, no_cert: bool) -> Result<Coinsupply, String> {
let mut rt = tokio::runtime::Runtime::new().map_err(|e| e.to_string())?; let mut rt = tokio::runtime::Runtime::new().map_err(|e| e.to_string())?;
rt.block_on(get_coinsupply_info(&uri, no_cert)).map_err( |e| e.to_string()) rt.block_on(get_coinsupply_info(&uri, no_cert)).map_err( |e| e.to_string())
} }
async fn get_block_range<F : 'static + std::marker::Send>( async fn get_block_range<F : 'static + std::marker::Send>(
uri: &http::Uri, uri: &http::Uri,
start_height: u64, start_height: u64,
end_height: u64, end_height: u64,
no_cert: bool, no_cert: bool,
pool: ThreadPool, pool: ThreadPool,
c: F c: F
) -> Result<(), Box<dyn std::error::Error>> ) -> Result<(), Box<dyn std::error::Error>>
where F : Fn(&[u8], u64) { where F : Fn(&[u8], u64) {
let mut client = get_client(uri, no_cert).await?; let mut client = get_client(uri, no_cert).await?;
let bs = BlockId { height: start_height, hash: vec![] }; let bs = BlockId { height: start_height, hash: vec![] };
let be = BlockId { height: end_height, hash: vec![] }; let be = BlockId { height: end_height, hash: vec![] };
let request = Request::new(BlockRange { start: Some(bs), end: Some(be) }); let request = Request::new(BlockRange { start: Some(bs), end: Some(be) });
let (tx, rx) = channel::<Option<CompactBlock>>(); let (tx, rx) = channel::<Option<CompactBlock>>();
let (ftx, frx) = channel(); let (ftx, frx) = channel();
pool.execute(move || { pool.execute(move || {
while let Ok(Some(block)) = rx.recv() { while let Ok(Some(block)) = rx.recv() {
use prost::Message; use prost::Message;
let mut encoded_buf = vec![]; let mut encoded_buf = vec![];
if let Err(e) = block.encode(&mut encoded_buf) { if let Err(e) = block.encode(&mut encoded_buf) {
error!("Error encoding block: {:?}", e); error!("Error encoding block: {:?}", e);
break; break;
} }
c(&encoded_buf, block.height); c(&encoded_buf, block.height);
} }
if let Err(e) = ftx.send(Ok(())) { if let Err(e) = ftx.send(Ok(())) {
error!("Error sending completion signal: {:?}", e); error!("Error sending completion signal: {:?}", e);
} }
}); });
let mut response = client.get_block_range(request).await?.into_inner(); let mut response = client.get_block_range(request).await?.into_inner();
while let Some(block) = response.message().await? { while let Some(block) = response.message().await? {
if let Err(e) = tx.send(Some(block)) { if let Err(e) = tx.send(Some(block)) {
error!("Error sending block to channel: {:?}", e); error!("Error sending block to channel: {:?}", e);
break; break;
} }
} }
if let Err(e) = tx.send(None) { if let Err(e) = tx.send(None) {
error!("Error sending end signal to channel: {:?}", e); error!("Error sending end signal to channel: {:?}", e);
} }
frx.iter().take(1).collect::<Result<Vec<()>, String>>()?; frx.iter().take(1).collect::<Result<Vec<()>, String>>()?;
Ok(()) Ok(())
} }
pub fn fetch_blocks<F : 'static + std::marker::Send>(uri: &http::Uri, start_height: u64, end_height: u64, no_cert: bool, pool: ThreadPool, c: F) -> Result<(), String> pub fn fetch_blocks<F : 'static + std::marker::Send>(uri: &http::Uri, start_height: u64, end_height: u64, no_cert: bool, pool: ThreadPool, c: F) -> Result<(), String>
where F : Fn(&[u8], u64) { where F : Fn(&[u8], u64) {
let mut rt = tokio::runtime::Runtime::new().map_err(|e| format!("Error creating runtime {:?}", e))?;
let mut rt = match tokio::runtime::Runtime::new() { fetch_blocks_with_runtime(&mut rt, uri, start_height, end_height, no_cert, pool, c)
Ok(r) => r, }
Err(e) => {
let es = format!("Error creating runtime {:?}", e); pub fn fetch_blocks_with_runtime<F : 'static + std::marker::Send>(rt: &mut tokio::runtime::Runtime, uri: &http::Uri, start_height: u64, end_height: u64, no_cert: bool, pool: ThreadPool, c: F) -> Result<(), String>
error!("{}", es); where F : Fn(&[u8], u64) {
return Err(es);
} match rt.block_on(get_block_range(uri, start_height, end_height, no_cert, pool, c)) {
}; Ok(o) => Ok(o),
Err(e) => {
match rt.block_on(get_block_range(uri, start_height, end_height, no_cert, pool, c)) { let e = format!("Error fetching blocks {:?}", e);
Ok(o) => Ok(o), error!("{}", e);
Err(e) => { Err(e)
let e = format!("Error fetching blocks {:?}", e); }
error!("{}", e); }
Err(e) }
}
} // get_address_txids GRPC call
} async fn get_address_txids<F : 'static + std::marker::Send>(
uri: &http::Uri,
// get_address_txids GRPC call address: String,
async fn get_address_txids<F : 'static + std::marker::Send>( start_height: u64,
uri: &http::Uri, end_height: u64,
address: String, no_cert: bool,
start_height: u64, c: F
end_height: u64, ) -> Result<(), Box<dyn std::error::Error>>
no_cert: bool, where F : Fn(&[u8], u64) {
c: F
) -> Result<(), Box<dyn std::error::Error>> let mut client = match get_client(uri, no_cert).await {
where F : Fn(&[u8], u64) { Ok(client) => client,
Err(e) => {
let mut client = match get_client(uri, no_cert).await { error!("Error creating client: {:?}", e);
Ok(client) => client, return Err(e.into());
Err(e) => { }
error!("Error creating client: {:?}", e); };
return Err(e.into());
} let start = Some(BlockId{ height: start_height, hash: vec!()});
}; let end = Some(BlockId{ height: end_height, hash: vec!()});
let start = Some(BlockId{ height: start_height, hash: vec!()}); let request = Request::new(TransparentAddressBlockFilter{ address, range: Some(BlockRange{ start, end }) });
let end = Some(BlockId{ height: end_height, hash: vec!()});
let maybe_response = match client.get_address_txids(request).await {
let request = Request::new(TransparentAddressBlockFilter{ address, range: Some(BlockRange{ start, end }) }); Ok(response) => response,
Err(e) => {
let maybe_response = match client.get_address_txids(request).await { error!("Error getting address txids: {:?}", e);
Ok(response) => response, return Err(e.into());
Err(e) => { }
error!("Error getting address txids: {:?}", e); };
return Err(e.into());
} let mut response = maybe_response.into_inner();
};
while let Some(tx) = response.message().await? {
let mut response = maybe_response.into_inner(); c(&tx.data, tx.height);
}
while let Some(tx) = response.message().await? {
c(&tx.data, tx.height); Ok(())
} }
Ok(()) // function to monitor mempool transactions
} pub async fn monitor_mempool<F: 'static + std::marker::Send>(
uri: &http::Uri,
// function to monitor mempool transactions no_cert: bool,
pub async fn monitor_mempool<F: 'static + std::marker::Send>( mut c: F
uri: &http::Uri, ) -> Result<(), Box<dyn std::error::Error>>
no_cert: bool, where
mut c: F F: FnMut(RawTransaction) -> Result<(), Box<dyn std::error::Error>>,
) -> Result<(), Box<dyn std::error::Error>> {
where
F: FnMut(RawTransaction) -> Result<(), Box<dyn std::error::Error>>, let mut client = get_client(uri, no_cert)
{ .await
.map_err(|e| format!("Error getting client: {:?}", e))?;
let mut client = get_client(uri, no_cert)
.await
.map_err(|e| format!("Error getting client: {:?}", e))?; let request = Request::new(Empty {});
let mut response = client
let request = Request::new(Empty {}); .get_mempool_stream(request)
.await
let mut response = client .map_err(|e| format!("{}", e))?
.get_mempool_stream(request) .into_inner();
.await
.map_err(|e| format!("{}", e))?
.into_inner(); while let Ok(Some(rtx)) = response.message().await {
if let Err(e) = c(rtx) {
while let Ok(Some(rtx)) = response.message().await { info!("Error processing RawTransaction: {:?}", e);
}
if let Err(e) = c(rtx) { }
info!("Error processing RawTransaction: {:?}", e);
} Ok(())
} }
Ok(()) pub fn fetch_transparent_txids<F : 'static + std::marker::Send>(
} uri: &http::Uri,
address: String,
pub fn fetch_transparent_txids<F : 'static + std::marker::Send>( start_height: u64,
uri: &http::Uri, end_height: u64,
address: String, no_cert: bool,
start_height: u64, c: F
end_height: u64, ) -> Result<(), String>
no_cert: bool, where F : Fn(&[u8], u64) {
c: F
) -> Result<(), String> let mut rt = match tokio::runtime::Runtime::new() {
where F : Fn(&[u8], u64) { Ok(r) => r,
Err(e) => {
let mut rt = match tokio::runtime::Runtime::new() { let e = format!("Error creating runtime {:?}", e);
Ok(r) => r, error!("{}", e);
Err(e) => { return Err(e);
let e = format!("Error creating runtime {:?}", e); }
error!("{}", e); };
return Err(e);
} match rt.block_on(get_address_txids(uri, address.clone(), start_height, end_height, no_cert, c)) {
}; Ok(o) => Ok(o),
Err(e) => {
match rt.block_on(get_address_txids(uri, address.clone(), start_height, end_height, no_cert, c)) { let e = format!("Error with get_address_txids runtime {:?}", e);
Ok(o) => Ok(o), error!("{}", e);
Err(e) => { return Err(e)
let e = format!("Error with get_address_txids runtime {:?}", e); }
error!("{}", e); }
return Err(e) }
}
} // get_transaction GRPC call
} async fn get_transaction(uri: &http::Uri, txid: TxId, no_cert: bool)
-> Result<RawTransaction, Box<dyn std::error::Error>> {
// get_transaction GRPC call let mut client = get_client(uri, no_cert).await?;
async fn get_transaction(uri: &http::Uri, txid: TxId, no_cert: bool) let request = Request::new(TxFilter { block: None, index: 0, hash: txid.0.to_vec() });
-> Result<RawTransaction, Box<dyn std::error::Error>> {
let mut client = get_client(uri, no_cert).await?; let response = client.get_transaction(request).await?;
let request = Request::new(TxFilter { block: None, index: 0, hash: txid.0.to_vec() });
Ok(response.into_inner())
let response = client.get_transaction(request).await?; }
Ok(response.into_inner()) pub fn fetch_full_tx(uri: &http::Uri, txid: TxId, no_cert: bool) -> Result<Vec<u8>, String> {
} let mut rt = match tokio::runtime::Runtime::new() {
Ok(r) => r,
pub fn fetch_full_tx(uri: &http::Uri, txid: TxId, no_cert: bool) -> Result<Vec<u8>, String> { Err(e) => {
let mut rt = match tokio::runtime::Runtime::new() { let errstr = format!("Error creating runtime {}", e.to_string());
Ok(r) => r, error!("{}", errstr);
Err(e) => { return Err(errstr);
let errstr = format!("Error creating runtime {}", e.to_string()); }
error!("{}", errstr); };
return Err(errstr);
} match rt.block_on(get_transaction(uri, txid, no_cert)) {
}; Ok(rawtx) => Ok(rawtx.data.to_vec()),
Err(e) => {
match rt.block_on(get_transaction(uri, txid, no_cert)) { let errstr = format!("Error in get_transaction runtime {}", e.to_string());
Ok(rawtx) => Ok(rawtx.data.to_vec()), error!("{}", errstr);
Err(e) => { Err(errstr)
let errstr = format!("Error in get_transaction runtime {}", e.to_string()); }
error!("{}", errstr); }
Err(errstr) }
}
} // send_transaction GRPC call
} async fn send_transaction(uri: &http::Uri, no_cert: bool, tx_bytes: Box<[u8]>) -> Result<String, Box<dyn std::error::Error>> {
let mut client = get_client(uri, no_cert).await?;
// send_transaction GRPC call
async fn send_transaction(uri: &http::Uri, no_cert: bool, tx_bytes: Box<[u8]>) -> Result<String, Box<dyn std::error::Error>> { let request = Request::new(RawTransaction {data: tx_bytes.to_vec(), height: 0});
let mut client = get_client(uri, no_cert).await?;
let response = client.send_transaction(request).await?;
let request = Request::new(RawTransaction {data: tx_bytes.to_vec(), height: 0});
let sendresponse = response.into_inner();
let response = client.send_transaction(request).await?; if sendresponse.error_code == 0 {
let mut txid = sendresponse.error_message;
let sendresponse = response.into_inner(); if txid.starts_with("\"") && txid.ends_with("\"") {
if sendresponse.error_code == 0 { txid = txid[1..txid.len()-1].to_string();
let mut txid = sendresponse.error_message; }
if txid.starts_with("\"") && txid.ends_with("\"") {
txid = txid[1..txid.len()-1].to_string(); Ok(txid)
} } else {
Err(Box::from(format!("Error: {:?}", sendresponse)))
Ok(txid) }
} else { }
Err(Box::from(format!("Error: {:?}", sendresponse)))
} pub fn broadcast_raw_tx(uri: &http::Uri, no_cert: bool, tx_bytes: Box<[u8]>) -> Result<String, String> {
} let mut rt = tokio::runtime::Runtime::new().map_err(|e| e.to_string())?;
pub fn broadcast_raw_tx(uri: &http::Uri, no_cert: bool, tx_bytes: Box<[u8]>) -> Result<String, String> { rt.block_on(send_transaction(uri, no_cert, tx_bytes)).map_err( |e| e.to_string())
let mut rt = tokio::runtime::Runtime::new().map_err(|e| e.to_string())?; }
rt.block_on(send_transaction(uri, no_cert, tx_bytes)).map_err( |e| e.to_string()) // get_latest_block GRPC call
} async fn get_latest_block(uri: &http::Uri, no_cert: bool) -> Result<BlockId, Box<dyn std::error::Error>> {
let mut client = get_client(uri, no_cert).await?;
// get_latest_block GRPC call
async fn get_latest_block(uri: &http::Uri, no_cert: bool) -> Result<BlockId, Box<dyn std::error::Error>> { let request = Request::new(ChainSpec {});
let mut client = get_client(uri, no_cert).await?;
let response = client.get_latest_block(request).await?;
let request = Request::new(ChainSpec {});
Ok(response.into_inner())
let response = client.get_latest_block(request).await?; }
Ok(response.into_inner()) pub fn fetch_latest_block(uri: &http::Uri, no_cert: bool) -> Result<BlockId, String> {
} let mut rt = match tokio::runtime::Runtime::new() {
Ok(r) => r,
pub fn fetch_latest_block(uri: &http::Uri, no_cert: bool) -> Result<BlockId, String> { Err(e) => {
let mut rt = match tokio::runtime::Runtime::new() { let errstr = format!("Error creating runtime {}", e.to_string());
Ok(r) => r, error!("{}", errstr);
Err(e) => { return Err(errstr);
let errstr = format!("Error creating runtime {}", e.to_string()); }
error!("{}", errstr); };
return Err(errstr);
} rt.block_on(get_latest_block(uri, no_cert)).map_err(|e| {
}; let errstr = format!("Error getting latest block {}", e.to_string());
error!("{}", errstr);
rt.block_on(get_latest_block(uri, no_cert)).map_err(|e| { errstr
let errstr = format!("Error getting latest block {}", e.to_string()); })
error!("{}", errstr); }
errstr
})
}

View File

@@ -1,25 +1,25 @@
// Copyright The Hush Developers 2019-2022 // Copyright The Hush Developers 2019-2022
// Released under the GPLv3 // Released under the GPLv3
#[macro_use] #[macro_use]
extern crate rust_embed; extern crate rust_embed;
pub mod lightclient; pub mod lightclient;
pub mod grpcconnector; pub mod grpcconnector;
pub mod lightwallet; pub mod lightwallet;
pub mod commands; pub mod commands;
#[cfg(feature = "embed_params")] #[cfg(feature = "embed_params")]
#[derive(RustEmbed)] #[derive(RustEmbed)]
#[folder = "zcash-params/"] #[folder = "zcash-params/"]
pub struct SaplingParams; pub struct SaplingParams;
#[derive(RustEmbed)] #[derive(RustEmbed)]
#[folder = "res/"] #[folder = "res/"]
pub struct PubCertificate; pub struct PubCertificate;
pub const ANCHOR_OFFSET: u32 = 0; pub const ANCHOR_OFFSET: u32 = 0;
pub mod grpc_client { pub mod grpc_client {
tonic::include_proto!("cash.z.wallet.sdk.rpc"); tonic::include_proto!("cash.z.wallet.sdk.rpc");
} }

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -1,46 +1,46 @@
//! Structs for handling supported address types. //! Structs for handling supported address types.
use pairing::bls12_381::Bls12; use pairing::bls12_381::Bls12;
use zcash_primitives::primitives::PaymentAddress; use zcash_primitives::primitives::PaymentAddress;
use zcash_client_backend::encoding::{decode_payment_address, decode_transparent_address}; use zcash_client_backend::encoding::{decode_payment_address, decode_transparent_address};
use zcash_primitives::legacy::TransparentAddress; use zcash_primitives::legacy::TransparentAddress;
/// An address that funds can be sent to. /// An address that funds can be sent to.
pub enum RecipientAddress { pub enum RecipientAddress {
Shielded(PaymentAddress<Bls12>), Shielded(PaymentAddress<Bls12>),
Transparent(TransparentAddress), Transparent(TransparentAddress),
} }
impl From<PaymentAddress<Bls12>> for RecipientAddress { impl From<PaymentAddress<Bls12>> for RecipientAddress {
fn from(addr: PaymentAddress<Bls12>) -> Self { fn from(addr: PaymentAddress<Bls12>) -> Self {
RecipientAddress::Shielded(addr) RecipientAddress::Shielded(addr)
} }
} }
impl From<TransparentAddress> for RecipientAddress { impl From<TransparentAddress> for RecipientAddress {
fn from(addr: TransparentAddress) -> Self { fn from(addr: TransparentAddress) -> Self {
RecipientAddress::Transparent(addr) RecipientAddress::Transparent(addr)
} }
} }
impl RecipientAddress { impl RecipientAddress {
pub fn from_str(s: &str, hrp_sapling_address: &str, b58_pubkey_address: [u8; 1], b58_script_address: [u8; 1]) -> Option<Self> { pub fn from_str(s: &str, hrp_sapling_address: &str, b58_pubkey_address: [u8; 1], b58_script_address: [u8; 1]) -> Option<Self> {
// Try to match a sapling z address // Try to match a sapling z address
if let Some(pa) = match decode_payment_address(hrp_sapling_address, s) { if let Some(pa) = match decode_payment_address(hrp_sapling_address, s) {
Ok(ret) => ret, Ok(ret) => ret,
Err(_) => None Err(_) => None
} }
{ {
Some(RecipientAddress::Shielded(pa)) // Matched a shielded address Some(RecipientAddress::Shielded(pa)) // Matched a shielded address
} else if let Some(addr) = match decode_transparent_address( } else if let Some(addr) = match decode_transparent_address(
&b58_pubkey_address, &b58_script_address, s) { &b58_pubkey_address, &b58_script_address, s) {
Ok(ret) => ret, Ok(ret) => ret,
Err(_) => None Err(_) => None
} }
{ {
Some(RecipientAddress::Transparent(addr)) // Matched a transparent address Some(RecipientAddress::Transparent(addr)) // Matched a transparent address
} else { } else {
None // Didn't match anything None // Didn't match anything
} }
} }
} }

File diff suppressed because it is too large Load Diff

View File

@@ -1,126 +1,126 @@
use ring::{ use ring::{
hmac::{self, Context, Key}, hmac::{self, Context, Key},
}; };
use lazy_static::lazy_static; use lazy_static::lazy_static;
use secp256k1::{PublicKey, Secp256k1, SecretKey, SignOnly, VerifyOnly, Error}; use secp256k1::{PublicKey, Secp256k1, SecretKey, SignOnly, VerifyOnly, Error};
lazy_static! { lazy_static! {
static ref SECP256K1_SIGN_ONLY: Secp256k1<SignOnly> = Secp256k1::signing_only(); static ref SECP256K1_SIGN_ONLY: Secp256k1<SignOnly> = Secp256k1::signing_only();
static ref SECP256K1_VERIFY_ONLY: Secp256k1<VerifyOnly> = Secp256k1::verification_only(); static ref SECP256K1_VERIFY_ONLY: Secp256k1<VerifyOnly> = Secp256k1::verification_only();
} }
/// Random entropy, part of extended key. /// Random entropy, part of extended key.
type ChainCode = Vec<u8>; type ChainCode = Vec<u8>;
const HARDENED_KEY_START_INDEX: u32 = 2_147_483_648; // 2 ** 31 const HARDENED_KEY_START_INDEX: u32 = 2_147_483_648; // 2 ** 31
/// KeyIndex indicates the key type and index of a child key. /// KeyIndex indicates the key type and index of a child key.
#[derive(Debug, Copy, Clone, Eq, PartialEq)] #[derive(Debug, Copy, Clone, Eq, PartialEq)]
pub enum KeyIndex { pub enum KeyIndex {
/// Normal key, index range is from 0 to 2 ** 31 - 1 /// Normal key, index range is from 0 to 2 ** 31 - 1
Normal(u32), Normal(u32),
/// Hardened key, index range is from 2 ** 31 to 2 ** 32 - 1 /// Hardened key, index range is from 2 ** 31 to 2 ** 32 - 1
Hardened(u32), Hardened(u32),
} }
impl KeyIndex { impl KeyIndex {
/// Check index range. /// Check index range.
pub fn is_valid(self) -> bool { pub fn is_valid(self) -> bool {
match self { match self {
KeyIndex::Normal(i) => i < HARDENED_KEY_START_INDEX, KeyIndex::Normal(i) => i < HARDENED_KEY_START_INDEX,
KeyIndex::Hardened(i) => i >= HARDENED_KEY_START_INDEX, KeyIndex::Hardened(i) => i >= HARDENED_KEY_START_INDEX,
} }
} }
/// Generate Hardened KeyIndex from normalize index value. /// Generate Hardened KeyIndex from normalize index value.
pub fn hardened_from_normalize_index(i: u32) -> Result<KeyIndex, Error> { pub fn hardened_from_normalize_index(i: u32) -> Result<KeyIndex, Error> {
if i < HARDENED_KEY_START_INDEX { if i < HARDENED_KEY_START_INDEX {
Ok(KeyIndex::Hardened(HARDENED_KEY_START_INDEX + i)) Ok(KeyIndex::Hardened(HARDENED_KEY_START_INDEX + i))
} else { } else {
Ok(KeyIndex::Hardened(i)) Ok(KeyIndex::Hardened(i))
} }
} }
/// Generate KeyIndex from raw index value. /// Generate KeyIndex from raw index value.
pub fn from_index(i: u32) -> Result<Self, Error> { pub fn from_index(i: u32) -> Result<Self, Error> {
if i < HARDENED_KEY_START_INDEX { if i < HARDENED_KEY_START_INDEX {
Ok(KeyIndex::Normal(i)) Ok(KeyIndex::Normal(i))
} else { } else {
Ok(KeyIndex::Hardened(i)) Ok(KeyIndex::Hardened(i))
} }
} }
} }
impl From<u32> for KeyIndex { impl From<u32> for KeyIndex {
fn from(index: u32) -> Self { fn from(index: u32) -> Self {
KeyIndex::from_index(index).expect("KeyIndex") KeyIndex::from_index(index).expect("KeyIndex")
} }
} }
/// ExtendedPrivKey is used for child key derivation. /// ExtendedPrivKey is used for child key derivation.
/// See [secp256k1 crate documentation](https://docs.rs/secp256k1) for SecretKey signatures usage. /// See [secp256k1 crate documentation](https://docs.rs/secp256k1) for SecretKey signatures usage.
#[derive(Debug, Clone, PartialEq, Eq)] #[derive(Debug, Clone, PartialEq, Eq)]
pub struct ExtendedPrivKey { pub struct ExtendedPrivKey {
pub private_key: SecretKey, pub private_key: SecretKey,
pub chain_code: ChainCode, pub chain_code: ChainCode,
} }
impl ExtendedPrivKey { impl ExtendedPrivKey {
/// Generate an ExtendedPrivKey from seed /// Generate an ExtendedPrivKey from seed
pub fn with_seed(seed: &[u8]) -> Result<ExtendedPrivKey, Error> { pub fn with_seed(seed: &[u8]) -> Result<ExtendedPrivKey, Error> {
let signature = { let signature = {
let signing_key = Key::new(hmac::HMAC_SHA512, b"Bitcoin seed"); let signing_key = Key::new(hmac::HMAC_SHA512, b"Bitcoin seed");
let mut h = Context::with_key(&signing_key); let mut h = Context::with_key(&signing_key);
h.update(&seed); h.update(&seed);
h.sign() h.sign()
}; };
let sig_bytes = signature.as_ref(); let sig_bytes = signature.as_ref();
let (key, chain_code) = sig_bytes.split_at(sig_bytes.len() / 2); let (key, chain_code) = sig_bytes.split_at(sig_bytes.len() / 2);
let private_key = SecretKey::from_slice(key)?; let private_key = SecretKey::from_slice(key)?;
Ok(ExtendedPrivKey { Ok(ExtendedPrivKey {
private_key, private_key,
chain_code: chain_code.to_vec(), chain_code: chain_code.to_vec(),
}) })
} }
fn sign_hardended_key(&self, index: u32) -> ring::hmac::Tag { fn sign_hardended_key(&self, index: u32) -> ring::hmac::Tag {
let signing_key = Key::new(hmac::HMAC_SHA512, &self.chain_code); let signing_key = Key::new(hmac::HMAC_SHA512, &self.chain_code);
let mut h = Context::with_key(&signing_key); let mut h = Context::with_key(&signing_key);
h.update(&[0x00]); h.update(&[0x00]);
h.update(&self.private_key[..]); h.update(&self.private_key[..]);
h.update(&index.to_be_bytes()); h.update(&index.to_be_bytes());
h.sign() h.sign()
} }
fn sign_normal_key(&self, index: u32) -> ring::hmac::Tag { fn sign_normal_key(&self, index: u32) -> ring::hmac::Tag {
let signing_key = Key::new(hmac::HMAC_SHA512, &self.chain_code); let signing_key = Key::new(hmac::HMAC_SHA512, &self.chain_code);
let mut h = Context::with_key(&signing_key); let mut h = Context::with_key(&signing_key);
let public_key = PublicKey::from_secret_key(&SECP256K1_SIGN_ONLY, &self.private_key); let public_key = PublicKey::from_secret_key(&SECP256K1_SIGN_ONLY, &self.private_key);
h.update(&public_key.serialize()); h.update(&public_key.serialize());
h.update(&index.to_be_bytes()); h.update(&index.to_be_bytes());
h.sign() h.sign()
} }
/// Derive a child key from ExtendedPrivKey. /// Derive a child key from ExtendedPrivKey.
pub fn derive_private_key(&self, key_index: KeyIndex) -> Result<ExtendedPrivKey, Error> { pub fn derive_private_key(&self, key_index: KeyIndex) -> Result<ExtendedPrivKey, Error> {
if !key_index.is_valid() { if !key_index.is_valid() {
return Err(Error::InvalidTweak); return Err(Error::InvalidTweak);
} }
let signature = match key_index { let signature = match key_index {
KeyIndex::Hardened(index) => self.sign_hardended_key(index), KeyIndex::Hardened(index) => self.sign_hardended_key(index),
KeyIndex::Normal(index) => self.sign_normal_key(index), KeyIndex::Normal(index) => self.sign_normal_key(index),
}; };
let sig_bytes = signature.as_ref(); let sig_bytes = signature.as_ref();
let (key, chain_code) = sig_bytes.split_at(sig_bytes.len() / 2); let (key, chain_code) = sig_bytes.split_at(sig_bytes.len() / 2);
let mut private_key = SecretKey::from_slice(key)?; let mut private_key = SecretKey::from_slice(key)?;
private_key.add_assign(&self.private_key[..])?; private_key.add_assign(&self.private_key[..])?;
Ok(ExtendedPrivKey { Ok(ExtendedPrivKey {
private_key, private_key,
chain_code: chain_code.to_vec(), chain_code: chain_code.to_vec(),
}) })
} }
} }

View File

@@ -1,123 +1,123 @@
//! Abstractions over the proving system and parameters for ease of use. //! Abstractions over the proving system and parameters for ease of use.
use bellman::groth16::{prepare_verifying_key, Parameters, PreparedVerifyingKey}; use bellman::groth16::{prepare_verifying_key, Parameters, PreparedVerifyingKey};
use pairing::bls12_381::{Bls12, Fr}; use pairing::bls12_381::{Bls12, Fr};
use zcash_primitives::{ use zcash_primitives::{
jubjub::{edwards, fs::Fs, Unknown}, jubjub::{edwards, fs::Fs, Unknown},
primitives::{Diversifier, PaymentAddress, ProofGenerationKey}, primitives::{Diversifier, PaymentAddress, ProofGenerationKey},
redjubjub::{PublicKey, Signature}, redjubjub::{PublicKey, Signature},
transaction::components::Amount transaction::components::Amount
}; };
use zcash_primitives::{ use zcash_primitives::{
merkle_tree::CommitmentTreeWitness, prover::TxProver, sapling::Node, merkle_tree::CommitmentTreeWitness, prover::TxProver, sapling::Node,
transaction::components::GROTH_PROOF_SIZE, JUBJUB, transaction::components::GROTH_PROOF_SIZE, JUBJUB,
}; };
use zcash_proofs::sapling::SaplingProvingContext; use zcash_proofs::sapling::SaplingProvingContext;
/// An implementation of [`TxProver`] using Sapling Spend and Output parameters provided /// An implementation of [`TxProver`] using Sapling Spend and Output parameters provided
/// in-memory. /// in-memory.
pub struct InMemTxProver { pub struct InMemTxProver {
spend_params: Parameters<Bls12>, spend_params: Parameters<Bls12>,
spend_vk: PreparedVerifyingKey<Bls12>, spend_vk: PreparedVerifyingKey<Bls12>,
output_params: Parameters<Bls12>, output_params: Parameters<Bls12>,
} }
impl InMemTxProver { impl InMemTxProver {
pub fn new(spend_params: &[u8], output_params: &[u8]) -> Self { pub fn new(spend_params: &[u8], output_params: &[u8]) -> Self {
// Deserialize params // Deserialize params
let spend_params = Parameters::<Bls12>::read(spend_params, false) let spend_params = Parameters::<Bls12>::read(spend_params, false)
.expect("couldn't deserialize Sapling spend parameters file"); .expect("couldn't deserialize Sapling spend parameters file");
let output_params = Parameters::<Bls12>::read(output_params, false) let output_params = Parameters::<Bls12>::read(output_params, false)
.expect("couldn't deserialize Sapling spend parameters file"); .expect("couldn't deserialize Sapling spend parameters file");
// Prepare verifying keys // Prepare verifying keys
let spend_vk = prepare_verifying_key(&spend_params.vk); let spend_vk = prepare_verifying_key(&spend_params.vk);
InMemTxProver { InMemTxProver {
spend_params, spend_params,
spend_vk, spend_vk,
output_params, output_params,
} }
} }
} }
impl TxProver for InMemTxProver { impl TxProver for InMemTxProver {
type SaplingProvingContext = SaplingProvingContext; type SaplingProvingContext = SaplingProvingContext;
fn new_sapling_proving_context(&self) -> Self::SaplingProvingContext { fn new_sapling_proving_context(&self) -> Self::SaplingProvingContext {
SaplingProvingContext::new() SaplingProvingContext::new()
} }
fn spend_proof( fn spend_proof(
&self, &self,
ctx: &mut Self::SaplingProvingContext, ctx: &mut Self::SaplingProvingContext,
proof_generation_key: ProofGenerationKey<Bls12>, proof_generation_key: ProofGenerationKey<Bls12>,
diversifier: Diversifier, diversifier: Diversifier,
rcm: Fs, rcm: Fs,
ar: Fs, ar: Fs,
value: u64, value: u64,
anchor: Fr, anchor: Fr,
witness: CommitmentTreeWitness<Node>, witness: CommitmentTreeWitness<Node>,
) -> Result< ) -> Result<
( (
[u8; GROTH_PROOF_SIZE], [u8; GROTH_PROOF_SIZE],
edwards::Point<Bls12, Unknown>, edwards::Point<Bls12, Unknown>,
PublicKey<Bls12>, PublicKey<Bls12>,
), ),
(), (),
> { > {
let (proof, cv, rk) = ctx.spend_proof( let (proof, cv, rk) = ctx.spend_proof(
proof_generation_key, proof_generation_key,
diversifier, diversifier,
rcm, rcm,
ar, ar,
value, value,
anchor, anchor,
witness, witness,
&self.spend_params, &self.spend_params,
&self.spend_vk, &self.spend_vk,
&JUBJUB, &JUBJUB,
)?; )?;
let mut zkproof = [0u8; GROTH_PROOF_SIZE]; let mut zkproof = [0u8; GROTH_PROOF_SIZE];
proof proof
.write(&mut zkproof[..]) .write(&mut zkproof[..])
.expect("should be able to serialize a proof"); .expect("should be able to serialize a proof");
Ok((zkproof, cv, rk)) Ok((zkproof, cv, rk))
} }
fn output_proof( fn output_proof(
&self, &self,
ctx: &mut Self::SaplingProvingContext, ctx: &mut Self::SaplingProvingContext,
esk: Fs, esk: Fs,
payment_address: PaymentAddress<Bls12>, payment_address: PaymentAddress<Bls12>,
rcm: Fs, rcm: Fs,
value: u64, value: u64,
) -> ([u8; GROTH_PROOF_SIZE], edwards::Point<Bls12, Unknown>) { ) -> ([u8; GROTH_PROOF_SIZE], edwards::Point<Bls12, Unknown>) {
let (proof, cv) = ctx.output_proof( let (proof, cv) = ctx.output_proof(
esk, esk,
payment_address, payment_address,
rcm, rcm,
value, value,
&self.output_params, &self.output_params,
&JUBJUB, &JUBJUB,
); );
let mut zkproof = [0u8; GROTH_PROOF_SIZE]; let mut zkproof = [0u8; GROTH_PROOF_SIZE];
proof proof
.write(&mut zkproof[..]) .write(&mut zkproof[..])
.expect("should be able to serialize a proof"); .expect("should be able to serialize a proof");
(zkproof, cv) (zkproof, cv)
} }
fn binding_sig( fn binding_sig(
&self, &self,
ctx: &mut Self::SaplingProvingContext, ctx: &mut Self::SaplingProvingContext,
value_balance: Amount, value_balance: Amount,
sighash: &[u8; 32], sighash: &[u8; 32],
) -> Result<Signature, ()> { ) -> Result<Signature, ()> {
ctx.binding_sig(value_balance, sighash, &JUBJUB) ctx.binding_sig(value_balance, sighash, &JUBJUB)
} }
} }

File diff suppressed because it is too large Load Diff

View File

@@ -1,21 +1,21 @@
use std::io::{self, Read, Write}; use std::io::{self, Read, Write};
use byteorder::{LittleEndian, ReadBytesExt, WriteBytesExt}; use byteorder::{LittleEndian, ReadBytesExt, WriteBytesExt};
pub fn read_string<R: Read>(mut reader: R) -> io::Result<String> { pub fn read_string<R: Read>(mut reader: R) -> io::Result<String> {
// Strings are written as <littleendian> len + bytes // Strings are written as <littleendian> len + bytes
let str_len = reader.read_u64::<LittleEndian>()?; let str_len = reader.read_u64::<LittleEndian>()?;
let mut str_bytes = vec![0; str_len as usize]; let mut str_bytes = vec![0; str_len as usize];
reader.read_exact(&mut str_bytes)?; reader.read_exact(&mut str_bytes)?;
let str = String::from_utf8(str_bytes).map_err(|e| { let str = String::from_utf8(str_bytes).map_err(|e| {
io::Error::new(io::ErrorKind::InvalidData, e.to_string()) io::Error::new(io::ErrorKind::InvalidData, e.to_string())
})?; })?;
Ok(str) Ok(str)
} }
pub fn write_string<W: Write>(mut writer: W, s: &String) -> io::Result<()> { pub fn write_string<W: Write>(mut writer: W, s: &String) -> io::Result<()> {
// Strings are written as len + utf8 // Strings are written as len + utf8
writer.write_u64::<LittleEndian>(s.as_bytes().len() as u64)?; writer.write_u64::<LittleEndian>(s.as_bytes().len() as u64)?;
writer.write_all(s.as_bytes()) writer.write_all(s.as_bytes())
} }

File diff suppressed because it is too large Load Diff

View File

@@ -1,111 +1,111 @@
#!/bin/bash #!/bin/bash
# This script depends on a docker image already being built # This script depends on a docker image already being built
# To build it, # To build it,
# cd docker # cd docker
# docker build --tag rustbuild:latest . # docker build --tag rustbuild:latest .
POSITIONAL=() POSITIONAL=()
while [[ $# -gt 0 ]] while [[ $# -gt 0 ]]
do do
key="$1" key="$1"
case $key in case $key in
-v|--version) -v|--version)
APP_VERSION="$2" APP_VERSION="$2"
shift # past argument shift # past argument
shift # past value shift # past value
;; ;;
*) # unknown option *) # unknown option
POSITIONAL+=("$1") # save it in an array for later POSITIONAL+=("$1") # save it in an array for later
shift # past argument shift # past argument
;; ;;
esac esac
done done
set -- "${POSITIONAL[@]}" # restore positional parameters set -- "${POSITIONAL[@]}" # restore positional parameters
if [ -z $APP_VERSION ]; then echo "APP_VERSION is not set"; exit 1; fi if [ -z $APP_VERSION ]; then echo "APP_VERSION is not set"; exit 1; fi
# Write the version file # Write the version file
echo "pub const VERSION:&str = \"$APP_VERSION\";" > /cli/src/version.rs echo "pub const VERSION:&str = \"$APP_VERSION\";" > /cli/src/version.rs
# First, do the tests # First, do the tests
cd lib && cargo test --release cd lib && cargo test --release
retVal=$? retVal=$?
if [ $retVal -ne 0 ]; then if [ $retVal -ne 0 ]; then
echo "Error" echo "Error"
exit $retVal exit $retVal
fi fi
cd .. cd ..
# Compile for mac directly # Compile for mac directly
cargo build --release cargo build --release
#macOS #macOS
rm -rf target/macOS-silentdragonxlite-cli-v$APP_VERSION rm -rf target/macOS-silentdragonxlite-cli-v$APP_VERSION
mkdir -p target/macOS-silentdragonxlite-cli-v$APP_VERSION mkdir -p target/macOS-silentdragonxlite-cli-v$APP_VERSION
cp target/release/silentdragonxlite-cli target/macOS-silentdragonxlite-cli-v$APP_VERSION/ cp target/release/silentdragonxlite-cli target/macOS-silentdragonxlite-cli-v$APP_VERSION/
# For Windows and Linux, build via docker # For Windows and Linux, build via docker
docker run --rm -v $(pwd)/:/opt/silentdragonxlite-cli rustbuild:latest bash -c "cd /opt/silentdragonxlite-cli && cargo build --release && cargo build --release --target armv7-unknown-linux-gnueabihf && cargo build --release --target aarch64-unknown-linux-gnu && SODIUM_LIB_DIR='/opt/libsodium-win64/lib/' cargo build --release --target x86_64-pc-windows-gnu" docker run --rm -v $(pwd)/:/opt/silentdragonxlite-cli rustbuild:latest bash -c "cd /opt/silentdragonxlite-cli && cargo build --release && cargo build --release --target armv7-unknown-linux-gnueabihf && cargo build --release --target aarch64-unknown-linux-gnu && SODIUM_LIB_DIR='/opt/libsodium-win64/lib/' cargo build --release --target x86_64-pc-windows-gnu"
# Now sign and zip the binaries # Now sign and zip the binaries
# macOS # macOS
gpg --batch --output target/macOS-silentdragonxlite-cli-v$APP_VERSION/silentdragonxlite-cli.sig --detach-sig target/macOS-silentdragonxlite-cli-v$APP_VERSION/silentdragonxlite-cli gpg --batch --output target/macOS-silentdragonxlite-cli-v$APP_VERSION/silentdragonxlite-cli.sig --detach-sig target/macOS-silentdragonxlite-cli-v$APP_VERSION/silentdragonxlite-cli
cd target cd target
cd macOS-silentdragonxlite-cli-v$APP_VERSION cd macOS-silentdragonxlite-cli-v$APP_VERSION
gsha256sum silentdragonxlite-cli > sha256sum.txt gsha256sum silentdragonxlite-cli > sha256sum.txt
cd .. cd ..
zip -r macOS-silentdragonxlite-cli-v$APP_VERSION.zip macOS-silentdragonxlite-cli-v$APP_VERSION zip -r macOS-silentdragonxlite-cli-v$APP_VERSION.zip macOS-silentdragonxlite-cli-v$APP_VERSION
cd .. cd ..
#Linux #Linux
rm -rf target/linux-silentdragonxlite-cli-v$APP_VERSION rm -rf target/linux-silentdragonxlite-cli-v$APP_VERSION
mkdir -p target/linux-silentdragonxlite-cli-v$APP_VERSION mkdir -p target/linux-silentdragonxlite-cli-v$APP_VERSION
cp target/release/silentdragonxlite-cli target/linux-silentdragonxlite-cli-v$APP_VERSION/ cp target/release/silentdragonxlite-cli target/linux-silentdragonxlite-cli-v$APP_VERSION/
gpg --batch --output target/linux-silentdragonxlite-cli-v$APP_VERSION/silentdragonxlite-cli.sig --detach-sig target/linux-silentdragonxlite-cli-v$APP_VERSION/silentdragonxlite-cli gpg --batch --output target/linux-silentdragonxlite-cli-v$APP_VERSION/silentdragonxlite-cli.sig --detach-sig target/linux-silentdragonxlite-cli-v$APP_VERSION/silentdragonxlite-cli
cd target cd target
cd linux-silentdragonxlite-cli-v$APP_VERSION cd linux-silentdragonxlite-cli-v$APP_VERSION
gsha256sum silentdragonxlite-cli > sha256sum.txt gsha256sum silentdragonxlite-cli > sha256sum.txt
cd .. cd ..
zip -r linux-silentdragonxlite-cli-v$APP_VERSION.zip linux-silentdragonxlite-cli-v$APP_VERSION zip -r linux-silentdragonxlite-cli-v$APP_VERSION.zip linux-silentdragonxlite-cli-v$APP_VERSION
cd .. cd ..
#Windows #Windows
rm -rf target/Windows-silentdragonxlite-cli-v$APP_VERSION rm -rf target/Windows-silentdragonxlite-cli-v$APP_VERSION
mkdir -p target/Windows-silentdragonxlite-cli-v$APP_VERSION mkdir -p target/Windows-silentdragonxlite-cli-v$APP_VERSION
cp target/x86_64-pc-windows-gnu/release/silentdragonxlite-cli.exe target/Windows-silentdragonxlite-cli-v$APP_VERSION/ cp target/x86_64-pc-windows-gnu/release/silentdragonxlite-cli.exe target/Windows-silentdragonxlite-cli-v$APP_VERSION/
gpg --batch --output target/Windows-silentdragonxlite-cli-v$APP_VERSION/silentdragonxlite-cli.sig --detach-sig target/Windows-silentdragonxlite-cli-v$APP_VERSION/silentdragonxlite-cli.exe gpg --batch --output target/Windows-silentdragonxlite-cli-v$APP_VERSION/silentdragonxlite-cli.sig --detach-sig target/Windows-silentdragonxlite-cli-v$APP_VERSION/silentdragonxlite-cli.exe
cd target cd target
cd Windows-silentdragonxlite-cli-v$APP_VERSION cd Windows-silentdragonxlite-cli-v$APP_VERSION
gsha256sum silentdragonxlite-cli.exe > sha256sum.txt gsha256sum silentdragonxlite-cli.exe > sha256sum.txt
cd .. cd ..
zip -r Windows-silentdragonxlite-cli-v$APP_VERSION.zip Windows-silentdragonxlite-cli-v$APP_VERSION zip -r Windows-silentdragonxlite-cli-v$APP_VERSION.zip Windows-silentdragonxlite-cli-v$APP_VERSION
cd .. cd ..
#Armv7 #Armv7
rm -rf target/Armv7-silentdragonxlite-cli-v$APP_VERSION rm -rf target/Armv7-silentdragonxlite-cli-v$APP_VERSION
mkdir -p target/Armv7-silentdragonxlite-cli-v$APP_VERSION mkdir -p target/Armv7-silentdragonxlite-cli-v$APP_VERSION
cp target/armv7-unknown-linux-gnueabihf/release/silentdragonxlite-cli target/Armv7-silentdragonxlite-cli-v$APP_VERSION/ cp target/armv7-unknown-linux-gnueabihf/release/silentdragonxlite-cli target/Armv7-silentdragonxlite-cli-v$APP_VERSION/
gpg --batch --output target/Armv7-silentdragonxlite-cli-v$APP_VERSION/silentdragonxlite-cli.sig --detach-sig target/Armv7-silentdragonxlite-cli-v$APP_VERSION/silentdragonxlite-cli gpg --batch --output target/Armv7-silentdragonxlite-cli-v$APP_VERSION/silentdragonxlite-cli.sig --detach-sig target/Armv7-silentdragonxlite-cli-v$APP_VERSION/silentdragonxlite-cli
cd target cd target
cd Armv7-silentdragonxlite-cli-v$APP_VERSION cd Armv7-silentdragonxlite-cli-v$APP_VERSION
gsha256sum silentdragonxlite-cli > sha256sum.txt gsha256sum silentdragonxlite-cli > sha256sum.txt
cd .. cd ..
zip -r Armv7-silentdragonxlite-cli-v$APP_VERSION.zip Armv7-silentdragonxlite-cli-v$APP_VERSION zip -r Armv7-silentdragonxlite-cli-v$APP_VERSION.zip Armv7-silentdragonxlite-cli-v$APP_VERSION
cd .. cd ..
#AARCH64 #AARCH64
rm -rf target/aarch64-silentdragonxlite-cli-v$APP_VERSION rm -rf target/aarch64-silentdragonxlite-cli-v$APP_VERSION
mkdir -p target/aarch64-silentdragonxlite-cli-v$APP_VERSION mkdir -p target/aarch64-silentdragonxlite-cli-v$APP_VERSION
cp target/aarch64-unknown-linux-gnu/release/silentdragonxlite-cli target/aarch64-silentdragonxlite-cli-v$APP_VERSION/ cp target/aarch64-unknown-linux-gnu/release/silentdragonxlite-cli target/aarch64-silentdragonxlite-cli-v$APP_VERSION/
gpg --batch --output target/aarch64-silentdragonxlite-cli-v$APP_VERSION/silentdragonxlite-cli.sig --detach-sig target/aarch64-silentdragonxlite-cli-v$APP_VERSION/silentdragonxlite-cli gpg --batch --output target/aarch64-silentdragonxlite-cli-v$APP_VERSION/silentdragonxlite-cli.sig --detach-sig target/aarch64-silentdragonxlite-cli-v$APP_VERSION/silentdragonxlite-cli
cd target cd target
cd aarch64-silentdragonxlite-cli-v$APP_VERSION cd aarch64-silentdragonxlite-cli-v$APP_VERSION
gsha256sum silentdragonxlite-cli > sha256sum.txt gsha256sum silentdragonxlite-cli > sha256sum.txt
cd .. cd ..
zip -r aarch64-silentdragonxlite-cli-v$APP_VERSION.zip aarch64-silentdragonxlite-cli-v$APP_VERSION zip -r aarch64-silentdragonxlite-cli-v$APP_VERSION.zip aarch64-silentdragonxlite-cli-v$APP_VERSION
cd .. cd ..

View File

@@ -1,50 +1,50 @@
#!/usr/bin/env bash #!/usr/bin/env bash
# Copyright 2021-2022 The Hush Developers # Copyright 2021-2022 The Hush Developers
# Distributed under the GPLv3 software license, see the accompanying # Distributed under the GPLv3 software license, see the accompanying
# file LICENSE or https://www.gnu.org/licenses/gpl-3.0.en.html # file LICENSE or https://www.gnu.org/licenses/gpl-3.0.en.html
# Purpose: Script to build Hush silentdragonxlite on x86 64-bit arch # Purpose: Script to build Hush silentdragonxlite on x86 64-bit arch
## Usage: ./util/build.sh ## Usage: ./util/build.sh
# Check if rustc is installed on system and exits if it is not # Check if rustc is installed on system and exits if it is not
if ! [ -x "$(command -v rustc)" ]; then if ! [ -x "$(command -v rustc)" ]; then
echo 'Error: rustc is not installed. Install it and try again.' >&2 echo 'Error: rustc is not installed. Install it and try again.' >&2
exit 1 exit 1
fi fi
# Check if cargo is installed on system and exits if it is not # Check if cargo is installed on system and exits if it is not
if ! [ -x "$(command -v cargo)" ]; then if ! [ -x "$(command -v cargo)" ]; then
echo 'Error: cargo is not installed. Install it and try again.' >&2 echo 'Error: cargo is not installed. Install it and try again.' >&2
exit 1 exit 1
fi fi
# Check if rustfmt is installed on system and exits if it is not # Check if rustfmt is installed on system and exits if it is not
if ! [ -x "$(command -v rustfmt)" ]; then if ! [ -x "$(command -v rustfmt)" ]; then
echo 'Error: rustfmt is not installed. Install it and try again.' >&2 echo 'Error: rustfmt is not installed. Install it and try again.' >&2
exit 1 exit 1
fi fi
echo "" echo ""
echo "Welcome to the Hush magic folks..." echo "Welcome to the Hush magic folks..."
echo "" echo ""
echo " #### ##### # #### # # ##### # # # # ##### ##### # # # ###### " echo " #### ##### # #### # # ##### # # # # ##### ##### # # # ###### "
echo "# # # # # # # # # # # # # # # # # # ## ## # " echo "# # # # # # # # # # # # # # # # # # ## ## # "
echo " #### # # # ##### # # # ##### # # # # # # # # # ## # ##### " echo " #### # # # ##### # # # ##### # # # # # # # # # ## # ##### "
echo " # # # # # # # # # # # # # # # # # # # # " echo " # # # # # # # # # # # # # # # # # # # # "
echo "# # # # # # # # # # # # # # # # # # # # # # " echo "# # # # # # # # # # # # # # # # # # # # # # "
echo " #### ##### ###### #### ###### # ##### #### # ###### ##### # # # # ###### " echo " #### ##### ###### #### ###### # ##### #### # ###### ##### # # # # ###### "
# now to compiling... # now to compiling...
echo "" echo ""
echo "You have the requirements installed, so let's build!" echo "You have the requirements installed, so let's build!"
cargo build --release cargo build --release
# check if compile was success # check if compile was success
if [ $? -ne 0 ]; then if [ $? -ne 0 ]; then
echo "" echo ""
echo 'Error: Something went wrong and it did not build successfully... Please reach out if you need support.' >&2 echo 'Error: Something went wrong and it did not build successfully... Please reach out if you need support.' >&2
exit 1 exit 1
fi fi
echo "" echo ""
echo "Hush silentdragonxlite-cli is now compiled for you. Enjoy and reach out if you need support." echo "Hush silentdragonxlite-cli is now compiled for you. Enjoy and reach out if you need support."
echo "For options, run ./silentdragonxlite --help" echo "For options, run ./silentdragonxlite --help"