aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.gitmodules3
-rw-r--r--.travis.yml2
-rw-r--r--CMakeLists.txt48
-rw-r--r--Makefile9
-rw-r--r--README.i18n.md2
-rw-r--r--README.md37
-rw-r--r--contrib/depends/packages/libiconv.mk15
-rw-r--r--contrib/depends/packages/sodium.mk2
-rw-r--r--contrib/depends/patches/libiconv/fix-whitespace.patch13
-rw-r--r--contrib/depends/patches/sodium/fix-whitespace.patch13
-rw-r--r--contrib/depends/toolchain.cmake.in3
-rw-r--r--contrib/epee/include/readline_buffer.h1
-rw-r--r--contrib/epee/src/readline_buffer.cpp5
-rw-r--r--contrib/gitian/README.md146
-rwxr-xr-xcontrib/gitian/gitian-build.py192
-rw-r--r--contrib/gitian/gitian-linux.yml162
-rw-r--r--contrib/gitian/gitian-osx.yml114
-rw-r--r--contrib/gitian/gitian-win.yml134
-rwxr-xr-xcontrib/gitian/symbol-check.py163
m---------external/trezor-common0
-rw-r--r--src/CMakeLists.txt1
-rw-r--r--src/blockchain_db/blockchain_db.h2
-rw-r--r--src/blockchain_db/lmdb/db_lmdb.cpp34
-rw-r--r--src/blockchain_utilities/CMakeLists.txt20
-rw-r--r--src/blockchain_utilities/blockchain_import.cpp8
-rw-r--r--src/blocks/CMakeLists.txt35
-rw-r--r--src/blocks/blockexports.c87
-rw-r--r--src/blocks/blocks.cpp31
-rw-r--r--src/blocks/blocks.dat0
-rw-r--r--src/blocks/blocks.h14
-rw-r--r--src/common/perf_timer.cpp8
-rw-r--r--src/common/perf_timer.h18
-rw-r--r--src/common/util.cpp25
-rw-r--r--src/cryptonote_basic/account.cpp25
-rw-r--r--src/cryptonote_basic/account.h1
-rw-r--r--src/cryptonote_basic/cryptonote_basic.h1
-rw-r--r--src/cryptonote_basic/cryptonote_format_utils.cpp85
-rw-r--r--src/cryptonote_basic/cryptonote_format_utils.h2
-rw-r--r--src/cryptonote_basic/miner.cpp2
-rw-r--r--src/cryptonote_config.h1
-rw-r--r--src/cryptonote_core/CMakeLists.txt7
-rw-r--r--src/cryptonote_core/blockchain.cpp38
-rw-r--r--src/cryptonote_core/blockchain.h23
-rw-r--r--src/cryptonote_core/cryptonote_core.cpp51
-rw-r--r--src/cryptonote_core/cryptonote_core.h15
-rw-r--r--src/cryptonote_core/cryptonote_tx_utils.cpp6
-rw-r--r--src/daemon/CMakeLists.txt21
-rw-r--r--src/daemon/core.h8
-rw-r--r--src/daemon/rpc_command_executor.cpp16
-rw-r--r--src/device/CMakeLists.txt9
-rw-r--r--src/device/device.hpp24
-rw-r--r--src/device/device_cold.hpp71
-rw-r--r--src/device/device_default.cpp10
-rw-r--r--src/device/device_io_hid.cpp15
-rw-r--r--src/device/device_ledger.cpp2
-rw-r--r--src/device/device_ledger.hpp1
-rw-r--r--src/device_trezor/CMakeLists.txt123
-rw-r--r--src/device_trezor/device_trezor.cpp363
-rw-r--r--src/device_trezor/device_trezor.hpp132
-rw-r--r--src/device_trezor/device_trezor_base.cpp301
-rw-r--r--src/device_trezor/device_trezor_base.hpp301
-rw-r--r--src/device_trezor/trezor.hpp44
-rw-r--r--src/device_trezor/trezor/exceptions.hpp193
-rw-r--r--src/device_trezor/trezor/messages/.gitignore2
-rw-r--r--src/device_trezor/trezor/messages_map.cpp125
-rw-r--r--src/device_trezor/trezor/messages_map.hpp94
-rw-r--r--src/device_trezor/trezor/protocol.cpp891
-rw-r--r--src/device_trezor/trezor/protocol.hpp300
-rw-r--r--src/device_trezor/trezor/tools/README.md36
-rw-r--r--src/device_trezor/trezor/tools/build_protob.py38
-rw-r--r--src/device_trezor/trezor/tools/pb2cpp.py186
-rw-r--r--src/device_trezor/trezor/transport.cpp651
-rw-r--r--src/device_trezor/trezor/transport.hpp331
-rw-r--r--src/device_trezor/trezor/trezor_defs.hpp48
-rw-r--r--src/mnemonics/electrum-words.cpp2
-rw-r--r--src/p2p/net_node.inl16
-rw-r--r--src/ringct/rctSigs.cpp49
-rw-r--r--src/ringct/rctSigs.h2
-rw-r--r--src/rpc/core_rpc_server.cpp31
-rw-r--r--src/rpc/core_rpc_server_commands_defs.h3
-rw-r--r--src/rpc/daemon_messages.h2
-rw-r--r--src/rpc/message.h2
-rw-r--r--src/simplewallet/simplewallet.cpp166
-rw-r--r--src/simplewallet/simplewallet.h3
-rw-r--r--src/wallet/CMakeLists.txt1
-rw-r--r--src/wallet/api/wallet.cpp26
-rw-r--r--src/wallet/api/wallet.h3
-rw-r--r--src/wallet/api/wallet2_api.h11
-rw-r--r--src/wallet/wallet2.cpp201
-rw-r--r--src/wallet/wallet2.h22
-rw-r--r--src/wallet/wallet_errors.h9
-rw-r--r--src/wallet/wallet_rpc_server.cpp173
-rw-r--r--src/wallet/wallet_rpc_server.h2
-rw-r--r--src/wallet/wallet_rpc_server_commands_defs.h67
-rw-r--r--src/wallet/wallet_rpc_server_error_codes.h1
-rw-r--r--tests/unit_tests/CMakeLists.txt2
-rw-r--r--tests/unit_tests/notify.cpp26
-rw-r--r--tests/unit_tests/test_tx_utils.cpp84
-rw-r--r--translations/monero.ts11
-rw-r--r--translations/monero_fr.ts11
-rw-r--r--translations/monero_it.ts11
-rw-r--r--translations/monero_ja.ts4072
-rw-r--r--translations/monero_sv.ts11
103 files changed, 10595 insertions, 365 deletions
diff --git a/.gitmodules b/.gitmodules
index 91bddb0a3..6e2339fb9 100644
--- a/.gitmodules
+++ b/.gitmodules
@@ -9,3 +9,6 @@
[submodule "external/rapidjson"]
path = external/rapidjson
url = https://github.com/Tencent/rapidjson
+[submodule "external/trezor-common"]
+ path = external/trezor-common
+ url = https://github.com/trezor/trezor-common.git
diff --git a/.travis.yml b/.travis.yml
index 6a9b9b94b..ca3a24ae8 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -58,7 +58,7 @@ script:
- export TRAVIS_COMMIT_LOG=`git log --format=fuller -1`
- OUTDIR=$BASE_OUTDIR/$TRAVIS_PULL_REQUEST/$TRAVIS_JOB_NUMBER-$HOST
- if [ -z "$NO_DEPENDS" ]; then $DOCKER_EXEC ccache --max-size=$CCACHE_SIZE; fi
- - $DOCKER_EXEC bash -c "mkdir build && cd build && cmake -DCMAKE_TOOLCHAIN_FILE=$TRAVIS_BUILD_DIR/contrib/depends/$HOST/share/toolchain.cmake -DTRAVIS=true .. && make $MAKEJOBS"
+ - $DOCKER_EXEC bash -c "mkdir build && cd build && cmake -DCMAKE_TOOLCHAIN_FILE=$TRAVIS_BUILD_DIR/contrib/depends/$HOST/share/toolchain.cmake .. && make $MAKEJOBS"
- export LD_LIBRARY_PATH=$TRAVIS_BUILD_DIR/contrib/depends/$HOST/lib
after_script:
- echo $TRAVIS_COMMIT_RANGE
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 8baac02e9..a3dbf974a 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -175,19 +175,22 @@ endif()
if(NOT MANUAL_SUBMODULES)
find_package(Git)
if(GIT_FOUND)
+ function (check_submodule relative_path)
+ execute_process(COMMAND git -C ${relative_path} rev-parse "HEAD" WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} OUTPUT_VARIABLE localHead)
+ execute_process(COMMAND git rev-parse "HEAD:${relative_path}" WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} OUTPUT_VARIABLE checkedHead)
+ string(COMPARE EQUAL "${localHead}" "${checkedHead}" upToDate)
+ if (upToDate)
+ message(STATUS "Submodule '${relative_path}' is up-to-date")
+ else()
+ message(FATAL_ERROR "Submodule '${relative_path}' is not up-to-date. Please update with\ngit submodule update --init --force ${relative_path}\nor run cmake with -DMANUAL_SUBMODULES=1")
+ endif()
+ endfunction ()
+
message(STATUS "Checking submodules")
- execute_process(COMMAND bash -c "cd ${CMAKE_CURRENT_SOURCE_DIR}/external/miniupnp && git rev-parse HEAD" OUTPUT_VARIABLE miniupnpLocalHead WORKING_DIRECTORY ${CMAKE_SOURCE_DIR})
- execute_process(COMMAND bash -c "cd ${CMAKE_CURRENT_SOURCE_DIR}/external/unbound && git rev-parse HEAD" OUTPUT_VARIABLE unboundLocalHead WORKING_DIRECTORY ${CMAKE_SOURCE_DIR})
- execute_process(COMMAND bash -c "cd ${CMAKE_CURRENT_SOURCE_DIR}/external/rapidjson && git rev-parse HEAD" OUTPUT_VARIABLE rapidjsonLocalHead WORKING_DIRECTORY ${CMAKE_SOURCE_DIR})
- execute_process(COMMAND bash -c "git ls-tree HEAD ${CMAKE_CURRENT_SOURCE_DIR}/external/miniupnp | awk '{print $3}'" OUTPUT_VARIABLE miniupnpCheckedHead WORKING_DIRECTORY ${CMAKE_SOURCE_DIR})
- execute_process(COMMAND bash -c "git ls-tree HEAD ${CMAKE_CURRENT_SOURCE_DIR}/external/unbound | awk '{print $3}'" OUTPUT_VARIABLE unboundCheckedHead WORKING_DIRECTORY ${CMAKE_SOURCE_DIR})
- execute_process(COMMAND bash -c "git ls-tree HEAD ${CMAKE_CURRENT_SOURCE_DIR}/external/rapidjson | awk '{print $3}'" OUTPUT_VARIABLE rapidjsonCheckedHead WORKING_DIRECTORY ${CMAKE_SOURCE_DIR})
- string(COMPARE EQUAL "${miniupnpLocalHead}" "${miniupnpCheckedHead}" miniupnpUpToDate)
- string(COMPARE EQUAL "${unboundLocalHead}" "${unboundCheckedHead}" unboundUpToDate)
- string(COMPARE EQUAL "${rapidjsonLocalHead}" "${rapidjsonCheckedHead}" rapidjsonUpToDate)
- if (NOT miniupnpUpToDate OR NOT unboundUpToDate OR NOT rapidjsonUpToDate)
- message(FATAL_ERROR "Submodules not up to date. Please update with git submodule init && git submodule update, or run cmake with -DMANUAL_SUBMODULES=1")
- endif()
+ check_submodule(external/miniupnp)
+ check_submodule(external/unbound)
+ check_submodule(external/rapidjson)
+ check_submodule(external/trezor-common)
endif()
endif()
@@ -199,6 +202,9 @@ set(PER_BLOCK_CHECKPOINT 1)
if(PER_BLOCK_CHECKPOINT)
add_definitions("-DPER_BLOCK_CHECKPOINT")
+ set(Blocks "blocks")
+else()
+ set(Blocks "")
endif()
list(INSERT CMAKE_MODULE_PATH 0
@@ -428,6 +434,8 @@ if (UNIX AND NOT APPLE)
# Note that at the time of this writing the -Wstrict-prototypes flag added below will make this fail
set(THREADS_PREFER_PTHREAD_FLAG ON)
find_package(Threads)
+ add_c_flag_if_supported(-pthread CMAKE_C_FLAGS)
+ add_cxx_flag_if_supported(-pthread CMAKE_CXX_FLAGS)
endif()
# Handle OpenSSL, used for sha256sum on binary updates and light wallet ssl http
@@ -505,6 +513,16 @@ else (HIDAPI_FOUND)
message(STATUS "Could not find HIDAPI")
endif()
+# Protobuf, optional. Required for TREZOR.
+include(FindProtobuf)
+find_package(Protobuf)
+if(Protobuf_FOUND)
+ set(HAVE_PROTOBUF 1)
+ add_definitions(-DHAVE_PROTOBUF=1)
+else(Protobuf_FOUND)
+ message(STATUS "Could not find Protobuf")
+endif()
+
if(MSVC)
add_definitions("/bigobj /MP /W3 /GS- /D_CRT_SECURE_NO_WARNINGS /wd4996 /wd4345 /D_WIN32_WINNT=0x0600 /DWIN32_LEAN_AND_MEAN /DGTEST_HAS_TR1_TUPLE=0 /FIinline_c.h /D__SSE4_1__")
# set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} /Dinline=__inline")
@@ -672,12 +690,10 @@ else()
add_linker_flag_if_supported(-Wl,-z,noexecstack noexecstack_SUPPORTED)
if (noexecstack_SUPPORTED)
set(LD_SECURITY_FLAGS "${LD_SECURITY_FLAGS} -Wl,-z,noexecstack")
- set(LD_RAW_FLAGS ${LD_RAW_FLAGS} -z noexecstack)
endif()
add_linker_flag_if_supported(-Wl,-z,noexecheap noexecheap_SUPPORTED)
if (noexecheap_SUPPORTED)
set(LD_SECURITY_FLAGS "${LD_SECURITY_FLAGS} -Wl,-z,noexecheap")
- set(LD_RAW_FLAGS ${LD_RAW_FLAGS} -z noexecheap)
endif()
# some windows linker bits
@@ -937,7 +953,9 @@ if(ANDROID)
endif()
if(CMAKE_C_COMPILER_ID STREQUAL "Clang" AND ARCH_WIDTH EQUAL "32" AND NOT IOS AND NOT FREEBSD)
find_library(ATOMIC atomic)
- list(APPEND EXTRA_LIBRARIES ${ATOMIC})
+ if (ATOMIC_FOUND)
+ list(APPEND EXTRA_LIBRARIES ${ATOMIC})
+ endif()
endif()
find_path(ZMQ_INCLUDE_PATH zmq.hpp)
diff --git a/Makefile b/Makefile
index 40b8839cc..f21038b74 100644
--- a/Makefile
+++ b/Makefile
@@ -28,6 +28,11 @@
ANDROID_STANDALONE_TOOLCHAIN_PATH ?= /usr/local/toolchain
+dotgit=$(shell ls -d .git/config)
+ifneq ($(dotgit), .git/config)
+ USE_SINGLE_BUILDDIR=1
+endif
+
subbuilddir:=$(shell echo `uname | sed -e 's|[:/\\ \(\)]|_|g'`/`git branch | grep '\* ' | cut -f2- -d' '| sed -e 's|[:/\\ \(\)]|_|g'`)
ifeq ($(USE_SINGLE_BUILDDIR),)
builddir := build/"$(subbuilddir)"
@@ -41,6 +46,10 @@ endif
all: release-all
+depends:
+ cd contrib/depends && $(MAKE) HOST=$(target) && cd ../.. && mkdir -p build/$(target)/release
+ cd build/$(target)/release && cmake -DCMAKE_TOOLCHAIN_FILE=$(CURDIR)/contrib/depends/$(target)/share/toolchain.cmake ../../.. && $(MAKE)
+
cmake-debug:
mkdir -p $(builddir)/debug
cd $(builddir)/debug && cmake -D CMAKE_BUILD_TYPE=Debug $(topdir)
diff --git a/README.i18n.md b/README.i18n.md
index 504fc9e75..a3bd8070e 100644
--- a/README.i18n.md
+++ b/README.i18n.md
@@ -3,7 +3,7 @@ Monero daemon internationalization
The Monero command line tools can be translated in various languages. If you wish to contribute and need help/support, contact the [Monero Localization Workgroup on Taiga](https://taiga.getmonero.org/project/erciccione-monero-localization/) or come chat on `#monero-translations` (Freenode/IRC, riot/matrix, MatterMost)
-In order to use the same translation workflow as the [Monero Core GUI](https://github.com/monero-project/monero-core), they use Qt Linguist translation files. However, to avoid the dependencies on Qt this normally implies, they use a custom loader to read those files at runtime.
+In order to use the same translation workflow as the [Monero Core GUI](https://github.com/monero-project/monero-gui), they use Qt Linguist translation files. However, to avoid the dependencies on Qt this normally implies, they use a custom loader to read those files at runtime.
### Tools for translators
diff --git a/README.md b/README.md
index 4b5dd0629..be7dc8b2d 100644
--- a/README.md
+++ b/README.md
@@ -32,9 +32,9 @@ These builds are of the master branch, which is used for active development and
| Ubuntu 16.04 | amd64 | [![Ubuntu 16.04 amd64](https://build.getmonero.org/png?builder=monero-static-ubuntu-amd64)](https://build.getmonero.org/builders/monero-static-ubuntu-amd64)
| Ubuntu 16.04 | armv7 | [![Ubuntu 16.04 armv7](https://build.getmonero.org/png?builder=monero-static-ubuntu-arm7)](https://build.getmonero.org/builders/monero-static-ubuntu-arm7)
| Debian Stable | armv8 | [![Debian armv8](https://build.getmonero.org/png?builder=monero-static-debian-armv8)](https://build.getmonero.org/builders/monero-static-debian-armv8)
-| OSX 10.10 | amd64 | [![OSX 10.10 amd64](https://build.getmonero.org/png?builder=monero-static-osx-10.10)](https://build.getmonero.org/builders/monero-static-osx-10.10)
| OSX 10.11 | amd64 | [![OSX 10.11 amd64](https://build.getmonero.org/png?builder=monero-static-osx-10.11)](https://build.getmonero.org/builders/monero-static-osx-10.11)
| OSX 10.12 | amd64 | [![OSX 10.12 amd64](https://build.getmonero.org/png?builder=monero-static-osx-10.12)](https://build.getmonero.org/builders/monero-static-osx-10.12)
+| OSX 10.13 | amd64 | [![OSX 10.13 amd64](https://build.getmonero.org/png?builder=monero-static-osx-10.13)](https://build.getmonero.org/builders/monero-static-osx-10.13)
| FreeBSD 11 | amd64 | [![FreeBSD 11 amd64](https://build.getmonero.org/png?builder=monero-static-freebsd64)](https://build.getmonero.org/builders/monero-static-freebsd64)
| DragonFly BSD 4.6 | amd64 | [![DragonFly BSD amd64](https://build.getmonero.org/png?builder=monero-static-dragonflybsd-amd64)](https://build.getmonero.org/builders/monero-static-dragonflybsd-amd64)
| Windows (MSYS2/MinGW) | i686 | [![Windows (MSYS2/MinGW) i686](https://build.getmonero.org/png?builder=monero-static-win32)](https://build.getmonero.org/builders/monero-static-win32)
@@ -140,6 +140,7 @@ library archives (`.a`).
| OpenSSL | basically any | NO | `libssl-dev` | `openssl` | `openssl-devel` | NO | sha256 sum |
| libzmq | 3.0.0 | NO | `libzmq3-dev` | `zeromq` | `cppzmq-devel` | NO | ZeroMQ library |
| OpenPGM | ? | NO | `libpgm-dev` | `libpgm` | `openpgm-devel` | NO | For ZeroMQ |
+| libnorm[2] | ? | NO | `libnorm-dev` | | ` | YES | For ZeroMQ |
| libunbound | 1.4.16 | YES | `libunbound-dev` | `unbound` | `unbound-devel` | NO | DNS resolver |
| libsodium | ? | NO | `libsodium-dev` | `libsodium` | `libsodium-devel` | NO | cryptography |
| libunwind | any | NO | `libunwind8-dev` | `libunwind` | `libunwind-devel` | YES | Stack traces |
@@ -147,13 +148,14 @@ library archives (`.a`).
| libreadline | 6.3.0 | NO | `libreadline6-dev` | `readline` | `readline-devel` | YES | Input editing |
| ldns | 1.6.17 | NO | `libldns-dev` | `ldns` | `ldns-devel` | YES | SSL toolkit |
| expat | 1.1 | NO | `libexpat1-dev` | `expat` | `expat-devel` | YES | XML parsing |
-| GTest | 1.5 | YES | `libgtest-dev`^ | `gtest` | `gtest-devel` | YES | Test suite |
+| GTest | 1.5 | YES | `libgtest-dev`[1] | `gtest` | `gtest-devel` | YES | Test suite |
| Doxygen | any | NO | `doxygen` | `doxygen` | `doxygen` | YES | Documentation |
| Graphviz | any | NO | `graphviz` | `graphviz` | `graphviz` | YES | Documentation |
-[^] On Debian/Ubuntu `libgtest-dev` only includes sources and headers. You must
+[1] On Debian/Ubuntu `libgtest-dev` only includes sources and headers. You must
build the library binary manually. This can be done with the following command ```sudo apt-get install libgtest-dev && cd /usr/src/gtest && sudo cmake . && sudo make && sudo mv libg* /usr/lib/ ```
+[2] libnorm-dev is needed if your zmq library was built with libnorm, and not needed otherwise
Debian / Ubuntu one liner for all dependencies
``` sudo apt update && sudo apt install build-essential cmake pkg-config libboost-all-dev libssl-dev libzmq3-dev libunbound-dev libsodium-dev libunwind8-dev liblzma-dev libreadline6-dev libldns-dev libexpat1-dev doxygen graphviz libpgm-dev```
@@ -179,7 +181,7 @@ invokes cmake commands as needed.
* Change to the root of the source code directory, change to the most recent release branch, and build:
cd monero
- git checkout v0.13.0.0
+ git checkout v0.13.0.4
make
*Optional*: If your machine has several cores and enough memory, enable
@@ -237,11 +239,13 @@ Tested on a Raspberry Pi Zero with a clean install of minimal Raspbian Stretch (
CONF_SWAPSIZE=1024
sudo /etc/init.d/dphys-swapfile start
```
+* If using an external hard disk without an external power supply, ensure it gets enough power to avoid hardware issues when syncing, by adding the line "max_usb_current=1" to /boot/config.txt
+
* Clone monero and checkout most recent release version:
```
git clone https://github.com/monero-project/monero.git
cd monero
- git checkout tags/v0.13.0.0
+ git checkout tags/v0.13.0.4
```
* Build:
```
@@ -340,7 +344,7 @@ application.
* If you would like a specific [version/tag](https://github.com/monero-project/monero/tags), do a git checkout for that version. eg. 'v0.13.0.0'. If you dont care about the version and just want binaries from master, skip this step:
- git checkout v0.13.0.0
+ git checkout v0.13.0.4
* If you are on a 64-bit system, run:
@@ -494,22 +498,19 @@ By default, in either dynamically or statically linked builds, binaries target t
### Cross Compiling
-You can also cross-compile static binaries on Linux for Windows and macOS with the `depends` system. Go to `contrib/depends` and type:
+You can also cross-compile static binaries on Linux for Windows and macOS with the `depends` system.
-* ```make HOST=x86_64-linux-gnu``` for 64-bit linux binaries.
-* ```make HOST=x86_64-w64-mingw32``` for 64-bit windows binaries. Requires: python3 nsis g++-mingw-w64-x86-64 wine1.6 bc
-* ```make HOST=x86_64-apple-darwin11``` for darwin binaries. Requires: cmake imagemagick libcap-dev librsvg2-bin libz-dev libbz2-dev libtiff-tools python-dev
-* ```make HOST=i686-linux-gnu``` for 32-bit linux binaries. Requires: g++-multilib bc
-* ```make HOST=i686-w64-mingw32``` for 32-bit windows binaries. Requires: python3 nsis g++-mingw-w64-i686
-* ```make HOST=arm-linux-gnueabihf``` for armv6 binaries. Requires: g++-arm-linux-gnueabihf
+* ```make depends target=x86_64-linux-gnu``` for 64-bit linux binaries.
+* ```make depends target=x86_64-w64-mingw32``` for 64-bit windows binaries. Requires: python3 g++-mingw-w64-x86-64 wine1.6 bc
+* ```make depends target=x86_64-apple-darwin11``` for macOS binaries. Requires: cmake imagemagick libcap-dev librsvg2-bin libz-dev libbz2-dev libtiff-tools python-dev
+* ```make depends target=i686-linux-gnu``` for 32-bit linux binaries. Requires: g++-multilib bc
+* ```make depends target=i686-w64-mingw32``` for 32-bit windows binaries. Requires: python3 g++-mingw-w64-i686
+* ```make depends target=arm-linux-gnueabihf``` for armv7 binaries. Requires: g++-arm-linux-gnueabihf
+* ```make depends target=aarch64-linux-gnu``` for armv8 binaries. Requires: g++-aarch64-linux-gnu
The required packages are the names for each toolchain on apt. Depending on your distro, they may have different names.
-Then go back to the source dir and type:
-
-* ```cmake -DCMAKE_TOOLCHAIN_FILE=`pwd`/contrib/depends/<chosen triplet>/share/toolchain.cmake```
-Where <chosen triplet> is one of the above mentioned targets.
-Using `depends` might also be easier to compile Monero on Windows than using MSys. Activate Windows Subsystem for Linux (WSL) with a distro (for example Ubuntu), install the apt build-essentials and follow the `depends` steps as depicted above.
+Using `depends` might also be easier to compile Monero on Windows than using MSYS. Activate Windows Subsystem for Linux (WSL) with a distro (for example Ubuntu), install the apt build-essentials and follow the `depends` steps as depicted above.
## Installing Monero from a package
diff --git a/contrib/depends/packages/libiconv.mk b/contrib/depends/packages/libiconv.mk
index 87e30b208..dbcb28141 100644
--- a/contrib/depends/packages/libiconv.mk
+++ b/contrib/depends/packages/libiconv.mk
@@ -3,9 +3,22 @@ $(package)_version=1.15
$(package)_download_path=https://ftp.gnu.org/gnu/libiconv
$(package)_file_name=libiconv-$($(package)_version).tar.gz
$(package)_sha256_hash=ccf536620a45458d26ba83887a983b96827001e92a13847b45e4925cc8913178
+$(package)_patches=fix-whitespace.patch
+
+define $(package)_set_vars
+ $(package)_config_opts=--disable-nls
+ $(package)_config_opts=--enable-static
+ $(package)_config_opts=--disable-shared
+ $(package)_config_opts_linux=--with-pic
+endef
+
+define $(package)_preprocess_cmds
+ cp -f $(BASEDIR)/config.guess $(BASEDIR)/config.sub build-aux/ &&\
+ patch -p1 < $($(package)_patch_dir)/fix-whitespace.patch
+endef
define $(package)_config_cmds
- $($(package)_autoconf) --disable-nls --enable-static --disable-shared
+ $($(package)_autoconf)
endef
define $(package)_build_cmds
diff --git a/contrib/depends/packages/sodium.mk b/contrib/depends/packages/sodium.mk
index c38121bf7..35f444fd5 100644
--- a/contrib/depends/packages/sodium.mk
+++ b/contrib/depends/packages/sodium.mk
@@ -3,6 +3,7 @@ $(package)_version=1.0.15
$(package)_download_path=https://download.libsodium.org/libsodium/releases/
$(package)_file_name=libsodium-$($(package)_version).tar.gz
$(package)_sha256_hash=fb6a9e879a2f674592e4328c5d9f79f082405ee4bb05cb6e679b90afe9e178f4
+$(package)_patches=fix-whitespace.patch
define $(package)_set_vars
$(package)_config_opts=--enable-static --disable-shared
@@ -11,6 +12,7 @@ endef
define $(package)_config_cmds
./autogen.sh &&\
+ patch -p1 < $($(package)_patch_dir)/fix-whitespace.patch &&\
$($(package)_autoconf) $($(package)_config_opts)
endef
diff --git a/contrib/depends/patches/libiconv/fix-whitespace.patch b/contrib/depends/patches/libiconv/fix-whitespace.patch
new file mode 100644
index 000000000..531364b45
--- /dev/null
+++ b/contrib/depends/patches/libiconv/fix-whitespace.patch
@@ -0,0 +1,13 @@
+diff --git a/preload/configure b/preload/configure
+index aab5c77..e20b8f0 100755
+--- a/preload/configure
++++ b/preload/configure
+@@ -588,7 +588,7 @@ MAKEFLAGS=
+ PACKAGE_NAME='libiconv'
+ PACKAGE_TARNAME='libiconv'
+ PACKAGE_VERSION='0'
+-PACKAGE_STRING='libiconv 0'
++PACKAGE_STRING='libiconv0'
+ PACKAGE_BUGREPORT=''
+ PACKAGE_URL=''
+
diff --git a/contrib/depends/patches/sodium/fix-whitespace.patch b/contrib/depends/patches/sodium/fix-whitespace.patch
new file mode 100644
index 000000000..c11838611
--- /dev/null
+++ b/contrib/depends/patches/sodium/fix-whitespace.patch
@@ -0,0 +1,13 @@
+diff --git a/configure b/configure
+index b29f769..ca008ae 100755
+--- a/configure
++++ b/configure
+@@ -591,7 +591,7 @@ MAKEFLAGS=
+ PACKAGE_NAME='libsodium'
+ PACKAGE_TARNAME='libsodium'
+ PACKAGE_VERSION='1.0.15'
+-PACKAGE_STRING='libsodium 1.0.15'
++PACKAGE_STRING='libsodium'
+ PACKAGE_BUGREPORT='https://github.com/jedisct1/libsodium/issues'
+ PACKAGE_URL='https://github.com/jedisct1/libsodium'
+
diff --git a/contrib/depends/toolchain.cmake.in b/contrib/depends/toolchain.cmake.in
index 66168facb..f59b7b5ee 100644
--- a/contrib/depends/toolchain.cmake.in
+++ b/contrib/depends/toolchain.cmake.in
@@ -51,9 +51,6 @@ if(CMAKE_SYSTEM_NAME STREQUAL "Darwin")
SET(APPLE True)
SET(BUILD_TAG "mac-x64")
SET(BUILD_64 ON)
- if(NOT TRAVIS)
- SET(ARCH "x86_64")
- endif()
SET(BREW OFF)
SET(PORT OFF)
SET(CMAKE_OSX_SYSROOT "@sdk@/MacOSX10.11.sdk/")
diff --git a/contrib/epee/include/readline_buffer.h b/contrib/epee/include/readline_buffer.h
index 87c8826cb..5968d243d 100644
--- a/contrib/epee/include/readline_buffer.h
+++ b/contrib/epee/include/readline_buffer.h
@@ -27,6 +27,7 @@ namespace rdln
private:
std::streambuf* m_cout_buf;
+ size_t m_prompt_length;
static std::vector<std::string>& completion_commands();
};
diff --git a/contrib/epee/src/readline_buffer.cpp b/contrib/epee/src/readline_buffer.cpp
index 076a63612..da264471f 100644
--- a/contrib/epee/src/readline_buffer.cpp
+++ b/contrib/epee/src/readline_buffer.cpp
@@ -44,7 +44,7 @@ std::vector<std::string>& rdln::readline_buffer::completion_commands()
}
rdln::readline_buffer::readline_buffer()
-: std::stringbuf(), m_cout_buf(NULL)
+: std::stringbuf(), m_cout_buf(NULL), m_prompt_length(0)
{
current = this;
}
@@ -86,8 +86,11 @@ void rdln::readline_buffer::set_prompt(const std::string& prompt)
if(m_cout_buf == NULL)
return;
boost::lock_guard<boost::mutex> lock(sync_mutex);
+ rl_set_prompt(std::string(m_prompt_length, ' ').c_str());
+ rl_redisplay();
rl_set_prompt(prompt.c_str());
rl_redisplay();
+ m_prompt_length = prompt.size();
}
void rdln::readline_buffer::add_completion(const std::string& command)
diff --git a/contrib/gitian/README.md b/contrib/gitian/README.md
new file mode 100644
index 000000000..4bd326f22
--- /dev/null
+++ b/contrib/gitian/README.md
@@ -0,0 +1,146 @@
+Gitian building
+================
+
+*Setup instructions for a Gitian build of Monero using a VM or physical system.*
+
+Gitian is the deterministic build process that is used to build the Monero CLI
+executables. It provides a way to be reasonably sure that the
+executables are really built from the git source. It also makes sure that
+the same, tested dependencies are used and statically built into the executable.
+
+Multiple developers build the source code by following a specific descriptor
+("recipe"), cryptographically sign the result, and upload the resulting signature.
+These results are compared and only if they match, the build is accepted and provided
+for download.
+
+More independent Gitian builders are needed, which is why this guide exists.
+It is preferred you follow these steps yourself instead of using someone else's
+VM image to avoid 'contaminating' the build.
+
+Table of Contents
+------------------
+
+Please note that these instructions have been forked from bitcoin's gitian build
+instructions. Please also consult their documentation, when running into problems.
+The signing is left as inherited from bitcoin at the moment.
+
+- [Preparing the Gitian builder host](#preparing-the-gitian-builder-host)
+- [Getting and building the inputs](#getting-and-building-the-inputs)
+- [Building Binaries](#building-bitcoin-core)
+- [Signing externally](#signing-externally)
+- [Uploading signatures](#uploading-signatures)
+
+Preparing the Gitian builder host
+---------------------------------
+
+The first step is to prepare the host environment that will be used to perform the Gitian builds.
+This guide explains how to set up the environment, and how to start the builds.
+
+Gitian builds are for now executed on Ubuntu 18.04 "Bionic Beaver". A solution is being worked on to run
+it in docker in the future. Please run Ubuntu in either a VM, or on your physical machine.
+You need to be logged in as the `gitianuser` in order to build gitian builds. If this user does not exist yet on your system,
+create it.
+
+Note that a version of `lxc-execute` higher or equal to 2.1.1 is required.
+You can check the version with `lxc-execute --version`.
+
+First we need to set up dependencies. Type/paste the following in the terminal:
+
+```bash
+sudo apt-get install git ruby apt-cacher-ng qemu-utils debootstrap lxc python-cheetah parted kpartx bridge-utils make ubuntu-archive-keyring curl firewalld
+```
+
+Then set up LXC and the rest with the following, which is a complex jumble of settings and workarounds:
+
+```bash
+sudo -s
+# the version of lxc-start in Debian needs to run as root, so make sure
+# that the build script can execute it without providing a password
+echo "%sudo ALL=NOPASSWD: /usr/bin/lxc-start" > /etc/sudoers.d/gitian-lxc
+echo "%sudo ALL=NOPASSWD: /usr/bin/lxc-execute" >> /etc/sudoers.d/gitian-lxc
+# make /etc/rc.local script that sets up bridge between guest and host
+echo '#!/bin/sh -e' > /etc/rc.local
+echo 'brctl addbr br0' >> /etc/rc.local
+echo 'ip addr add 10.0.3.1/24 broadcast 10.0.3.255 dev br0' >> /etc/rc.local
+echo 'ip link set br0 up' >> /etc/rc.local
+echo 'firewall-cmd --zone=trusted --add-interface=br0' >> /etc/rc.local
+echo 'exit 0' >> /etc/rc.local
+chmod +x /etc/rc.local
+# make sure that USE_LXC is always set when logging in as gitianuser,
+# and configure LXC IP addresses
+echo 'export USE_LXC=1' >> /home/gitianuser/.profile
+echo 'export GITIAN_HOST_IP=10.0.3.1' >> /home/gitianuser/.profile
+echo 'export LXC_GUEST_IP=10.0.3.5' >> /home/gitianuser/.profile
+reboot
+```
+
+This setup is required to enable networking in the container.
+
+
+Manual and Building
+-------------------
+The instructions below use the automated script [gitian-build.py](https://github.com/betcoin/bitcoin/blob/master/contrib/gitian-build.py) which only works in Ubuntu.
+It calls all available descriptors. Help for the build steps taken can be accessed with `./gitian-build.py --help`.
+
+Initial Gitian Setup
+--------------------
+The `gitian-build.py` script will checkout different release tags, so it's best to copy it:
+
+```bash
+cp monero/contrib/gitian/gitian-build.py .
+```
+
+Setup the required environment, you only need to do this once:
+
+```
+./gitian-build.py --setup fluffypony 0.14.0
+```
+
+Where `fluffypony` is your Github name and `0.14.0` is the version tag you want to build (without `v`).
+
+While gitian and this build script does provide a way for you to sign the build directly, it is recommended to sign in a seperate step.
+This script is only there for convenience. Seperate steps for building can still be taken.
+In order to sign gitian builds on your host machine, which has your PGP key,
+fork the gitian.sigs repository and clone it on your host machine,
+or pass the signed assert file back to your build machine.
+
+```
+git clone git@github.com:monero-project/gitian.sigs.git
+git remote add fluffypony git@github.com:fluffypony/gitian.sigs.git
+```
+
+Build Binaries
+-----------------------------
+To build the most recent tag:
+
+ `./gitian-build.py --detach-sign --no-commit -b fluffypony 0.14.0`
+
+To speed up the build, use `-j 5 -m 5000` as the first arguments, where `5` is the number of CPU's you allocated to the VM plus one, and 5000 is a little bit less than then the MB's of RAM you allocated. If there is memory corruption on your machine, try to tweak these values.
+
+If all went well, this produces a number of (uncommited) `.assert` files in the gitian.sigs repository.
+
+If you do detached, offline signing, you need to copy these uncommited changes to your host machine, where you can sign them. For example:
+
+```
+export NAME=fluffypony
+export VERSION=0.14
+gpg --output $VERSION-linux/$NAME/monero-linux-$VERSION-build.assert.sig --detach-sign $VERSION-linux/$NAME/monero-linux-$VERSION-build.assert
+gpg --output $VERSION-osx-unsigned/$NAME/monero-osx-$VERSION-build.assert.sig --detach-sign $VERSION-osx-unsigned/$NAME/monero-osx-$VERSION-build.assert
+gpg --output $VERSION-win-unsigned/$NAME/monero-win-$VERSION-build.assert.sig --detach-sign $VERSION-win-unsigned/$NAME/monero-win-$VERSION-build.assert
+```
+
+Make a pull request (both the `.assert` and `.assert.sig` files) to the
+[monero-project/gitian.sigs](https://github.com/monero-project/gitian.sigs/) repository:
+
+```
+git checkout -b 0.14.0
+git commit -S -a -m "Add $NAME 0.14.0"
+git push --set-upstream $NAME 0.14.0
+```
+
+```bash
+ gpg --detach-sign ${VERSION}-linux/${SIGNER}/monero-linux-*-build.assert
+ gpg --detach-sign ${VERSION}-win-unsigned/${SIGNER}/monero-win-*-build.assert
+ gpg --detach-sign ${VERSION}-osx-unsigned/${SIGNER}/monero-osx-*-build.assert
+```
+
diff --git a/contrib/gitian/gitian-build.py b/contrib/gitian/gitian-build.py
new file mode 100755
index 000000000..99c64e9dd
--- /dev/null
+++ b/contrib/gitian/gitian-build.py
@@ -0,0 +1,192 @@
+#!/usr/bin/env python3
+
+import argparse
+import os
+import subprocess
+import sys
+
+def setup():
+ global args, workdir
+ programs = ['ruby', 'git', 'apt-cacher-ng', 'make', 'wget']
+ if args.kvm:
+ programs += ['python-vm-builder', 'qemu-kvm', 'qemu-utils']
+ elif args.docker:
+ dockers = ['docker.io', 'docker-ce']
+ for i in dockers:
+ return_code = subprocess.call(['sudo', 'apt-get', 'install', '-qq', i])
+ if return_code == 0:
+ break
+ if return_code != 0:
+ print('Cannot find any way to install docker', file=sys.stderr)
+ exit(1)
+ else:
+ programs += ['lxc', 'debootstrap']
+ subprocess.check_call(['sudo', 'apt-get', 'install', '-qq'] + programs)
+ if not os.path.isdir('gitian.sigs'):
+ subprocess.check_call(['git', 'clone', 'https://github.com/monero-project/gitian.sigs.git'])
+ if not os.path.isdir('gitian-builder'):
+ subprocess.check_call(['git', 'clone', 'https://github.com/devrandom/gitian-builder.git'])
+ if not os.path.isdir('monero'):
+ subprocess.check_call(['git', 'clone', 'https://github.com/monero-project/monero.git'])
+ os.chdir('gitian-builder')
+ subprocess.check_call(['git', 'checkout', '963322de8420c50502c4cc33d4d7c0d84437b576'])
+ make_image_prog = ['bin/make-base-vm', '--suite', 'bionic', '--arch', 'amd64']
+ if args.docker:
+ make_image_prog += ['--docker']
+ elif not args.kvm:
+ make_image_prog += ['--lxc']
+ subprocess.check_call(make_image_prog)
+ os.chdir(workdir)
+ if args.is_bionic and not args.kvm and not args.docker:
+ subprocess.check_call(['sudo', 'sed', '-i', 's/lxcbr0/br0/', '/etc/default/lxc-net'])
+ print('Reboot is required')
+ exit(0)
+
+def build():
+ global args, workdir
+
+ os.makedirs('monero-binaries/' + args.version, exist_ok=True)
+ print('\nBuilding Dependencies\n')
+ os.chdir('gitian-builder')
+ os.makedirs('inputs', exist_ok=True)
+
+ subprocess.check_call(['wget', '-N', '-P', 'inputs', 'https://downloads.sourceforge.net/project/osslsigncode/osslsigncode/osslsigncode-1.7.1.tar.gz'])
+ subprocess.check_call(['wget', '-N', '-P', 'inputs', 'https://bitcoincore.org/cfields/osslsigncode-Backports-to-1.7.1.patch'])
+ subprocess.check_output(["echo 'a8c4e9cafba922f89de0df1f2152e7be286aba73f78505169bc351a7938dd911 inputs/osslsigncode-Backports-to-1.7.1.patch' | sha256sum -c"], shell=True)
+ subprocess.check_output(["echo 'f9a8cdb38b9c309326764ebc937cba1523a3a751a7ab05df3ecc99d18ae466c9 inputs/osslsigncode-1.7.1.tar.gz' | sha256sum -c"], shell=True)
+ subprocess.check_call(['make', '-C', '../monero/contrib/depends', 'download', 'SOURCES_PATH=' + os.getcwd() + '/cache/common'])
+
+ if args.linux:
+ print('\nCompiling ' + args.version + ' Linux')
+ subprocess.check_call(['bin/gbuild', '-j', args.jobs, '-m', args.memory, '--commit', 'monero='+args.commit, '--url', 'monero='+args.url, '../monero/contrib/gitian/gitian-linux.yml'])
+ subprocess.check_call(['bin/gsign', '-p', args.sign_prog, '--signer', args.signer, '--release', args.version+'-linux', '--destination', '../gitian.sigs/', '../monero/contrib/gitian/gitian-linux.yml'])
+ subprocess.check_call('mv build/out/monero-*.tar.gz ../monero-binaries/'+args.version, shell=True)
+
+ if args.windows:
+ print('\nCompiling ' + args.version + ' Windows')
+ subprocess.check_call(['bin/gbuild', '-j', args.jobs, '-m', args.memory, '--commit', 'monero='+args.commit, '--url', 'monero='+args.url, '../monero/contrib/gitian/gitian-win.yml'])
+ subprocess.check_call(['bin/gsign', '-p', args.sign_prog, '--signer', args.signer, '--release', args.version+'-win', '--destination', '../gitian.sigs/', '../monero/contrib/gitian/gitian-win.yml'])
+ subprocess.check_call('mv build/out/monero*.zip ../monero-binaries/'+args.version, shell=True)
+
+ if args.macos:
+ print('\nCompiling ' + args.version + ' MacOS')
+ subprocess.check_call(['bin/gbuild', '-j', args.jobs, '-m', args.memory, '--commit', 'monero='+args.commit, '--url', 'monero'+args.url, '../monero/contrib/gitian/gitian-osx.yml'])
+ subprocess.check_call(['bin/gsign', '-p', args.sign_prog, '--signer', args.signer, '--release', args.version+'-osx', '--destination', '../gitian.sigs/', '../monero/contrib/gitian/gitian-osx.yml'])
+ subprocess.check_call('mv build/out/monero*.tar.gz ../monero-binaries/'+args.version, shell=True)
+
+ os.chdir(workdir)
+
+ if args.commit_files:
+ print('\nCommitting '+args.version+' Unsigned Sigs\n')
+ os.chdir('gitian.sigs')
+ subprocess.check_call(['git', 'add', args.version+'-linux/'+args.signer])
+ subprocess.check_call(['git', 'add', args.version+'-win/'+args.signer])
+ subprocess.check_call(['git', 'add', args.version+'-osx/'+args.signer])
+ subprocess.check_call(['git', 'commit', '-m', 'Add '+args.version+' unsigned sigs for '+args.signer])
+ os.chdir(workdir)
+
+def verify():
+ global args, workdir
+ os.chdir('gitian-builder')
+
+ print('\nVerifying v'+args.version+' Linux\n')
+ subprocess.check_call(['bin/gverify', '-v', '-d', '../gitian.sigs/', '-r', args.version+'-linux', '../monero/contrib/gitian/gitian-linux.yml'])
+ print('\nVerifying v'+args.version+' Windows\n')
+ subprocess.check_call(['bin/gverify', '-v', '-d', '../gitian.sigs/', '-r', args.version+'-win', '../monero/contrib/gitian/gitian-win.yml'])
+ print('\nVerifying v'+args.version+' MacOS\n')
+ subprocess.check_call(['bin/gverify', '-v', '-d', '../gitian.sigs/', '-r', args.version+'-osx', '../monero/contrib/gitian/gitian-osx.yml'])
+ os.chdir(workdir)
+
+def main():
+ global args, workdir
+
+ parser = argparse.ArgumentParser(usage='%(prog)s [options] signer version')
+ parser.add_argument('-c', '--commit', action='store_true', dest='commit', help='Indicate that the version argument is for a commit or branch')
+ parser.add_argument('-p', '--pull', action='store_true', dest='pull', help='Indicate that the version argument is the number of a github repository pull request')
+ parser.add_argument('-u', '--url', dest='url', default='https://github.com/monero-project/monero', help='Specify the URL of the repository. Default is %(default)s')
+ parser.add_argument('-v', '--verify', action='store_true', dest='verify', help='Verify the Gitian build')
+ parser.add_argument('-b', '--build', action='store_true', dest='build', help='Do a Gitian build')
+ parser.add_argument('-B', '--buildsign', action='store_true', dest='buildsign', help='Build both signed and unsigned binaries')
+ parser.add_argument('-o', '--os', dest='os', default='lwm', help='Specify which Operating Systems the build is for. Default is %(default)s. l for Linux, w for Windows, m for MacOS')
+ parser.add_argument('-j', '--jobs', dest='jobs', default='2', help='Number of processes to use. Default %(default)s')
+ parser.add_argument('-m', '--memory', dest='memory', default='2000', help='Memory to allocate in MiB. Default %(default)s')
+ parser.add_argument('-k', '--kvm', action='store_true', dest='kvm', help='Use KVM instead of LXC')
+ parser.add_argument('-d', '--docker', action='store_true', dest='docker', help='Use Docker instead of LXC')
+ parser.add_argument('-S', '--setup', action='store_true', dest='setup', help='Set up the Gitian building environment. Uses LXC. If you want to use KVM, use the --kvm option. Only works on Debian-based systems (Ubuntu, Debian)')
+ parser.add_argument('-D', '--detach-sign', action='store_true', dest='detach_sign', help='Create the assert file for detached signing. Will not commit anything.')
+ parser.add_argument('-n', '--no-commit', action='store_false', dest='commit_files', help='Do not commit anything to git')
+ parser.add_argument('signer', help='GPG signer to sign each build assert file')
+ parser.add_argument('version', help='Version number, commit, or branch to build. If building a commit or branch, the -c option must be specified')
+
+ args = parser.parse_args()
+ workdir = os.getcwd()
+
+ args.linux = 'l' in args.os
+ args.windows = 'w' in args.os
+ args.macos = 'm' in args.os
+
+ args.is_bionic = b'bionic' in subprocess.check_output(['lsb_release', '-cs'])
+
+ if args.buildsign:
+ args.build=True
+ args.sign=True
+
+ if args.kvm and args.docker:
+ raise Exception('Error: cannot have both kvm and docker')
+
+ args.sign_prog = 'true' if args.detach_sign else 'gpg --detach-sign'
+
+ # Set enviroment variable USE_LXC or USE_DOCKER, let gitian-builder know that we use lxc or docker
+ if args.docker:
+ os.environ['USE_DOCKER'] = '1'
+ elif not args.kvm:
+ os.environ['USE_LXC'] = '1'
+ if not 'GITIAN_HOST_IP' in os.environ.keys():
+ os.environ['GITIAN_HOST_IP'] = '10.0.3.1'
+ if not 'LXC_GUEST_IP' in os.environ.keys():
+ os.environ['LXC_GUEST_IP'] = '10.0.3.5'
+
+ # Disable for MacOS if no SDK found
+ if args.macos and not os.path.isfile('gitian-builder/inputs/MacOSX10.11.sdk.tar.gz'):
+ print('Cannot build for MacOS, SDK does not exist. Will build for other OSes')
+ args.macos = False
+
+ script_name = os.path.basename(sys.argv[0])
+ # Signer and version shouldn't be empty
+ if args.signer == '':
+ print(script_name+': Missing signer.')
+ print('Try '+script_name+' --help for more information')
+ exit(1)
+ if args.version == '':
+ print(script_name+': Missing version.')
+ print('Try '+script_name+' --help for more information')
+ exit(1)
+
+ # Add leading 'v' for tags
+ if args.commit and args.pull:
+ raise Exception('Cannot have both commit and pull')
+ args.commit = ('' if args.commit else) + args.version
+
+ if args.setup:
+ setup()
+
+ os.chdir('monero')
+ if args.pull:
+ subprocess.check_call(['git', 'fetch', args.url, 'refs/pull/'+args.version+'/merge'])
+ os.chdir('../gitian-builder/inputs/monero')
+ subprocess.check_call(['git', 'fetch', args.url, 'refs/pull/'+args.version+'/merge'])
+ args.commit = subprocess.check_output(['git', 'show', '-s', '--format=%H', 'FETCH_HEAD'], universal_newlines=True).strip()
+ args.version = 'pull-' + args.version
+ print(args.commit)
+ subprocess.check_call(['git', 'fetch'])
+ subprocess.check_call(['git', 'checkout', args.commit])
+ os.chdir(workdir)
+
+ if args.build:
+ build()
+
+ if args.verify:
+ verify()
+
+if __name__ == '__main__':
+ main()
diff --git a/contrib/gitian/gitian-linux.yml b/contrib/gitian/gitian-linux.yml
new file mode 100644
index 000000000..473a7720d
--- /dev/null
+++ b/contrib/gitian/gitian-linux.yml
@@ -0,0 +1,162 @@
+---
+name: "monero-linux-0.14"
+enable_cache: true
+suites:
+- "bionic"
+architectures:
+- "amd64"
+packages:
+- "curl"
+- "gperf"
+- "gcc-7"
+- "g++-7"
+- "gcc"
+- "g++"
+- "gcc-7-aarch64-linux-gnu"
+- "g++-7-aarch64-linux-gnu"
+- "gcc-aarch64-linux-gnu"
+- "g++-aarch64-linux-gnu"
+- "binutils-aarch64-linux-gnu"
+- "gcc-7-arm-linux-gnueabihf"
+- "g++-7-arm-linux-gnueabihf"
+- "gcc-arm-linux-gnueabihf"
+- "g++-arm-linux-gnueabihf"
+- "g++-7-multilib"
+- "gcc-7-multilib"
+- "binutils-arm-linux-gnueabihf"
+- "binutils-gold"
+- "git"
+- "pkg-config"
+- "build-essential"
+- "autoconf"
+- "libtool"
+- "automake"
+- "faketime"
+- "bsdmainutils"
+- "ca-certificates"
+- "python"
+- "cmake"
+- "ccache"
+- "protobuf-compiler"
+- "libdbus-1-dev"
+- "libharfbuzz-dev"
+- "libprotobuf-dev"
+- "python3-zmq"
+remotes:
+- "url": "https://github.com/monero-project/monero.git"
+ "dir": "monero"
+files: []
+script: |
+
+ WRAP_DIR=$HOME/wrapped
+ HOSTS="x86_64-linux-gnu arm-linux-gnueabihf aarch64-linux-gnu i686-linux-gnu"
+ FAKETIME_HOST_PROGS="gcc g++"
+ FAKETIME_PROGS="date ar ranlib nm"
+ HOST_CFLAGS="-O2 -g"
+ HOST_CXXFLAGS="-O2 -g"
+ HOST_LDFLAGS=-static-libstdc++
+
+ export GZIP="-9n"
+ export TAR_OPTIONS="--mtime="$REFERENCE_DATE\\\ $REFERENCE_TIME""
+ export TZ="UTC"
+ export BUILD_DIR=`pwd`
+ mkdir -p ${WRAP_DIR}
+ if test -n "$GBUILD_CACHE_ENABLED"; then
+ export SOURCES_PATH=${GBUILD_COMMON_CACHE}
+ export BASE_CACHE=${GBUILD_PACKAGE_CACHE}
+ mkdir -p ${BASE_CACHE} ${SOURCES_PATH}
+ fi
+
+ function create_global_faketime_wrappers {
+ for prog in ${FAKETIME_PROGS}; do
+ echo '#!/usr/bin/env bash' > ${WRAP_DIR}/${prog}
+ echo "REAL=\`which -a ${prog} | grep -v ${WRAP_DIR}/${prog} | head -1\`" >> ${WRAP_DIR}/${prog}
+ echo 'export LD_PRELOAD=/usr/lib/x86_64-linux-gnu/faketime/libfaketime.so.1' >> ${WRAP_DIR}/${prog}
+ echo "export FAKETIME=\"$1\"" >> ${WRAP_DIR}/${prog}
+ echo "\$REAL \$@" >> $WRAP_DIR/${prog}
+ chmod +x ${WRAP_DIR}/${prog}
+ done
+ }
+
+ function create_per-host_faketime_wrappers {
+ for i in $HOSTS; do
+ for prog in ${FAKETIME_HOST_PROGS}; do
+ if which ${i}-${prog}-7
+ then
+ echo '#!/usr/bin/env bash' > ${WRAP_DIR}/${i}-${prog}
+ echo "REAL=\`which -a ${i}-${prog}-7 | grep -v ${WRAP_DIR}/${i}-${prog} | head -1\`" >> ${WRAP_DIR}/${i}-${prog}
+ echo 'export LD_PRELOAD=/usr/lib/x86_64-linux-gnu/faketime/libfaketime.so.1' >> ${WRAP_DIR}/${i}-${prog}
+ echo "export FAKETIME=\"$1\"" >> ${WRAP_DIR}/${i}-${prog}
+ echo "\$REAL \$@" >> $WRAP_DIR/${i}-${prog}
+ chmod +x ${WRAP_DIR}/${i}-${prog}
+ fi
+ done
+ done
+ }
+
+ # Faketime for depends so intermediate results are comparable
+ export PATH_orig=${PATH}
+ create_global_faketime_wrappers "2000-01-01 12:00:00"
+ create_per-host_faketime_wrappers "2000-01-01 12:00:00"
+ export PATH=${WRAP_DIR}:${PATH}
+
+ EXTRA_INCLUDES_BASE=$WRAP_DIR/extra_includes
+ mkdir -p $EXTRA_INCLUDES_BASE
+
+ # x86 needs /usr/include/i386-linux-gnu/asm pointed to /usr/include/x86_64-linux-gnu/asm,
+ # but we can't write there. Instead, create a link here and force it to be included in the
+ # search paths by wrapping gcc/g++.
+
+ mkdir -p $EXTRA_INCLUDES_BASE/i686-pc-linux-gnu
+ rm -f $WRAP_DIR/extra_includes/i686-pc-linux-gnu/asm
+ ln -s /usr/include/x86_64-linux-gnu/asm $EXTRA_INCLUDES_BASE/i686-pc-linux-gnu/asm
+
+ for prog in gcc g++; do
+ rm -f ${WRAP_DIR}/${prog}
+ cat << EOF > ${WRAP_DIR}/${prog}
+ #!/usr/bin/env bash
+ REAL="`which -a ${prog}-7 | grep -v ${WRAP_DIR}/${prog} | head -1`"
+ for var in "\$@"
+ do
+ if [ "\$var" = "-m32" ]; then
+ export C_INCLUDE_PATH="$EXTRA_INCLUDES_BASE/i686-pc-linux-gnu"
+ export CPLUS_INCLUDE_PATH="$EXTRA_INCLUDES_BASE/i686-pc-linux-gnu"
+ break
+ fi
+ done
+ \$REAL \$@
+ EOF
+ chmod +x ${WRAP_DIR}/${prog}
+ done
+
+ cd monero
+ BASEPREFIX=`pwd`/contrib/depends
+ # Build dependencies for each host
+ for i in $HOSTS; do
+ EXTRA_INCLUDES="$EXTRA_INCLUDES_BASE/$i"
+ if [ -d "$EXTRA_INCLUDES" ]; then
+ export HOST_ID_SALT="$EXTRA_INCLUDES"
+ fi
+ make ${MAKEOPTS} -C ${BASEPREFIX} HOST="${i}" -j 4 V=1
+ unset HOST_ID_SALT
+ done
+
+ # Faketime for binaries
+ export PATH=${PATH_orig}
+ create_global_faketime_wrappers "${REFERENCE_DATETIME}"
+ create_per-host_faketime_wrappers "${REFERENCE_DATETIME}"
+ export PATH=${WRAP_DIR}:${PATH}
+
+ ORIGPATH="$PATH"
+ # Build in a new dir for each host
+ for i in ${HOSTS}; do
+ export PATH=${BASEPREFIX}/${i}/native/bin:${ORIGPATH}
+ mkdir build && cd build
+ cmake .. -DCMAKE_TOOLCHAIN_FILE=${BASEPREFIX}/${i}/share/toolchain.cmake
+ make
+ DISTNAME=monero-${i}
+ mv bin ${DISTNAME}
+ find ${DISTNAME}/ | sort | tar --no-recursion --mode='u+rw,go+r-w,a+X' --owner=0 --group=0 -c -T - | gzip -9n > ${OUTDIR}/${DISTNAME}.tar.gz
+ cd ..
+ rm -rf build
+ done
diff --git a/contrib/gitian/gitian-osx.yml b/contrib/gitian/gitian-osx.yml
new file mode 100644
index 000000000..a6fcff0da
--- /dev/null
+++ b/contrib/gitian/gitian-osx.yml
@@ -0,0 +1,114 @@
+---
+name: "monero-osx-0.14"
+enable_cache: true
+suites:
+- "bionic"
+architectures:
+- "amd64"
+packages:
+- "ca-certificates"
+- "curl"
+- "g++"
+- "git"
+- "pkg-config"
+- "autoconf"
+- "librsvg2-bin"
+- "libtiff-tools"
+- "libtool"
+- "automake"
+- "faketime"
+- "bsdmainutils"
+- "cmake"
+- "imagemagick"
+- "libcap-dev"
+- "libz-dev"
+- "libbz2-dev"
+- "python"
+- "python-dev"
+- "python-setuptools"
+- "fonts-tuffy"
+remotes:
+- "url": "https://github.com/monero-project/monero.git"
+ "dir": "monero"
+files:
+- "MacOSX10.11.sdk.tar.gz"
+script: |
+ WRAP_DIR=$HOME/wrapped
+ HOSTS="x86_64-apple-darwin11"
+ FAKETIME_HOST_PROGS=""
+ FAKETIME_PROGS="ar ranlib date dmg genisoimage"
+
+ export GZIP="-9n"
+ export TAR_OPTIONS="--mtime="$REFERENCE_DATE\\\ $REFERENCE_TIME""
+ export TZ="UTC"
+ export BUILD_DIR=`pwd`
+ mkdir -p ${WRAP_DIR}
+ if test -n "$GBUILD_CACHE_ENABLED"; then
+ export SOURCES_PATH=${GBUILD_COMMON_CACHE}
+ export BASE_CACHE=${GBUILD_PACKAGE_CACHE}
+ mkdir -p ${BASE_CACHE} ${SOURCES_PATH}
+ fi
+
+ export ZERO_AR_DATE=1
+
+ function create_global_faketime_wrappers {
+ for prog in ${FAKETIME_PROGS}; do
+ echo '#!/usr/bin/env bash' > ${WRAP_DIR}/${prog}
+ echo "REAL=\`which -a ${prog} | grep -v ${WRAP_DIR}/${prog} | head -1\`" >> ${WRAP_DIR}/${prog}
+ echo 'export LD_PRELOAD=/usr/lib/x86_64-linux-gnu/faketime/libfaketime.so.1' >> ${WRAP_DIR}/${prog}
+ echo "export FAKETIME=\"$1\"" >> ${WRAP_DIR}/${prog}
+ echo "\$REAL \$@" >> $WRAP_DIR/${prog}
+ chmod +x ${WRAP_DIR}/${prog}
+ done
+ }
+
+ function create_per-host_faketime_wrappers {
+ for i in $HOSTS; do
+ for prog in ${FAKETIME_HOST_PROGS}; do
+ echo '#!/usr/bin/env bash' > ${WRAP_DIR}/${i}-${prog}
+ echo "REAL=\`which -a ${i}-${prog} | grep -v ${WRAP_DIR}/${i}-${prog} | head -1\`" >> ${WRAP_DIR}/${i}-${prog}
+ echo 'export LD_PRELOAD=/usr/lib/x86_64-linux-gnu/faketime/libfaketime.so.1' >> ${WRAP_DIR}/${i}-${prog}
+ echo "export FAKETIME=\"$1\"" >> ${WRAP_DIR}/${i}-${prog}
+ echo "\$REAL \$@" >> $WRAP_DIR/${i}-${prog}
+ chmod +x ${WRAP_DIR}/${i}-${prog}
+ done
+ done
+ }
+
+ # Faketime for depends so intermediate results are comparable
+ export PATH_orig=${PATH}
+ create_global_faketime_wrappers "2000-01-01 12:00:00"
+ create_per-host_faketime_wrappers "2000-01-01 12:00:00"
+ export PATH=${WRAP_DIR}:${PATH}
+
+ cd monero
+ BASEPREFIX=`pwd`/contrib/depends
+
+ mkdir -p ${BASEPREFIX}/SDKs
+ tar -C ${BASEPREFIX}/SDKs -xf ${BUILD_DIR}/MacOSX10.11.sdk.tar.gz
+
+ # Build dependencies for each host
+ for i in $HOSTS; do
+ make ${MAKEOPTS} -C ${BASEPREFIX} HOST="${i}"
+ done
+
+ # Faketime for binaries
+ export PATH=${PATH_orig}
+ create_global_faketime_wrappers "${REFERENCE_DATETIME}"
+ create_per-host_faketime_wrappers "${REFERENCE_DATETIME}"
+ export PATH=${WRAP_DIR}:${PATH}
+
+ ORIGPATH="$PATH"
+ # Build in a new dir for each host
+ for i in ${HOSTS}; do
+ export PATH=${BASEPREFIX}/${i}/native/bin:${ORIGPATH}
+ mkdir build && cd build
+ cmake .. -DCMAKE_TOOLCHAIN_FILE=${BASEPREFIX}/${i}/share/toolchain.cmake
+ make
+ DISTNAME=monero-${i}
+ mv bin ${DISTNAME}
+ find ${DISTNAME}/ | sort | tar --no-recursion --mode='u+rw,go+r-w,a+X' --owner=0 --group=0 -c -T - | gzip -9n > ${OUTDIR}/${DISTNAME}.tar.gz
+ cd ..
+ rm -rf build
+ done
+
diff --git a/contrib/gitian/gitian-win.yml b/contrib/gitian/gitian-win.yml
new file mode 100644
index 000000000..fef5567f9
--- /dev/null
+++ b/contrib/gitian/gitian-win.yml
@@ -0,0 +1,134 @@
+---
+name: "monero-win-0.14"
+enable_cache: true
+suites:
+- "bionic"
+architectures:
+- "amd64"
+packages:
+- "curl"
+- "g++"
+- "git"
+- "pkg-config"
+- "autoconf"
+- "libtool"
+- "automake"
+- "faketime"
+- "bsdmainutils"
+- "mingw-w64"
+- "g++-mingw-w64"
+- "zip"
+- "ca-certificates"
+- "python"
+- "rename"
+- "cmake"
+remotes:
+- "url": "https://github.com/monero-project/monero.git"
+ "dir": "monero"
+files: []
+script: |
+ WRAP_DIR=$HOME/wrapped
+ HOSTS="i686-w64-mingw32 x86_64-w64-mingw32"
+ FAKETIME_HOST_PROGS="ar ranlib nm windres strip objcopy"
+ FAKETIME_PROGS="date zip"
+ HOST_CFLAGS="-O2 -g"
+ HOST_CXXFLAGS="-O2 -g"
+
+ export GZIP="-9n"
+ export TAR_OPTIONS="--mtime="$REFERENCE_DATE\\\ $REFERENCE_TIME""
+ export TZ="UTC"
+ export BUILD_DIR=`pwd`
+ mkdir -p ${WRAP_DIR}
+ if test -n "$GBUILD_CACHE_ENABLED"; then
+ export SOURCES_PATH=${GBUILD_COMMON_CACHE}
+ export BASE_CACHE=${GBUILD_PACKAGE_CACHE}
+ mkdir -p ${BASE_CACHE} ${SOURCES_PATH}
+ fi
+
+ function create_global_faketime_wrappers {
+ for prog in ${FAKETIME_PROGS}; do
+ echo '#!/usr/bin/env bash' > ${WRAP_DIR}/${prog}
+ echo "REAL=\`which -a ${prog} | grep -v ${WRAP_DIR}/${prog} | head -1\`" >> ${WRAP_DIR}/${prog}
+ echo 'export LD_PRELOAD=/usr/lib/x86_64-linux-gnu/faketime/libfaketime.so.1' >> ${WRAP_DIR}/${prog}
+ echo "export FAKETIME=\"$1\"" >> ${WRAP_DIR}/${prog}
+ echo "\$REAL \$@" >> $WRAP_DIR/${prog}
+ chmod +x ${WRAP_DIR}/${prog}
+ done
+ }
+
+ function create_per-host_faketime_wrappers {
+ for i in $HOSTS; do
+ for prog in ${FAKETIME_HOST_PROGS}; do
+ echo '#!/usr/bin/env bash' > ${WRAP_DIR}/${i}-${prog}
+ echo "REAL=\`which -a ${i}-${prog} | grep -v ${WRAP_DIR}/${i}-${prog} | head -1\`" >> ${WRAP_DIR}/${i}-${prog}
+ echo 'export LD_PRELOAD=/usr/lib/x86_64-linux-gnu/faketime/libfaketime.so.1' >> ${WRAP_DIR}/${i}-${prog}
+ echo "export FAKETIME=\"$1\"" >> ${WRAP_DIR}/${i}-${prog}
+ echo "\$REAL \$@" >> $WRAP_DIR/${i}-${prog}
+ chmod +x ${WRAP_DIR}/${i}-${prog}
+ done
+ done
+ }
+
+ function create_per-host_linker_wrapper {
+ # This is only needed for trusty, as the mingw linker leaks a few bytes of
+ # heap, causing non-determinism. See discussion in https://github.com/bitcoin/bitcoin/pull/6900
+ for i in $HOSTS; do
+ mkdir -p ${WRAP_DIR}/${i}
+ for prog in collect2; do
+ echo '#!/usr/bin/env bash' > ${WRAP_DIR}/${i}/${prog}
+ REAL=$(${i}-gcc -print-prog-name=${prog})
+ echo "export MALLOC_PERTURB_=255" >> ${WRAP_DIR}/${i}/${prog}
+ echo "${REAL} \$@" >> $WRAP_DIR/${i}/${prog}
+ chmod +x ${WRAP_DIR}/${i}/${prog}
+ done
+ for prog in gcc g++; do
+ echo '#!/usr/bin/env bash' > ${WRAP_DIR}/${i}-${prog}
+ echo "REAL=\`which -a ${i}-${prog}-posix | grep -v ${WRAP_DIR}/${i}-${prog} | head -1\`" >> ${WRAP_DIR}/${i}-${prog}
+ echo 'export LD_PRELOAD=/usr/lib/x86_64-linux-gnu/faketime/libfaketime.so.1' >> ${WRAP_DIR}/${i}-${prog}
+ echo "export FAKETIME=\"$1\"" >> ${WRAP_DIR}/${i}-${prog}
+ echo "export COMPILER_PATH=${WRAP_DIR}/${i}" >> ${WRAP_DIR}/${i}-${prog}
+ echo "\$REAL \$@" >> $WRAP_DIR/${i}-${prog}
+ chmod +x ${WRAP_DIR}/${i}-${prog}
+ done
+ done
+ }
+
+ # Faketime for depends so intermediate results are comparable
+ export PATH_orig=${PATH}
+ create_global_faketime_wrappers "2000-01-01 12:00:00"
+ create_per-host_faketime_wrappers "2000-01-01 12:00:00"
+ create_per-host_linker_wrapper "2000-01-01 12:00:00"
+ export PATH=${WRAP_DIR}:${PATH}
+
+ cd monero
+ BASEPREFIX=`pwd`/contrib/depends
+ # Build dependencies for each host
+ for i in $HOSTS; do
+ EXTRA_INCLUDES="$EXTRA_INCLUDES_BASE/$i"
+ if [ -d "$EXTRA_INCLUDES" ]; then
+ export HOST_ID_SALT="$EXTRA_INCLUDES"
+ fi
+ make ${MAKEOPTS} -C ${BASEPREFIX} HOST="${i}" -j 4 V=1
+ unset HOST_ID_SALT
+ done
+
+ # Faketime for binaries
+ export PATH=${PATH_orig}
+ create_global_faketime_wrappers "${REFERENCE_DATETIME}"
+ create_per-host_faketime_wrappers "${REFERENCE_DATETIME}"
+ export PATH=${WRAP_DIR}:${PATH}
+
+ ORIGPATH="$PATH"
+ # Run cmake and make, for each create a new build/ directory,
+ # compile from there, archive, export and delete the archive again
+ for i in ${HOSTS}; do
+ export PATH=${BASEPREFIX}/${i}/native/bin:${ORIGPATH}
+ mkdir build && cd build
+ cmake .. -DCMAKE_TOOLCHAIN_FILE=${BASEPREFIX}/${i}/share/toolchain.cmake
+ make
+ DISTNAME=monero-${i}
+ mv bin ${DISTNAME}
+ find ${DISTNAME}/ | sort | zip -X@ ${OUTDIR}/${DISTNAME}.zip
+ cd .. && rm -rf build
+ done
+
diff --git a/contrib/gitian/symbol-check.py b/contrib/gitian/symbol-check.py
new file mode 100755
index 000000000..6808e77da
--- /dev/null
+++ b/contrib/gitian/symbol-check.py
@@ -0,0 +1,163 @@
+#!/usr/bin/env python3
+# Copyright (c) 2014 Wladimir J. van der Laan
+# Distributed under the MIT software license, see the accompanying
+# file COPYING or http://www.opensource.org/licenses/mit-license.php.
+'''
+A script to check that the (Linux) executables produced by gitian only contain
+allowed gcc, glibc and libstdc++ version symbols. This makes sure they are
+still compatible with the minimum supported Linux distribution versions.
+
+Example usage:
+
+ find ../gitian-builder/build -type f -executable | xargs python contrib/devtools/symbol-check.py
+'''
+import subprocess
+import re
+import sys
+import os
+
+# Debian 6.0.9 (Squeeze) has:
+#
+# - g++ version 4.4.5 (https://packages.debian.org/search?suite=default&section=all&arch=any&searchon=names&keywords=g%2B%2B)
+# - libc version 2.11.3 (https://packages.debian.org/search?suite=default&section=all&arch=any&searchon=names&keywords=libc6)
+# - libstdc++ version 4.4.5 (https://packages.debian.org/search?suite=default&section=all&arch=any&searchon=names&keywords=libstdc%2B%2B6)
+#
+# Ubuntu 10.04.4 (Lucid Lynx) has:
+#
+# - g++ version 4.4.3 (http://packages.ubuntu.com/search?keywords=g%2B%2B&searchon=names&suite=lucid&section=all)
+# - libc version 2.11.1 (http://packages.ubuntu.com/search?keywords=libc6&searchon=names&suite=lucid&section=all)
+# - libstdc++ version 4.4.3 (http://packages.ubuntu.com/search?suite=lucid&section=all&arch=any&keywords=libstdc%2B%2B&searchon=names)
+#
+# Taking the minimum of these as our target.
+#
+# According to GNU ABI document (http://gcc.gnu.org/onlinedocs/libstdc++/manual/abi.html) this corresponds to:
+# GCC 4.4.0: GCC_4.4.0
+# GCC 4.4.2: GLIBCXX_3.4.13, CXXABI_1.3.3
+# (glibc) GLIBC_2_11
+#
+MAX_VERSIONS = {
+'GCC': (4,4,0),
+'CXXABI': (1,3,3),
+'GLIBCXX': (3,4,13),
+'GLIBC': (2,11)
+}
+# See here for a description of _IO_stdin_used:
+# https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=634261#109
+
+# Ignore symbols that are exported as part of every executable
+IGNORE_EXPORTS = {
+'_edata', '_end', '_init', '__bss_start', '_fini', '_IO_stdin_used', 'stdin', 'stdout', 'stderr'
+}
+READELF_CMD = os.getenv('READELF', '/usr/bin/readelf')
+CPPFILT_CMD = os.getenv('CPPFILT', '/usr/bin/c++filt')
+# Allowed NEEDED libraries
+ALLOWED_LIBRARIES = {
+# bitcoind and bitcoin-qt
+'libgcc_s.so.1', # GCC base support
+'libc.so.6', # C library
+'libpthread.so.0', # threading
+'libanl.so.1', # DNS resolve
+'libm.so.6', # math library
+'librt.so.1', # real-time (clock)
+'ld-linux-x86-64.so.2', # 64-bit dynamic linker
+'ld-linux.so.2', # 32-bit dynamic linker
+# bitcoin-qt only
+'libX11-xcb.so.1', # part of X11
+'libX11.so.6', # part of X11
+'libxcb.so.1', # part of X11
+'libfontconfig.so.1', # font support
+'libfreetype.so.6', # font parsing
+'libdl.so.2' # programming interface to dynamic linker
+}
+
+class CPPFilt(object):
+ '''
+ Demangle C++ symbol names.
+
+ Use a pipe to the 'c++filt' command.
+ '''
+ def __init__(self):
+ self.proc = subprocess.Popen(CPPFILT_CMD, stdin=subprocess.PIPE, stdout=subprocess.PIPE, universal_newlines=True)
+
+ def __call__(self, mangled):
+ self.proc.stdin.write(mangled + '\n')
+ self.proc.stdin.flush()
+ return self.proc.stdout.readline().rstrip()
+
+ def close(self):
+ self.proc.stdin.close()
+ self.proc.stdout.close()
+ self.proc.wait()
+
+def read_symbols(executable, imports=True):
+ '''
+ Parse an ELF executable and return a list of (symbol,version) tuples
+ for dynamic, imported symbols.
+ '''
+ p = subprocess.Popen([READELF_CMD, '--dyn-syms', '-W', executable], stdout=subprocess.PIPE, stderr=subprocess.PIPE, stdin=subprocess.PIPE, universal_newlines=True)
+ (stdout, stderr) = p.communicate()
+ if p.returncode:
+ raise IOError('Could not read symbols for %s: %s' % (executable, stderr.strip()))
+ syms = []
+ for line in stdout.splitlines():
+ line = line.split()
+ if len(line)>7 and re.match('[0-9]+:$', line[0]):
+ (sym, _, version) = line[7].partition('@')
+ is_import = line[6] == 'UND'
+ if version.startswith('@'):
+ version = version[1:]
+ if is_import == imports:
+ syms.append((sym, version))
+ return syms
+
+def check_version(max_versions, version):
+ if '_' in version:
+ (lib, _, ver) = version.rpartition('_')
+ else:
+ lib = version
+ ver = '0'
+ ver = tuple([int(x) for x in ver.split('.')])
+ if not lib in max_versions:
+ return False
+ return ver <= max_versions[lib]
+
+def read_libraries(filename):
+ p = subprocess.Popen([READELF_CMD, '-d', '-W', filename], stdout=subprocess.PIPE, stderr=subprocess.PIPE, stdin=subprocess.PIPE, universal_newlines=True)
+ (stdout, stderr) = p.communicate()
+ if p.returncode:
+ raise IOError('Error opening file')
+ libraries = []
+ for line in stdout.splitlines():
+ tokens = line.split()
+ if len(tokens)>2 and tokens[1] == '(NEEDED)':
+ match = re.match('^Shared library: \[(.*)\]$', ' '.join(tokens[2:]))
+ if match:
+ libraries.append(match.group(1))
+ else:
+ raise ValueError('Unparseable (NEEDED) specification')
+ return libraries
+
+if __name__ == '__main__':
+ cppfilt = CPPFilt()
+ retval = 0
+ for filename in sys.argv[1:]:
+ # Check imported symbols
+ for sym,version in read_symbols(filename, True):
+ if version and not check_version(MAX_VERSIONS, version):
+ print('%s: symbol %s from unsupported version %s' % (filename, cppfilt(sym), version))
+ retval = 1
+ # Check exported symbols
+ for sym,version in read_symbols(filename, False):
+ if sym in IGNORE_EXPORTS:
+ continue
+ print('%s: export of symbol %s not allowed' % (filename, cppfilt(sym)))
+ retval = 1
+ # Check dependency libraries
+ for library_name in read_libraries(filename):
+ if library_name not in ALLOWED_LIBRARIES:
+ print('%s: NEEDED library %s is not allowed' % (filename, library_name))
+ retval = 1
+
+ sys.exit(retval)
+
+
diff --git a/external/trezor-common b/external/trezor-common
new file mode 160000
+Subproject 588f8e03f5ac111adf719f0a437de67481a26ae
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
index 3b71c38cd..6ee7effdd 100644
--- a/src/CMakeLists.txt
+++ b/src/CMakeLists.txt
@@ -145,3 +145,4 @@ if(PER_BLOCK_CHECKPOINT)
endif()
add_subdirectory(device)
+add_subdirectory(device_trezor)
diff --git a/src/blockchain_db/blockchain_db.h b/src/blockchain_db/blockchain_db.h
index 396ae7544..71c46d76b 100644
--- a/src/blockchain_db/blockchain_db.h
+++ b/src/blockchain_db/blockchain_db.h
@@ -544,7 +544,7 @@ public:
/**
* @brief An empty constructor.
*/
- BlockchainDB(): m_open(false) { }
+ BlockchainDB(): m_hardfork(NULL), m_open(false) { }
/**
* @brief An empty destructor.
diff --git a/src/blockchain_db/lmdb/db_lmdb.cpp b/src/blockchain_db/lmdb/db_lmdb.cpp
index bd91f308a..84a083c26 100644
--- a/src/blockchain_db/lmdb/db_lmdb.cpp
+++ b/src/blockchain_db/lmdb/db_lmdb.cpp
@@ -1208,7 +1208,7 @@ void BlockchainLMDB::open(const std::string& filename, const int db_flags)
if (is_hdd_result)
{
if (is_hdd_result.value())
- MCLOG_RED(el::Level::Warning, "global", "The blockchain is on a rotating drive: this will be very slow, use a SSD if possible");
+ MCLOG_RED(el::Level::Warning, "global", "The blockchain is on a rotating drive: this will be very slow, use an SSD if possible");
}
m_folder = filename;
@@ -1980,22 +1980,36 @@ std::vector<uint64_t> BlockchainLMDB::get_block_cumulative_rct_outputs(const std
MDB_val v;
uint64_t prev_height = heights[0];
+ uint64_t range_begin = 0, range_end = 0;
for (uint64_t height: heights)
{
- if (height == prev_height + 1)
+ if (height >= range_begin && height < range_end)
{
- MDB_val k2;
- result = mdb_cursor_get(m_cur_block_info, &k2, &v, MDB_NEXT);
+ // nohting to do
}
else
{
- v.mv_size = sizeof(uint64_t);
- v.mv_data = (void*)&height;
- result = mdb_cursor_get(m_cur_block_info, (MDB_val *)&zerokval, &v, MDB_GET_BOTH);
+ if (height == prev_height + 1)
+ {
+ MDB_val k2;
+ result = mdb_cursor_get(m_cur_block_info, &k2, &v, MDB_NEXT_MULTIPLE);
+ range_begin = ((const mdb_block_info*)v.mv_data)->bi_height;
+ range_end = range_begin + v.mv_size / sizeof(mdb_block_info); // whole records please
+ if (height < range_begin || height >= range_end)
+ throw0(DB_ERROR(("Height " + std::to_string(height) + " not included in multuple record range: " + std::to_string(range_begin) + "-" + std::to_string(range_end)).c_str()));
+ }
+ else
+ {
+ v.mv_size = sizeof(uint64_t);
+ v.mv_data = (void*)&height;
+ result = mdb_cursor_get(m_cur_block_info, (MDB_val *)&zerokval, &v, MDB_GET_BOTH);
+ range_begin = height;
+ range_end = range_begin + 1;
+ }
+ if (result)
+ throw0(DB_ERROR(lmdb_error("Error attempting to retrieve rct distribution from the db: ", result).c_str()));
}
- if (result)
- throw0(DB_ERROR(lmdb_error("Error attempting to retrieve rct distribution from the db: ", result).c_str()));
- const mdb_block_info *bi = (const mdb_block_info *)v.mv_data;
+ const mdb_block_info *bi = ((const mdb_block_info *)v.mv_data) + (height - range_begin);
res.push_back(bi->bi_cum_rct);
prev_height = height;
}
diff --git a/src/blockchain_utilities/CMakeLists.txt b/src/blockchain_utilities/CMakeLists.txt
index 24a750eb0..6e6e4c6f1 100644
--- a/src/blockchain_utilities/CMakeLists.txt
+++ b/src/blockchain_utilities/CMakeLists.txt
@@ -26,20 +26,6 @@
# STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
# THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-set(blocksdat "")
-if(PER_BLOCK_CHECKPOINT)
- if(APPLE AND DEPENDS)
- add_custom_command(OUTPUT blocksdat.o MAIN_DEPENDENCY ../blocks/checkpoints.dat COMMAND cd ${CMAKE_CURRENT_SOURCE_DIR} && touch stub.c && ${CMAKE_C_COMPILER} --target=x86_64-apple-darwin11 -o stub.o -c stub.c COMMAND cd ${CMAKE_CURRENT_SOURCE_DIR} && ${CMAKE_LINKER} -r -sectcreate __DATA __blocks_dat ../blocks/checkpoints.dat -o ${CMAKE_CURRENT_BINARY_DIR}/blocksdat.o stub.o && rm -f stub.*)
- elseif(APPLE AND NOT DEPENDS)
- add_custom_command(OUTPUT blocksdat.o MAIN_DEPENDENCY ../blocks/checkpoints.dat COMMAND cd ${CMAKE_CURRENT_SOURCE_DIR} && touch stub.c && ${CMAKE_C_COMPILER} -o stub.o -c stub.c COMMAND cd ${CMAKE_CURRENT_SOURCE_DIR} && ${CMAKE_LINKER} ${LD_RAW_FLAGS} -r -sectcreate __DATA __blocks_dat ../blocks/checkpoints.dat -o ${CMAKE_CURRENT_BINARY_DIR}/blocksdat.o stub.o && rm -f stub.*)
- elseif(LINUX_32)
- add_custom_command(OUTPUT blocksdat.o MAIN_DEPENDENCY ../blocks/checkpoints.dat COMMAND cd ${CMAKE_CURRENT_SOURCE_DIR} && cp ../blocks/checkpoints.dat blocks.dat && ${CMAKE_LINKER} -m elf_i386 ${LD_RAW_FLAGS} -r -b binary -o ${CMAKE_CURRENT_BINARY_DIR}/blocksdat.o blocks.dat && rm -f blocks.dat)
- else()
- add_custom_command(OUTPUT blocksdat.o MAIN_DEPENDENCY ../blocks/checkpoints.dat COMMAND cd ${CMAKE_CURRENT_SOURCE_DIR} && cp ../blocks/checkpoints.dat blocks.dat && ${CMAKE_LINKER} ${LD_RAW_FLAGS} -r -b binary -o ${CMAKE_CURRENT_BINARY_DIR}/blocksdat.o blocks.dat && rm -f blocks.dat)
- endif()
- set(blocksdat "blocksdat.o")
-endif()
-
set(blockchain_import_sources
blockchain_import.cpp
bootstrap_file.cpp
@@ -119,8 +105,7 @@ monero_private_headers(blockchain_depth
monero_add_executable(blockchain_import
${blockchain_import_sources}
- ${blockchain_import_private_headers}
- ${blocksdat})
+ ${blockchain_import_private_headers})
target_link_libraries(blockchain_import
PRIVATE
@@ -132,7 +117,8 @@ target_link_libraries(blockchain_import
${Boost_SYSTEM_LIBRARY}
${Boost_THREAD_LIBRARY}
${CMAKE_THREAD_LIBS_INIT}
- ${EXTRA_LIBRARIES})
+ ${EXTRA_LIBRARIES}
+ ${Blocks})
if(ARCH_WIDTH)
target_compile_definitions(blockchain_import
diff --git a/src/blockchain_utilities/blockchain_import.cpp b/src/blockchain_utilities/blockchain_import.cpp
index 9ec768d26..7f92ecd87 100644
--- a/src/blockchain_utilities/blockchain_import.cpp
+++ b/src/blockchain_utilities/blockchain_import.cpp
@@ -37,6 +37,7 @@
#include "misc_log_ex.h"
#include "bootstrap_file.h"
#include "bootstrap_serialization.h"
+#include "blocks/blocks.h"
#include "cryptonote_basic/cryptonote_format_utils.h"
#include "serialization/binary_utils.h" // dump_binary(), parse_binary()
#include "serialization/json_utils.h" // dump_json()
@@ -758,7 +759,12 @@ int main(int argc, char* argv[])
{
core.disable_dns_checkpoints(true);
- if (!core.init(vm, NULL))
+#if defined(PER_BLOCK_CHECKPOINT)
+ const GetCheckpointsCallback& get_checkpoints = blocks::GetCheckpointsData;
+#else
+ const GetCheckpointsCallback& get_checkpoints = nullptr;
+#endif
+ if (!core.init(vm, nullptr, nullptr, get_checkpoints))
{
std::cerr << "Failed to initialize core" << ENDL;
return 1;
diff --git a/src/blocks/CMakeLists.txt b/src/blocks/CMakeLists.txt
index ebb5408cc..30d85adbf 100644
--- a/src/blocks/CMakeLists.txt
+++ b/src/blocks/CMakeLists.txt
@@ -26,20 +26,23 @@
# STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
# THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-if(APPLE)
- add_library(blocks STATIC blockexports.c)
- set_target_properties(blocks PROPERTIES LINKER_LANGUAGE C)
-else()
- if(LINUX_32)
- add_custom_command(OUTPUT blocks.o MAIN_DEPENDENCY blocks.dat COMMAND cd ${CMAKE_CURRENT_SOURCE_DIR} && ${CMAKE_LINKER} -m elf_i386 ${LD_RAW_FLAGS} -r -b binary -o ${CMAKE_CURRENT_BINARY_DIR}/blocks.o blocks.dat)
- add_custom_command(OUTPUT testnet_blocks.o MAIN_DEPENDENCY testnet_blocks.dat COMMAND cd ${CMAKE_CURRENT_SOURCE_DIR} && ${CMAKE_LINKER} -m elf_i386 ${LD_RAW_FLAGS} -r -b binary -o ${CMAKE_CURRENT_BINARY_DIR}/testnet_blocks.o testnet_blocks.dat)
- add_custom_command(OUTPUT stagenet_blocks.o MAIN_DEPENDENCY stagenet_blocks.dat COMMAND cd ${CMAKE_CURRENT_SOURCE_DIR} && ${CMAKE_LINKER} -m elf_i386 ${LD_RAW_FLAGS} -r -b binary -o ${CMAKE_CURRENT_BINARY_DIR}/stagenet_blocks.o stagenet_blocks.dat)
- else()
- add_custom_command(OUTPUT blocks.o MAIN_DEPENDENCY blocks.dat COMMAND cd ${CMAKE_CURRENT_SOURCE_DIR} && ${CMAKE_LINKER} ${LD_RAW_FLAGS} -r -b binary -o ${CMAKE_CURRENT_BINARY_DIR}/blocks.o blocks.dat)
- add_custom_command(OUTPUT testnet_blocks.o MAIN_DEPENDENCY testnet_blocks.dat COMMAND cd ${CMAKE_CURRENT_SOURCE_DIR} && ${CMAKE_LINKER} ${LD_RAW_FLAGS} -r -b binary -o ${CMAKE_CURRENT_BINARY_DIR}/testnet_blocks.o testnet_blocks.dat)
- add_custom_command(OUTPUT stagenet_blocks.o MAIN_DEPENDENCY stagenet_blocks.dat COMMAND cd ${CMAKE_CURRENT_SOURCE_DIR} && ${CMAKE_LINKER} ${LD_RAW_FLAGS} -r -b binary -o ${CMAKE_CURRENT_BINARY_DIR}/stagenet_blocks.o stagenet_blocks.dat)
- endif()
- add_library(blocks STATIC blocks.o testnet_blocks.o stagenet_blocks.o blockexports.c)
- set_target_properties(blocks PROPERTIES LINKER_LANGUAGE C)
-endif()
+set(GENERATED_SOURCES "")
+foreach(BLOB_NAME checkpoints testnet_blocks stagenet_blocks)
+ set(OUTPUT_C_SOURCE "generated_${BLOB_NAME}.c")
+ list(APPEND GENERATED_SOURCES ${OUTPUT_C_SOURCE})
+ set(INPUT_DAT_FILE "${BLOB_NAME}.dat")
+ add_custom_command(
+ OUTPUT ${OUTPUT_C_SOURCE}
+ MAIN_DEPENDENCY ${INPUT_DAT_FILE}
+ COMMAND
+ cd ${CMAKE_CURRENT_BINARY_DIR} &&
+ echo "'#include\t<stddef.h>'" > ${OUTPUT_C_SOURCE} &&
+ echo "'const\tunsigned\tchar\t${BLOB_NAME}[]={'" >> ${OUTPUT_C_SOURCE} &&
+ od -v -An -tu1 ${CMAKE_CURRENT_SOURCE_DIR}/${INPUT_DAT_FILE} | sed -e "'s/[0-9]\\{1,\\}/&,/g'" -e "'$$s/.$$//'" >> ${OUTPUT_C_SOURCE} &&
+ echo "'};'" >> ${OUTPUT_C_SOURCE} &&
+ echo "'const\tsize_t\t${BLOB_NAME}_len\t=\tsizeof(${BLOB_NAME});'" >> ${OUTPUT_C_SOURCE}
+ )
+endforeach()
+
+add_library(blocks STATIC blocks.cpp ${GENERATED_SOURCES})
diff --git a/src/blocks/blockexports.c b/src/blocks/blockexports.c
deleted file mode 100644
index 0154b0413..000000000
--- a/src/blocks/blockexports.c
+++ /dev/null
@@ -1,87 +0,0 @@
-#include <stddef.h>
-
-#if defined(__APPLE__)
-#include <mach-o/getsect.h>
-#ifdef BUILD_SHARED_LIBS
-#if !defined(__LP64__)
-const struct mach_header _mh_execute_header;
-#else
-const struct mach_header_64 _mh_execute_header;
-#endif
-#else
-#if !defined(__LP64__)
-extern const struct mach_header _mh_execute_header;
-#else
-extern const struct mach_header_64 _mh_execute_header;
-#endif
-#endif
-
-const unsigned char *get_blocks_dat_start(int testnet, int stagenet)
-{
- size_t size;
- if (testnet)
- return getsectiondata(&_mh_execute_header, "__DATA", "__testnet_blocks_dat", &size);
- else if (stagenet)
- return getsectiondata(&_mh_execute_header, "__DATA", "__stagenet_blocks_dat", &size);
- else
- return getsectiondata(&_mh_execute_header, "__DATA", "__blocks_dat", &size);
-}
-
-size_t get_blocks_dat_size(int testnet, int stagenet)
-{
- size_t size;
- if (testnet)
- getsectiondata(&_mh_execute_header, "__DATA", "__testnet_blocks_dat", &size);
- else if (stagenet)
- getsectiondata(&_mh_execute_header, "__DATA", "__stagenet_blocks_dat", &size);
- else
- getsectiondata(&_mh_execute_header, "__DATA", "__blocks_dat", &size);
- return size;
-}
-
-#else
-
-#if defined(_WIN32) && !defined(_WIN64)
-#define _binary_blocks_start binary_blocks_dat_start
-#define _binary_blocks_end binary_blocks_dat_end
-#define _binary_testnet_blocks_start binary_testnet_blocks_dat_start
-#define _binary_testnet_blocks_end binary_testnet_blocks_dat_end
-#define _binary_stagenet_blocks_start binary_stagenet_blocks_dat_start
-#define _binary_stagenet_blocks_end binary_stagenet_blocks_dat_end
-#else
-#define _binary_blocks_start _binary_blocks_dat_start
-#define _binary_blocks_end _binary_blocks_dat_end
-#define _binary_testnet_blocks_start _binary_testnet_blocks_dat_start
-#define _binary_testnet_blocks_end _binary_testnet_blocks_dat_end
-#define _binary_stagenet_blocks_start _binary_stagenet_blocks_dat_start
-#define _binary_stagenet_blocks_end _binary_stagenet_blocks_dat_end
-#endif
-
-extern const unsigned char _binary_blocks_start[];
-extern const unsigned char _binary_blocks_end[];
-extern const unsigned char _binary_testnet_blocks_start[];
-extern const unsigned char _binary_testnet_blocks_end[];
-extern const unsigned char _binary_stagenet_blocks_start[];
-extern const unsigned char _binary_stagenet_blocks_end[];
-
-const unsigned char *get_blocks_dat_start(int testnet, int stagenet)
-{
- if (testnet)
- return _binary_testnet_blocks_start;
- else if (stagenet)
- return _binary_stagenet_blocks_start;
- else
- return _binary_blocks_start;
-}
-
-size_t get_blocks_dat_size(int testnet, int stagenet)
-{
- if (testnet)
- return (size_t) (_binary_testnet_blocks_end - _binary_testnet_blocks_start);
- else if (stagenet)
- return (size_t) (_binary_stagenet_blocks_end - _binary_stagenet_blocks_start);
- else
- return (size_t) (_binary_blocks_end - _binary_blocks_start);
-}
-
-#endif
diff --git a/src/blocks/blocks.cpp b/src/blocks/blocks.cpp
new file mode 100644
index 000000000..0661f8448
--- /dev/null
+++ b/src/blocks/blocks.cpp
@@ -0,0 +1,31 @@
+#include "blocks.h"
+
+#include <unordered_map>
+
+extern const unsigned char checkpoints[];
+extern const size_t checkpoints_len;
+extern const unsigned char stagenet_blocks[];
+extern const size_t stagenet_blocks_len;
+extern const unsigned char testnet_blocks[];
+extern const size_t testnet_blocks_len;
+
+namespace blocks
+{
+
+ const std::unordered_map<cryptonote::network_type, const epee::span<const unsigned char>, std::hash<size_t>> CheckpointsByNetwork = {
+ {cryptonote::network_type::MAINNET, {checkpoints, checkpoints_len}},
+ {cryptonote::network_type::STAGENET, {stagenet_blocks, stagenet_blocks_len}},
+ {cryptonote::network_type::TESTNET, {testnet_blocks, testnet_blocks_len}}
+ };
+
+ const epee::span<const unsigned char> GetCheckpointsData(cryptonote::network_type network)
+ {
+ const auto it = CheckpointsByNetwork.find(network);
+ if (it != CheckpointsByNetwork.end())
+ {
+ return it->second;
+ }
+ return nullptr;
+ }
+
+}
diff --git a/src/blocks/blocks.dat b/src/blocks/blocks.dat
deleted file mode 100644
index e69de29bb..000000000
--- a/src/blocks/blocks.dat
+++ /dev/null
diff --git a/src/blocks/blocks.h b/src/blocks/blocks.h
index ec683f47e..14e391319 100644
--- a/src/blocks/blocks.h
+++ b/src/blocks/blocks.h
@@ -1,16 +1,12 @@
#ifndef SRC_BLOCKS_BLOCKS_H_
#define SRC_BLOCKS_BLOCKS_H_
-#ifdef __cplusplus
-extern "C" {
-#endif
+#include "cryptonote_config.h"
+#include "span.h"
-const unsigned char *get_blocks_dat_start(int testnet, int stagenet);
-size_t get_blocks_dat_size(int testnet, int stagenet);
-
-#ifdef __cplusplus
+namespace blocks
+{
+ const epee::span<const unsigned char> GetCheckpointsData(cryptonote::network_type network);
}
-#endif
-
#endif /* SRC_BLOCKS_BLOCKS_H_ */
diff --git a/src/common/perf_timer.cpp b/src/common/perf_timer.cpp
index 6910ebdd4..47f01de65 100644
--- a/src/common/perf_timer.cpp
+++ b/src/common/perf_timer.cpp
@@ -104,11 +104,11 @@ PerformanceTimer::PerformanceTimer(bool paused): started(true), paused(paused)
ticks = get_tick_count();
}
-LoggingPerformanceTimer::LoggingPerformanceTimer(const std::string &s, uint64_t unit, el::Level l): PerformanceTimer(), name(s), unit(unit), level(l)
+LoggingPerformanceTimer::LoggingPerformanceTimer(const std::string &s, const std::string &cat, uint64_t unit, el::Level l): PerformanceTimer(), name(s), cat(cat), unit(unit), level(l)
{
if (!performance_timers)
{
- MLOG(level, "PERF ----------");
+ MCLOG(level, cat.c_str(), "PERF ----------");
performance_timers = new std::vector<LoggingPerformanceTimer*>();
}
else
@@ -117,7 +117,7 @@ LoggingPerformanceTimer::LoggingPerformanceTimer(const std::string &s, uint64_t
if (!pt->started && !pt->paused)
{
size_t size = 0; for (const auto *tmp: *performance_timers) if (!tmp->paused) ++size;
- MLOG(pt->level, "PERF " << std::string((size-1) * 2, ' ') << " " << pt->name);
+ MCLOG(pt->level, cat.c_str(), "PERF " << std::string((size-1) * 2, ' ') << " " << pt->name);
pt->started = true;
}
}
@@ -137,7 +137,7 @@ LoggingPerformanceTimer::~LoggingPerformanceTimer()
char s[12];
snprintf(s, sizeof(s), "%8llu ", (unsigned long long)(ticks_to_ns(ticks) / (1000000000 / unit)));
size_t size = 0; for (const auto *tmp: *performance_timers) if (!tmp->paused || tmp==this) ++size;
- MLOG(level, "PERF " << s << std::string(size * 2, ' ') << " " << name);
+ MCLOG(level, cat.c_str(), "PERF " << s << std::string(size * 2, ' ') << " " << name);
if (performance_timers->empty())
{
delete performance_timers;
diff --git a/src/common/perf_timer.h b/src/common/perf_timer.h
index 675d6234d..1d4dee5b5 100644
--- a/src/common/perf_timer.h
+++ b/src/common/perf_timer.h
@@ -33,9 +33,6 @@
#include <memory>
#include "misc_log_ex.h"
-#undef MONERO_DEFAULT_LOG_CATEGORY
-#define MONERO_DEFAULT_LOG_CATEGORY "perf"
-
namespace tools
{
@@ -67,23 +64,24 @@ protected:
class LoggingPerformanceTimer: public PerformanceTimer
{
public:
- LoggingPerformanceTimer(const std::string &s, uint64_t unit, el::Level l = el::Level::Debug);
+ LoggingPerformanceTimer(const std::string &s, const std::string &cat, uint64_t unit, el::Level l = el::Level::Debug);
~LoggingPerformanceTimer();
private:
std::string name;
+ std::string cat;
uint64_t unit;
el::Level level;
};
void set_performance_timer_log_level(el::Level level);
-#define PERF_TIMER_UNIT(name, unit) tools::LoggingPerformanceTimer pt_##name(#name, unit, tools::performance_timer_log_level)
-#define PERF_TIMER_UNIT_L(name, unit, l) tools::LoggingPerformanceTimer pt_##name(#name, unit, l)
-#define PERF_TIMER(name) PERF_TIMER_UNIT(name, 1000)
-#define PERF_TIMER_L(name, l) PERF_TIMER_UNIT_L(name, 1000, l)
-#define PERF_TIMER_START_UNIT(name, unit) std::unique_ptr<tools::LoggingPerformanceTimer> pt_##name(new tools::LoggingPerformanceTimer(#name, unit, el::Level::Info))
-#define PERF_TIMER_START(name) PERF_TIMER_START_UNIT(name, 1000)
+#define PERF_TIMER_UNIT(name, unit) tools::LoggingPerformanceTimer pt_##name(#name, "perf." MONERO_DEFAULT_LOG_CATEGORY, unit, tools::performance_timer_log_level)
+#define PERF_TIMER_UNIT_L(name, unit, l) tools::LoggingPerformanceTimer pt_##name(#name, "perf." MONERO_DEFAULT_LOG_CATEGORY, unit, l)
+#define PERF_TIMER(name) PERF_TIMER_UNIT(name, 1000000)
+#define PERF_TIMER_L(name, l) PERF_TIMER_UNIT_L(name, 1000000, l)
+#define PERF_TIMER_START_UNIT(name, unit) std::unique_ptr<tools::LoggingPerformanceTimer> pt_##name(new tools::LoggingPerformanceTimer(#name, "perf." MONERO_DEFAULT_LOG_CATEGORY, unit, el::Level::Info))
+#define PERF_TIMER_START(name) PERF_TIMER_START_UNIT(name, 1000000)
#define PERF_TIMER_STOP(name) do { pt_##name.reset(NULL); } while(0)
#define PERF_TIMER_PAUSE(name) pt_##name->pause()
#define PERF_TIMER_RESUME(name) pt_##name->resume()
diff --git a/src/common/util.cpp b/src/common/util.cpp
index f91230528..43973c511 100644
--- a/src/common/util.cpp
+++ b/src/common/util.cpp
@@ -308,10 +308,19 @@ namespace tools
StringCchCopy(pszOS, BUFSIZE, TEXT("Microsoft "));
// Test for the specific product.
+ if ( osvi.dwMajorVersion == 10 )
+ {
+ if ( osvi.dwMinorVersion == 0 )
+ {
+ if( osvi.wProductType == VER_NT_WORKSTATION )
+ StringCchCat(pszOS, BUFSIZE, TEXT("Windows 10 "));
+ else StringCchCat(pszOS, BUFSIZE, TEXT("Windows Server 2016 " ));
+ }
+ }
if ( osvi.dwMajorVersion == 6 )
{
- if( osvi.dwMinorVersion == 0 )
+ if ( osvi.dwMinorVersion == 0 )
{
if( osvi.wProductType == VER_NT_WORKSTATION )
StringCchCat(pszOS, BUFSIZE, TEXT("Windows Vista "));
@@ -325,6 +334,20 @@ namespace tools
else StringCchCat(pszOS, BUFSIZE, TEXT("Windows Server 2008 R2 " ));
}
+ if ( osvi.dwMinorVersion == 2 )
+ {
+ if( osvi.wProductType == VER_NT_WORKSTATION )
+ StringCchCat(pszOS, BUFSIZE, TEXT("Windows 8 "));
+ else StringCchCat(pszOS, BUFSIZE, TEXT("Windows Server 2012 " ));
+ }
+
+ if ( osvi.dwMinorVersion == 3 )
+ {
+ if( osvi.wProductType == VER_NT_WORKSTATION )
+ StringCchCat(pszOS, BUFSIZE, TEXT("Windows 8.1 "));
+ else StringCchCat(pszOS, BUFSIZE, TEXT("Windows Server 2012 R2 " ));
+ }
+
pGPI = (PGPI) GetProcAddress(
GetModuleHandle(TEXT("kernel32.dll")),
"GetProductInfo");
diff --git a/src/cryptonote_basic/account.cpp b/src/cryptonote_basic/account.cpp
index e891a748d..edbc2c561 100644
--- a/src/cryptonote_basic/account.cpp
+++ b/src/cryptonote_basic/account.cpp
@@ -136,6 +136,16 @@ DISABLE_VS_WARNINGS(4244 4345)
void account_base::set_null()
{
m_keys = account_keys();
+ m_creation_timestamp = 0;
+ }
+ //-----------------------------------------------------------------
+ void account_base::deinit()
+ {
+ try{
+ m_keys.get_device().disconnect();
+ } catch (const std::exception &e){
+ MERROR("Device disconnect exception: " << e.what());
+ }
}
//-----------------------------------------------------------------
void account_base::forget_spend_key()
@@ -205,11 +215,16 @@ DISABLE_VS_WARNINGS(4244 4345)
void account_base::create_from_device(hw::device &hwdev)
{
m_keys.set_device(hwdev);
- MCDEBUG("ledger", "device type: "<<typeid(hwdev).name());
- hwdev.init();
- hwdev.connect();
- hwdev.get_public_address(m_keys.m_account_address);
- hwdev.get_secret_keys(m_keys.m_view_secret_key, m_keys.m_spend_secret_key);
+ MCDEBUG("device", "device type: "<<typeid(hwdev).name());
+ CHECK_AND_ASSERT_THROW_MES(hwdev.init(), "Device init failed");
+ CHECK_AND_ASSERT_THROW_MES(hwdev.connect(), "Device connect failed");
+ try {
+ CHECK_AND_ASSERT_THROW_MES(hwdev.get_public_address(m_keys.m_account_address), "Cannot get a device address");
+ CHECK_AND_ASSERT_THROW_MES(hwdev.get_secret_keys(m_keys.m_view_secret_key, m_keys.m_spend_secret_key), "Cannot get device secret");
+ } catch (const std::exception &e){
+ hwdev.disconnect();
+ throw;
+ }
struct tm timestamp = {0};
timestamp.tm_year = 2014 - 1900; // year 2014
timestamp.tm_mon = 4 - 1; // month april
diff --git a/src/cryptonote_basic/account.h b/src/cryptonote_basic/account.h
index 98bba55b1..021f84029 100644
--- a/src/cryptonote_basic/account.h
+++ b/src/cryptonote_basic/account.h
@@ -89,6 +89,7 @@ namespace cryptonote
hw::device& get_device() const {return m_keys.get_device();}
void set_device( hw::device &hwdev) {m_keys.set_device(hwdev);}
+ void deinit();
uint64_t get_createtime() const { return m_creation_timestamp; }
void set_createtime(uint64_t val) { m_creation_timestamp = val; }
diff --git a/src/cryptonote_basic/cryptonote_basic.h b/src/cryptonote_basic/cryptonote_basic.h
index d4558ef7b..b0eabb0aa 100644
--- a/src/cryptonote_basic/cryptonote_basic.h
+++ b/src/cryptonote_basic/cryptonote_basic.h
@@ -47,7 +47,6 @@
#include "crypto/crypto.h"
#include "crypto/hash.h"
#include "misc_language.h"
-#include "tx_extra.h"
#include "ringct/rctTypes.h"
#include "device/device.hpp"
diff --git a/src/cryptonote_basic/cryptonote_format_utils.cpp b/src/cryptonote_basic/cryptonote_format_utils.cpp
index 9e9c12605..e26aac76b 100644
--- a/src/cryptonote_basic/cryptonote_format_utils.cpp
+++ b/src/cryptonote_basic/cryptonote_format_utils.cpp
@@ -445,6 +445,91 @@ namespace cryptonote
return true;
}
//---------------------------------------------------------------
+ template<typename T>
+ static bool pick(binary_archive<true> &ar, std::vector<tx_extra_field> &fields, uint8_t tag)
+ {
+ std::vector<tx_extra_field>::iterator it;
+ while ((it = std::find_if(fields.begin(), fields.end(), [](const tx_extra_field &f) { return f.type() == typeid(T); })) != fields.end())
+ {
+ bool r = ::do_serialize(ar, tag);
+ CHECK_AND_NO_ASSERT_MES_L1(r, false, "failed to serialize tx extra field");
+ r = ::do_serialize(ar, boost::get<T>(*it));
+ CHECK_AND_NO_ASSERT_MES_L1(r, false, "failed to serialize tx extra field");
+ fields.erase(it);
+ }
+ return true;
+ }
+ //---------------------------------------------------------------
+ bool sort_tx_extra(const std::vector<uint8_t>& tx_extra, std::vector<uint8_t> &sorted_tx_extra, bool allow_partial)
+ {
+ std::vector<tx_extra_field> tx_extra_fields;
+
+ if(tx_extra.empty())
+ {
+ sorted_tx_extra.clear();
+ return true;
+ }
+
+ std::string extra_str(reinterpret_cast<const char*>(tx_extra.data()), tx_extra.size());
+ std::istringstream iss(extra_str);
+ binary_archive<false> ar(iss);
+
+ bool eof = false;
+ size_t processed = 0;
+ while (!eof)
+ {
+ tx_extra_field field;
+ bool r = ::do_serialize(ar, field);
+ if (!r)
+ {
+ MWARNING("failed to deserialize extra field. extra = " << string_tools::buff_to_hex_nodelimer(std::string(reinterpret_cast<const char*>(tx_extra.data()), tx_extra.size())));
+ if (!allow_partial)
+ return false;
+ break;
+ }
+ tx_extra_fields.push_back(field);
+ processed = iss.tellg();
+
+ std::ios_base::iostate state = iss.rdstate();
+ eof = (EOF == iss.peek());
+ iss.clear(state);
+ }
+ if (!::serialization::check_stream_state(ar))
+ {
+ MWARNING("failed to deserialize extra field. extra = " << string_tools::buff_to_hex_nodelimer(std::string(reinterpret_cast<const char*>(tx_extra.data()), tx_extra.size())));
+ if (!allow_partial)
+ return false;
+ }
+ MTRACE("Sorted " << processed << "/" << tx_extra.size());
+
+ std::ostringstream oss;
+ binary_archive<true> nar(oss);
+
+ // sort by:
+ if (!pick<tx_extra_pub_key>(nar, tx_extra_fields, TX_EXTRA_TAG_PUBKEY)) return false;
+ if (!pick<tx_extra_additional_pub_keys>(nar, tx_extra_fields, TX_EXTRA_TAG_ADDITIONAL_PUBKEYS)) return false;
+ if (!pick<tx_extra_nonce>(nar, tx_extra_fields, TX_EXTRA_NONCE)) return false;
+ if (!pick<tx_extra_merge_mining_tag>(nar, tx_extra_fields, TX_EXTRA_MERGE_MINING_TAG)) return false;
+ if (!pick<tx_extra_mysterious_minergate>(nar, tx_extra_fields, TX_EXTRA_MYSTERIOUS_MINERGATE_TAG)) return false;
+ if (!pick<tx_extra_padding>(nar, tx_extra_fields, TX_EXTRA_TAG_PADDING)) return false;
+
+ // if not empty, someone added a new type and did not add a case above
+ if (!tx_extra_fields.empty())
+ {
+ MERROR("tx_extra_fields not empty after sorting, someone forgot to add a case above");
+ return false;
+ }
+
+ std::string oss_str = oss.str();
+ if (allow_partial && processed < tx_extra.size())
+ {
+ MDEBUG("Appending unparsed data");
+ oss_str += std::string((const char*)tx_extra.data() + processed, tx_extra.size() - processed);
+ }
+ sorted_tx_extra = std::vector<uint8_t>(oss_str.begin(), oss_str.end());
+ return true;
+ }
+ //---------------------------------------------------------------
crypto::public_key get_tx_pub_key_from_extra(const std::vector<uint8_t>& tx_extra, size_t pk_index)
{
std::vector<tx_extra_field> tx_extra_fields;
diff --git a/src/cryptonote_basic/cryptonote_format_utils.h b/src/cryptonote_basic/cryptonote_format_utils.h
index 725c75f4e..8d33b1ca4 100644
--- a/src/cryptonote_basic/cryptonote_format_utils.h
+++ b/src/cryptonote_basic/cryptonote_format_utils.h
@@ -31,6 +31,7 @@
#pragma once
#include "blobdatatype.h"
#include "cryptonote_basic_impl.h"
+#include "tx_extra.h"
#include "account.h"
#include "subaddress_index.h"
#include "include_base_utils.h"
@@ -65,6 +66,7 @@ namespace cryptonote
}
bool parse_tx_extra(const std::vector<uint8_t>& tx_extra, std::vector<tx_extra_field>& tx_extra_fields);
+ bool sort_tx_extra(const std::vector<uint8_t>& tx_extra, std::vector<uint8_t> &sorted_tx_extra, bool allow_partial = false);
crypto::public_key get_tx_pub_key_from_extra(const std::vector<uint8_t>& tx_extra, size_t pk_index = 0);
crypto::public_key get_tx_pub_key_from_extra(const transaction_prefix& tx, size_t pk_index = 0);
crypto::public_key get_tx_pub_key_from_extra(const transaction& tx, size_t pk_index = 0);
diff --git a/src/cryptonote_basic/miner.cpp b/src/cryptonote_basic/miner.cpp
index d0b03593e..d8ca2dd35 100644
--- a/src/cryptonote_basic/miner.cpp
+++ b/src/cryptonote_basic/miner.cpp
@@ -637,7 +637,7 @@ namespace cryptonote
boost::tribool battery_powered(on_battery_power());
if(!indeterminate( battery_powered ))
{
- on_ac_power = !battery_powered;
+ on_ac_power = !(bool)battery_powered;
}
}
diff --git a/src/cryptonote_config.h b/src/cryptonote_config.h
index a6858ce7c..c62eeb738 100644
--- a/src/cryptonote_config.h
+++ b/src/cryptonote_config.h
@@ -30,6 +30,7 @@
#pragma once
+#include <stdexcept>
#include <string>
#include <boost/uuid/uuid.hpp>
diff --git a/src/cryptonote_core/CMakeLists.txt b/src/cryptonote_core/CMakeLists.txt
index 72844db66..231489fdb 100644
--- a/src/cryptonote_core/CMakeLists.txt
+++ b/src/cryptonote_core/CMakeLists.txt
@@ -41,12 +41,6 @@ set(cryptonote_core_private_headers
tx_pool.h
cryptonote_tx_utils.h)
-if(PER_BLOCK_CHECKPOINT)
- set(Blocks "blocks")
-else()
- set(Blocks "")
-endif()
-
monero_private_headers(cryptonote_core
${cryptonote_core_private_headers})
monero_add_library(cryptonote_core
@@ -69,5 +63,4 @@ target_link_libraries(cryptonote_core
${Boost_SYSTEM_LIBRARY}
${Boost_THREAD_LIBRARY}
PRIVATE
- ${Blocks}
${EXTRA_LIBRARIES})
diff --git a/src/cryptonote_core/blockchain.cpp b/src/cryptonote_core/blockchain.cpp
index 1ec2366e4..77b6d0b69 100644
--- a/src/cryptonote_core/blockchain.cpp
+++ b/src/cryptonote_core/blockchain.cpp
@@ -53,9 +53,6 @@
#include "ringct/rctSigs.h"
#include "common/perf_timer.h"
#include "common/notify.h"
-#if defined(PER_BLOCK_CHECKPOINT)
-#include "blocks/blocks.h"
-#endif
#undef MONERO_DEFAULT_LOG_CATEGORY
#define MONERO_DEFAULT_LOG_CATEGORY "blockchain"
@@ -341,7 +338,7 @@ uint64_t Blockchain::get_current_blockchain_height() const
//------------------------------------------------------------------
//FIXME: possibly move this into the constructor, to avoid accidentally
// dereferencing a null BlockchainDB pointer
-bool Blockchain::init(BlockchainDB* db, const network_type nettype, bool offline, const cryptonote::test_options *test_options, difficulty_type fixed_difficulty)
+bool Blockchain::init(BlockchainDB* db, const network_type nettype, bool offline, const cryptonote::test_options *test_options, difficulty_type fixed_difficulty, const GetCheckpointsCallback& get_checkpoints/* = nullptr*/)
{
LOG_PRINT_L3("Blockchain::" << __func__);
@@ -442,7 +439,7 @@ bool Blockchain::init(BlockchainDB* db, const network_type nettype, bool offline
#if defined(PER_BLOCK_CHECKPOINT)
if (m_nettype != FAKECHAIN)
- load_compiled_in_block_hashes();
+ load_compiled_in_block_hashes(get_checkpoints);
#endif
MINFO("Blockchain initialized. last block: " << m_db->height() - 1 << ", " << epee::misc_utils::get_time_interval_string(timestamp_diff) << " time ago, current difficulty: " << get_difficulty_for_next_block());
@@ -886,6 +883,15 @@ difficulty_type Blockchain::get_difficulty_for_next_block()
return diff;
}
//------------------------------------------------------------------
+std::vector<time_t> Blockchain::get_last_block_timestamps(unsigned int blocks) const
+{
+ std::vector<time_t> timestamps(blocks);
+ uint64_t height = m_db->height();
+ while (blocks--)
+ timestamps[blocks] = m_db->get_block_timestamp(height - blocks - 1);
+ return timestamps;
+}
+//------------------------------------------------------------------
// This function removes blocks from the blockchain until it gets to the
// position where the blockchain switch started and then re-adds the blocks
// that had been removed.
@@ -4407,19 +4413,21 @@ void Blockchain::cancel()
#if defined(PER_BLOCK_CHECKPOINT)
static const char expected_block_hashes_hash[] = "954cb2bbfa2fe6f74b2cdd22a1a4c767aea249ad47ad4f7c9445f0f03260f511";
-void Blockchain::load_compiled_in_block_hashes()
+void Blockchain::load_compiled_in_block_hashes(const GetCheckpointsCallback& get_checkpoints)
{
- const bool testnet = m_nettype == TESTNET;
- const bool stagenet = m_nettype == STAGENET;
- if (m_fast_sync && get_blocks_dat_start(testnet, stagenet) != nullptr && get_blocks_dat_size(testnet, stagenet) > 0)
+ if (get_checkpoints == nullptr || !m_fast_sync)
{
- MINFO("Loading precomputed blocks (" << get_blocks_dat_size(testnet, stagenet) << " bytes)");
-
+ return;
+ }
+ const epee::span<const unsigned char> &checkpoints = get_checkpoints(m_nettype);
+ if (!checkpoints.empty())
+ {
+ MINFO("Loading precomputed blocks (" << checkpoints.size() << " bytes)");
if (m_nettype == MAINNET)
{
// first check hash
crypto::hash hash;
- if (!tools::sha256sum(get_blocks_dat_start(testnet, stagenet), get_blocks_dat_size(testnet, stagenet), hash))
+ if (!tools::sha256sum(checkpoints.data(), checkpoints.size(), hash))
{
MERROR("Failed to hash precomputed blocks data");
return;
@@ -4439,9 +4447,9 @@ void Blockchain::load_compiled_in_block_hashes()
}
}
- if (get_blocks_dat_size(testnet, stagenet) > 4)
+ if (checkpoints.size() > 4)
{
- const unsigned char *p = get_blocks_dat_start(testnet, stagenet);
+ const unsigned char *p = checkpoints.data();
const uint32_t nblocks = *p | ((*(p+1))<<8) | ((*(p+2))<<16) | ((*(p+3))<<24);
if (nblocks > (std::numeric_limits<uint32_t>::max() - 4) / sizeof(hash))
{
@@ -4449,7 +4457,7 @@ void Blockchain::load_compiled_in_block_hashes()
return;
}
const size_t size_needed = 4 + nblocks * sizeof(crypto::hash);
- if(nblocks > 0 && nblocks > (m_db->height() + HASH_OF_HASHES_STEP - 1) / HASH_OF_HASHES_STEP && get_blocks_dat_size(testnet, stagenet) >= size_needed)
+ if(nblocks > 0 && nblocks > (m_db->height() + HASH_OF_HASHES_STEP - 1) / HASH_OF_HASHES_STEP && checkpoints.size() >= size_needed)
{
p += sizeof(uint32_t);
m_blocks_hash_of_hashes.reserve(nblocks);
diff --git a/src/cryptonote_core/blockchain.h b/src/cryptonote_core/blockchain.h
index ab66fac8b..f140d7719 100644
--- a/src/cryptonote_core/blockchain.h
+++ b/src/cryptonote_core/blockchain.h
@@ -38,9 +38,11 @@
#include <boost/multi_index/hashed_index.hpp>
#include <boost/multi_index/member.hpp>
#include <atomic>
+#include <functional>
#include <unordered_map>
#include <unordered_set>
+#include "span.h"
#include "syncobj.h"
#include "string_tools.h"
#include "cryptonote_basic/cryptonote_basic.h"
@@ -73,6 +75,15 @@ namespace cryptonote
db_nosync //!< Leave syncing up to the backing db (safest, but slowest because of disk I/O)
};
+ /**
+ * @brief Callback routine that returns checkpoints data for specific network type
+ *
+ * @param network network type
+ *
+ * @return checkpoints data, empty span if there ain't any checkpoints for specific network type
+ */
+ typedef std::function<const epee::span<const unsigned char>(cryptonote::network_type network)> GetCheckpointsCallback;
+
/************************************************************************/
/* */
/************************************************************************/
@@ -117,10 +128,11 @@ namespace cryptonote
* @param offline true if running offline, else false
* @param test_options test parameters
* @param fixed_difficulty fixed difficulty for testing purposes; 0 means disabled
+ * @param get_checkpoints if set, will be called to get checkpoints data
*
* @return true on success, false if any initialization steps fail
*/
- bool init(BlockchainDB* db, const network_type nettype = MAINNET, bool offline = false, const cryptonote::test_options *test_options = NULL, difficulty_type fixed_difficulty = 0);
+ bool init(BlockchainDB* db, const network_type nettype = MAINNET, bool offline = false, const cryptonote::test_options *test_options = NULL, difficulty_type fixed_difficulty = 0, const GetCheckpointsCallback& get_checkpoints = nullptr);
/**
* @brief Initialize the Blockchain state
@@ -952,6 +964,11 @@ namespace cryptonote
*/
void on_new_tx_from_block(const cryptonote::transaction &tx);
+ /**
+ * @brief returns the timestamps of the last N blocks
+ */
+ std::vector<time_t> get_last_block_timestamps(unsigned int blocks) const;
+
private:
// TODO: evaluate whether or not each of these typedefs are left over from blockchain_storage
@@ -1369,8 +1386,10 @@ namespace cryptonote
* A (possibly empty) set of block hashes can be compiled into the
* monero daemon binary. This function loads those hashes into
* a useful state.
+ *
+ * @param get_checkpoints if set, will be called to get checkpoints data
*/
- void load_compiled_in_block_hashes();
+ void load_compiled_in_block_hashes(const GetCheckpointsCallback& get_checkpoints);
/**
* @brief expands v2 transaction data from blockchain
diff --git a/src/cryptonote_core/cryptonote_core.cpp b/src/cryptonote_core/cryptonote_core.cpp
index 2fec6b613..4b806c282 100644
--- a/src/cryptonote_core/cryptonote_core.cpp
+++ b/src/cryptonote_core/cryptonote_core.cpp
@@ -389,7 +389,7 @@ namespace cryptonote
return m_blockchain_storage.get_alternative_blocks_count();
}
//-----------------------------------------------------------------------------------------------
- bool core::init(const boost::program_options::variables_map& vm, const char *config_subdir, const cryptonote::test_options *test_options)
+ bool core::init(const boost::program_options::variables_map& vm, const char *config_subdir, const cryptonote::test_options *test_options, const GetCheckpointsCallback& get_checkpoints/* = nullptr */)
{
start_time = std::time(nullptr);
@@ -567,7 +567,7 @@ namespace cryptonote
regtest_hard_forks
};
const difficulty_type fixed_difficulty = command_line::get_arg(vm, arg_fixed_difficulty);
- r = m_blockchain_storage.init(db.release(), m_nettype, m_offline, regtest ? &regtest_test_options : test_options, fixed_difficulty);
+ r = m_blockchain_storage.init(db.release(), m_nettype, m_offline, regtest ? &regtest_test_options : test_options, fixed_difficulty, get_checkpoints);
r = m_mempool.init(max_txpool_weight);
CHECK_AND_ASSERT_MES(r, false, "Failed to initialize memory pool");
@@ -1494,6 +1494,7 @@ namespace cryptonote
m_txpool_auto_relayer.do_call(boost::bind(&core::relay_txpool_transactions, this));
m_check_updates_interval.do_call(boost::bind(&core::check_updates, this));
m_check_disk_space_interval.do_call(boost::bind(&core::check_disk_space, this));
+ m_block_rate_interval.do_call(boost::bind(&core::check_block_rate, this));
m_miner.on_idle();
m_mempool.on_idle();
return true;
@@ -1682,6 +1683,52 @@ namespace cryptonote
return true;
}
//-----------------------------------------------------------------------------------------------
+ double factorial(unsigned int n)
+ {
+ if (n <= 1)
+ return 1.0;
+ double f = n;
+ while (n-- > 1)
+ f *= n;
+ return f;
+ }
+ //-----------------------------------------------------------------------------------------------
+ static double probability(unsigned int blocks, unsigned int expected)
+ {
+ // https://www.umass.edu/wsp/resources/poisson/#computing
+ return pow(expected, blocks) / (factorial(blocks) * exp(expected));
+ }
+ //-----------------------------------------------------------------------------------------------
+ bool core::check_block_rate()
+ {
+ if (m_offline || m_target_blockchain_height > get_current_blockchain_height())
+ {
+ MDEBUG("Not checking block rate, offline or syncing");
+ return true;
+ }
+
+ static constexpr double threshold = 1. / (864000 / DIFFICULTY_TARGET_V2); // one false positive every 10 days
+
+ const time_t now = time(NULL);
+ const std::vector<time_t> timestamps = m_blockchain_storage.get_last_block_timestamps(60);
+
+ static const unsigned int seconds[] = { 5400, 1800, 600 };
+ for (size_t n = 0; n < sizeof(seconds)/sizeof(seconds[0]); ++n)
+ {
+ unsigned int b = 0;
+ for (time_t ts: timestamps) b += ts >= now - static_cast<time_t>(seconds[n]);
+ const double p = probability(b, seconds[n] / DIFFICULTY_TARGET_V2);
+ MDEBUG("blocks in the last " << seconds[n] / 60 << " minutes: " << b << " (probability " << p << ")");
+ if (p < threshold)
+ {
+ MWARNING("There were " << b << " blocks in the last " << seconds[n] / 60 << " minutes, there might be large hash rate changes, or we might be partitioned, cut off from the Monero network or under attack. Or it could be just sheer bad luck.");
+ break; // no need to look further
+ }
+ }
+
+ return true;
+ }
+ //-----------------------------------------------------------------------------------------------
void core::set_target_blockchain_height(uint64_t target_blockchain_height)
{
m_target_blockchain_height = target_blockchain_height;
diff --git a/src/cryptonote_core/cryptonote_core.h b/src/cryptonote_core/cryptonote_core.h
index 58fe5b7b5..4eca2a57b 100644
--- a/src/cryptonote_core/cryptonote_core.h
+++ b/src/cryptonote_core/cryptonote_core.h
@@ -117,7 +117,7 @@ namespace cryptonote
* @param relayed whether or not the transaction was relayed to us
* @param do_not_relay whether to prevent the transaction from being relayed
*
- * @return true if the transaction made it to the transaction pool, otherwise false
+ * @return true if the transaction was accepted, false otherwise
*/
bool handle_incoming_tx(const blobdata& tx_blob, tx_verification_context& tvc, bool keeped_by_block, bool relayed, bool do_not_relay);
@@ -133,7 +133,7 @@ namespace cryptonote
* @param relayed whether or not the transactions were relayed to us
* @param do_not_relay whether to prevent the transactions from being relayed
*
- * @return true if the transactions made it to the transaction pool, otherwise false
+ * @return true if the transactions were accepted, false otherwise
*/
bool handle_incoming_txs(const std::vector<blobdata>& tx_blobs, std::vector<tx_verification_context>& tvc, bool keeped_by_block, bool relayed, bool do_not_relay);
@@ -244,10 +244,11 @@ namespace cryptonote
* @param vm command line parameters
* @param config_subdir subdirectory for config storage
* @param test_options configuration options for testing
+ * @param get_checkpoints if set, will be called to get checkpoints data, must return checkpoints data pointer and size or nullptr if there ain't any checkpoints for specific network type
*
* @return false if one of the init steps fails, otherwise true
*/
- bool init(const boost::program_options::variables_map& vm, const char *config_subdir = NULL, const test_options *test_options = NULL);
+ bool init(const boost::program_options::variables_map& vm, const char *config_subdir = NULL, const test_options *test_options = NULL, const GetCheckpointsCallback& get_checkpoints = nullptr);
/**
* @copydoc Blockchain::reset_and_set_genesis_block
@@ -945,6 +946,13 @@ namespace cryptonote
*/
bool check_disk_space();
+ /**
+ * @brief checks block rate, and warns if it's too slow
+ *
+ * @return true on success, false otherwise
+ */
+ bool check_block_rate();
+
bool m_test_drop_download = true; //!< whether or not to drop incoming blocks (for testing)
uint64_t m_test_drop_download_height = 0; //!< height under which to drop incoming blocks, if doing so
@@ -969,6 +977,7 @@ namespace cryptonote
epee::math_helper::once_a_time_seconds<60*2, false> m_txpool_auto_relayer; //!< interval for checking re-relaying txpool transactions
epee::math_helper::once_a_time_seconds<60*60*12, true> m_check_updates_interval; //!< interval for checking for new versions
epee::math_helper::once_a_time_seconds<60*10, true> m_check_disk_space_interval; //!< interval for checking for disk space
+ epee::math_helper::once_a_time_seconds<90, false> m_block_rate_interval; //!< interval for checking block rate
std::atomic<bool> m_starter_message_showed; //!< has the "daemon will sync now" message been shown?
diff --git a/src/cryptonote_core/cryptonote_tx_utils.cpp b/src/cryptonote_core/cryptonote_tx_utils.cpp
index 4bc33b56b..4fc2736a6 100644
--- a/src/cryptonote_core/cryptonote_tx_utils.cpp
+++ b/src/cryptonote_core/cryptonote_tx_utils.cpp
@@ -38,6 +38,7 @@ using namespace epee;
#include "cryptonote_tx_utils.h"
#include "cryptonote_config.h"
#include "cryptonote_basic/miner.h"
+#include "cryptonote_basic/tx_extra.h"
#include "crypto/crypto.h"
#include "crypto/hash.h"
#include "ringct/rctSigs.h"
@@ -84,6 +85,8 @@ namespace cryptonote
if(!extra_nonce.empty())
if(!add_extra_nonce_to_tx_extra(tx.extra, extra_nonce))
return false;
+ if (!sort_tx_extra(tx.extra, tx.extra))
+ return false;
txin_gen in;
in.height = height;
@@ -434,6 +437,9 @@ namespace cryptonote
add_additional_tx_pub_keys_to_extra(tx.extra, additional_tx_public_keys);
}
+ if (!sort_tx_extra(tx.extra, tx.extra))
+ return false;
+
//check money
if(summary_outs_money > summary_inputs_money )
{
diff --git a/src/daemon/CMakeLists.txt b/src/daemon/CMakeLists.txt
index f645836a4..117790455 100644
--- a/src/daemon/CMakeLists.txt
+++ b/src/daemon/CMakeLists.txt
@@ -26,20 +26,6 @@
# STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
# THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-set(blocksdat "")
-if(PER_BLOCK_CHECKPOINT)
- if(APPLE AND DEPENDS)
- add_custom_command(OUTPUT blocksdat.o MAIN_DEPENDENCY ../blocks/checkpoints.dat COMMAND cd ${CMAKE_CURRENT_SOURCE_DIR} && touch stub.c && ${CMAKE_C_COMPILER} --target=x86_64-apple-darwin11 -o stub.o -c stub.c COMMAND cd ${CMAKE_CURRENT_SOURCE_DIR} && ${CMAKE_LINKER} -r -sectcreate __DATA __blocks_dat ../blocks/checkpoints.dat -o ${CMAKE_CURRENT_BINARY_DIR}/blocksdat.o stub.o && rm -f stub.*)
- elseif(APPLE AND NOT DEPENDS)
- add_custom_command(OUTPUT blocksdat.o MAIN_DEPENDENCY ../blocks/checkpoints.dat COMMAND cd ${CMAKE_CURRENT_SOURCE_DIR} && touch stub.c && ${CMAKE_C_COMPILER} -o stub.o -c stub.c COMMAND cd ${CMAKE_CURRENT_SOURCE_DIR} && ${CMAKE_LINKER} ${LD_RAW_FLAGS} -r -sectcreate __DATA __blocks_dat ../blocks/checkpoints.dat -o ${CMAKE_CURRENT_BINARY_DIR}/blocksdat.o stub.o && rm -f stub.*)
- elseif(LINUX_32)
- add_custom_command(OUTPUT blocksdat.o MAIN_DEPENDENCY ../blocks/checkpoints.dat COMMAND cd ${CMAKE_CURRENT_SOURCE_DIR} && cp ../blocks/checkpoints.dat blocks.dat && ${CMAKE_LINKER} -m elf_i386 ${LD_RAW_FLAGS} -r -b binary -o ${CMAKE_CURRENT_BINARY_DIR}/blocksdat.o blocks.dat && rm -f blocks.dat)
- else()
- add_custom_command(OUTPUT blocksdat.o MAIN_DEPENDENCY ../blocks/checkpoints.dat COMMAND cd ${CMAKE_CURRENT_SOURCE_DIR} && cp ../blocks/checkpoints.dat blocks.dat && ${CMAKE_LINKER} ${LD_RAW_FLAGS} -r -b binary -o ${CMAKE_CURRENT_BINARY_DIR}/blocksdat.o blocks.dat && rm -f blocks.dat)
- endif()
- set(blocksdat "blocksdat.o")
-endif()
-
set(daemon_sources
command_parser_executor.cpp
command_server.cpp
@@ -81,9 +67,7 @@ monero_private_headers(daemon
monero_add_executable(daemon
${daemon_sources}
${daemon_headers}
- ${daemon_private_headers}
- ${blocksdat}
-)
+ ${daemon_private_headers})
target_link_libraries(daemon
PRIVATE
rpc
@@ -106,7 +90,8 @@ target_link_libraries(daemon
${CMAKE_THREAD_LIBS_INIT}
${ZMQ_LIB}
${GNU_READLINE_LIBRARY}
- ${EXTRA_LIBRARIES})
+ ${EXTRA_LIBRARIES}
+ ${Blocks})
set_property(TARGET daemon
PROPERTY
OUTPUT_NAME "monerod")
diff --git a/src/daemon/core.h b/src/daemon/core.h
index 475f418d6..d1defd573 100644
--- a/src/daemon/core.h
+++ b/src/daemon/core.h
@@ -28,6 +28,7 @@
#pragma once
+#include "blocks/blocks.h"
#include "cryptonote_core/cryptonote_core.h"
#include "cryptonote_protocol/cryptonote_protocol_handler.h"
#include "misc_log_ex.h"
@@ -85,7 +86,12 @@ public:
//initialize core here
MGINFO("Initializing core...");
std::string config_subdir = get_config_subdir();
- if (!m_core.init(m_vm_HACK, config_subdir.empty() ? NULL : config_subdir.c_str()))
+#if defined(PER_BLOCK_CHECKPOINT)
+ const cryptonote::GetCheckpointsCallback& get_checkpoints = blocks::GetCheckpointsData;
+#else
+ const cryptonote::GetCheckpointsCallback& get_checkpoints = nullptr;
+#endif
+ if (!m_core.init(m_vm_HACK, config_subdir.empty() ? NULL : config_subdir.c_str(), nullptr, get_checkpoints))
{
return false;
}
diff --git a/src/daemon/rpc_command_executor.cpp b/src/daemon/rpc_command_executor.cpp
index 6464d372f..34d74d642 100644
--- a/src/daemon/rpc_command_executor.cpp
+++ b/src/daemon/rpc_command_executor.cpp
@@ -449,7 +449,7 @@ bool t_rpc_command_executor::show_status() {
% get_sync_percentage(ires)
% (ires.testnet ? "testnet" : ires.stagenet ? "stagenet" : "mainnet")
% bootstrap_msg
- % (!has_mining_info ? "mining info unavailable" : mining_busy ? "syncing" : mres.active ? ( ( mres.is_background_mining_enabled ? "smart " : "" ) + std::string("mining at ") + get_mining_speed(mres.speed) ) : "not mining")
+ % (!has_mining_info ? "mining info unavailable" : mining_busy ? "syncing" : mres.active ? ( ( mres.is_background_mining_enabled ? "smart " : "" ) + std::string("mining at ") + get_mining_speed(mres.speed) + std::string(" to ") + mres.address ) : "not mining")
% get_mining_speed(ires.difficulty / ires.target)
% (unsigned)hfres.version
% get_fork_extra_info(hfres.earliest_height, net_height, ires.target)
@@ -1717,11 +1717,14 @@ bool t_rpc_command_executor::print_blockchain_dynamic_stats(uint64_t nblocks)
cryptonote::COMMAND_RPC_GET_BLOCK_HEADERS_RANGE::response bhres;
cryptonote::COMMAND_RPC_GET_BASE_FEE_ESTIMATE::request fereq;
cryptonote::COMMAND_RPC_GET_BASE_FEE_ESTIMATE::response feres;
+ cryptonote::COMMAND_RPC_HARD_FORK_INFO::request hfreq;
+ cryptonote::COMMAND_RPC_HARD_FORK_INFO::response hfres;
epee::json_rpc::error error_resp;
std::string fail_message = "Problem fetching info";
fereq.grace_blocks = 0;
+ hfreq.version = HF_VERSION_PER_BYTE_FEE;
if (m_is_rpc)
{
if (!m_rpc_client->rpc_request(ireq, ires, "/getinfo", fail_message.c_str()))
@@ -1732,6 +1735,10 @@ bool t_rpc_command_executor::print_blockchain_dynamic_stats(uint64_t nblocks)
{
return true;
}
+ if (!m_rpc_client->json_rpc_request(hfreq, hfres, "hard_fork_info", fail_message.c_str()))
+ {
+ return true;
+ }
}
else
{
@@ -1745,10 +1752,15 @@ bool t_rpc_command_executor::print_blockchain_dynamic_stats(uint64_t nblocks)
tools::fail_msg_writer() << make_error(fail_message, feres.status);
return true;
}
+ if (!m_rpc_server->on_hard_fork_info(hfreq, hfres, error_resp) || hfres.status != CORE_RPC_STATUS_OK)
+ {
+ tools::fail_msg_writer() << make_error(fail_message, hfres.status);
+ return true;
+ }
}
tools::msg_writer() << "Height: " << ires.height << ", diff " << ires.difficulty << ", cum. diff " << ires.cumulative_difficulty
- << ", target " << ires.target << " sec" << ", dyn fee " << cryptonote::print_money(feres.fee) << "/kB";
+ << ", target " << ires.target << " sec" << ", dyn fee " << cryptonote::print_money(feres.fee) << "/" << (hfres.enabled ? "byte" : "kB");
if (nblocks > 0)
{
diff --git a/src/device/CMakeLists.txt b/src/device/CMakeLists.txt
index 8f446f42a..91d670b73 100644
--- a/src/device/CMakeLists.txt
+++ b/src/device/CMakeLists.txt
@@ -44,6 +44,7 @@ set(device_headers
device.hpp
device_io.hpp
device_default.hpp
+ device_cold.hpp
log.hpp
)
@@ -58,12 +59,6 @@ endif()
set(device_private_headers)
-if(PER_BLOCK_CHECKPOINT)
- set(Blocks "blocks")
-else()
- set(Blocks "")
-endif()
-
monero_private_headers(device
${device_private_headers})
@@ -78,6 +73,6 @@ target_link_libraries(device
cncrypto
ringct_basic
${OPENSSL_CRYPTO_LIBRARIES}
+ ${Boost_SERIALIZATION_LIBRARY}
PRIVATE
- ${Blocks}
${EXTRA_LIBRARIES})
diff --git a/src/device/device.hpp b/src/device/device.hpp
index cb9117650..dd9ad4332 100644
--- a/src/device/device.hpp
+++ b/src/device/device.hpp
@@ -47,6 +47,7 @@
#include "crypto/crypto.h"
#include "crypto/chacha.h"
#include "ringct/rctTypes.h"
+#include "cryptonote_config.h"
#ifndef USE_DEVICE_LEDGER
@@ -85,7 +86,7 @@ namespace hw {
public:
- device() {}
+ device(): mode(NONE) {}
device(const device &hwdev) {}
virtual ~device() {}
@@ -99,10 +100,17 @@ namespace hw {
enum device_type
{
SOFTWARE = 0,
- LEDGER = 1
+ LEDGER = 1,
+ TREZOR = 2
};
+ enum device_protocol_t {
+ PROTOCOL_DEFAULT,
+ PROTOCOL_PROXY, // Originally defined by Ledger
+ PROTOCOL_COLD, // Originally defined by Trezor
+ };
+
/* ======================================================================= */
/* SETUP/TEARDOWN */
/* ======================================================================= */
@@ -115,10 +123,12 @@ namespace hw {
virtual bool connect(void) = 0;
virtual bool disconnect(void) = 0;
- virtual bool set_mode(device_mode mode) = 0;
+ virtual bool set_mode(device_mode mode) { this->mode = mode; return true; }
+ virtual device_mode get_mode() const { return mode; }
virtual device_type get_type() const = 0;
+ virtual device_protocol_t device_protocol() const { return PROTOCOL_DEFAULT; };
/* ======================================================================= */
/* LOCKER */
@@ -202,6 +212,14 @@ namespace hw {
virtual bool mlsag_sign(const rct::key &c, const rct::keyV &xx, const rct::keyV &alpha, const size_t rows, const size_t dsRows, rct::keyV &ss) = 0;
virtual bool close_tx(void) = 0;
+
+ virtual bool has_ki_cold_sync(void) const { return false; }
+ virtual bool has_tx_cold_sign(void) const { return false; }
+
+ virtual void set_network_type(cryptonote::network_type network_type) { }
+
+ protected:
+ device_mode mode;
} ;
struct reset_mode {
diff --git a/src/device/device_cold.hpp b/src/device/device_cold.hpp
new file mode 100644
index 000000000..22128cec1
--- /dev/null
+++ b/src/device/device_cold.hpp
@@ -0,0 +1,71 @@
+// Copyright (c) 2017-2018, The Monero Project
+//
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without modification, are
+// permitted provided that the following conditions are met:
+//
+// 1. Redistributions of source code must retain the above copyright notice, this list of
+// conditions and the following disclaimer.
+//
+// 2. Redistributions in binary form must reproduce the above copyright notice, this list
+// of conditions and the following disclaimer in the documentation and/or other
+// materials provided with the distribution.
+//
+// 3. Neither the name of the copyright holder nor the names of its contributors may be
+// used to endorse or promote products derived from this software without specific
+// prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
+// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
+// THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
+// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+//
+
+#ifndef MONERO_DEVICE_COLD_H
+#define MONERO_DEVICE_COLD_H
+
+#include "wallet/wallet2.h"
+#include <boost/function.hpp>
+
+
+namespace hw {
+
+ typedef struct wallet_shim {
+ boost::function<crypto::public_key (const tools::wallet2::transfer_details &td)> get_tx_pub_key_from_received_outs;
+ } wallet_shim;
+
+ class tx_aux_data {
+ public:
+ std::vector<std::string> tx_device_aux; // device generated aux data
+ std::vector<cryptonote::address_parse_info> tx_recipients; // as entered by user
+ };
+
+ class device_cold {
+ public:
+
+ using exported_key_image = std::vector<std::pair<crypto::key_image, crypto::signature>>;
+
+ /**
+ * Key image sync with the cold protocol.
+ */
+ virtual void ki_sync(wallet_shim * wallet,
+ const std::vector<::tools::wallet2::transfer_details> & transfers,
+ exported_key_image & ski) =0;
+
+ /**
+ * Signs unsigned transaction with the cold protocol.
+ */
+ virtual void tx_sign(wallet_shim * wallet,
+ const ::tools::wallet2::unsigned_tx_set & unsigned_tx,
+ ::tools::wallet2::signed_tx_set & signed_tx,
+ tx_aux_data & aux_data) =0;
+ };
+}
+
+#endif //MONERO_DEVICE_COLD_H
diff --git a/src/device/device_default.cpp b/src/device/device_default.cpp
index a4f40e041..1e3d80949 100644
--- a/src/device/device_default.cpp
+++ b/src/device/device_default.cpp
@@ -69,21 +69,21 @@ namespace hw {
}
bool device_default::init(void) {
- dfns();
+ return true;
}
bool device_default::release() {
- dfns();
+ return true;
}
bool device_default::connect(void) {
- dfns();
+ return true;
}
bool device_default::disconnect() {
- dfns();
+ return true;
}
bool device_default::set_mode(device_mode mode) {
- return true;
+ return device::set_mode(mode);
}
/* ======================================================================= */
diff --git a/src/device/device_io_hid.cpp b/src/device/device_io_hid.cpp
index 666255cb3..1aadfb9ea 100644
--- a/src/device/device_io_hid.cpp
+++ b/src/device/device_io_hid.cpp
@@ -1,3 +1,18 @@
+// Copyright (c) 2017-2018, The Monero Project
+//
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without modification, are
+// permitted provided that the following conditions are met:
+//
+// 1. Redistributions of source code must retain the above copyright notice, this list of
+// conditions and the following disclaimer.
+//
+// 2. Redistributions in binary form must reproduce the above copyright notice, this list
+// of conditions and the following disclaimer in the documentation and/or other
+// materials provided with the distribution.
+//
+// 3. Neither the name of the copyright holder nor the names of its contributors may be
// used to endorse or promote products derived from this software without specific
// prior written permission.
//
diff --git a/src/device/device_ledger.cpp b/src/device/device_ledger.cpp
index d879ee95a..0a86e6987 100644
--- a/src/device/device_ledger.cpp
+++ b/src/device/device_ledger.cpp
@@ -396,7 +396,7 @@ namespace hw {
CHECK_AND_ASSERT_THROW_MES(false, " device_ledger::set_mode(unsigned int mode): invalid mode: "<<mode);
}
MDEBUG("Switch to mode: " <<mode);
- return true;
+ return device::set_mode(mode);
}
diff --git a/src/device/device_ledger.hpp b/src/device/device_ledger.hpp
index dde69fbfd..2f5beb044 100644
--- a/src/device/device_ledger.hpp
+++ b/src/device/device_ledger.hpp
@@ -141,6 +141,7 @@ namespace hw {
bool set_mode(device_mode mode) override;
device_type get_type() const override {return device_type::LEDGER;};
+ device_protocol_t device_protocol() const override { return PROTOCOL_PROXY; };
/* ======================================================================= */
/* LOCKER */
diff --git a/src/device_trezor/CMakeLists.txt b/src/device_trezor/CMakeLists.txt
new file mode 100644
index 000000000..c555e9fcd
--- /dev/null
+++ b/src/device_trezor/CMakeLists.txt
@@ -0,0 +1,123 @@
+# Copyright (c) 2014-2017, The Monero Project
+#
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without modification, are
+# permitted provided that the following conditions are met:
+#
+# 1. Redistributions of source code must retain the above copyright notice, this list of
+# conditions and the following disclaimer.
+#
+# 2. Redistributions in binary form must reproduce the above copyright notice, this list
+# of conditions and the following disclaimer in the documentation and/or other
+# materials provided with the distribution.
+#
+# 3. Neither the name of the copyright holder nor the names of its contributors may be
+# used to endorse or promote products derived from this software without specific
+# prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
+# EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+# MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
+# THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+# STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
+# THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+set(TREZOR_PROTOB_H
+ trezor/messages/messages.pb.h
+ trezor/messages/messages-common.pb.h
+ trezor/messages/messages-management.pb.h
+ trezor/messages/messages-monero.pb.h
+)
+
+set(TREZOR_PROTOB_CPP
+ trezor/messages/messages.pb.cc
+ trezor/messages/messages-common.pb.cc
+ trezor/messages/messages-management.pb.cc
+ trezor/messages/messages-monero.pb.cc
+)
+
+set(trezor_headers
+ trezor/exceptions.hpp
+ trezor/messages_map.hpp
+ trezor/protocol.hpp
+ trezor/transport.hpp
+ device_trezor_base.hpp
+ device_trezor.hpp
+ trezor.hpp
+ ${TREZOR_PROTOB_H}
+)
+
+set(trezor_sources
+ trezor/messages_map.cpp
+ trezor/protocol.cpp
+ trezor/transport.cpp
+ device_trezor_base.cpp
+ device_trezor.cpp
+ ${TREZOR_PROTOB_CPP}
+)
+
+set(trezor_private_headers)
+
+
+include(FindProtobuf)
+find_package(Protobuf) # REQUIRED
+
+# Test for HAVE_PROTOBUF from the parent
+if(Protobuf_FOUND AND HAVE_PROTOBUF)
+ if ("$ENV{PYTHON3}" STREQUAL "")
+ set(PYTHON3 "python3")
+ else()
+ set(PYTHON3 "$ENV{PYTHON3}" CACHE INTERNAL "Copied from environment variable")
+ endif()
+
+ execute_process(COMMAND ${PYTHON3} tools/build_protob.py WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/trezor RESULT_VARIABLE RET OUTPUT_VARIABLE OUT ERROR_VARIABLE ERR)
+ if(RET)
+ message(WARNING "Trezor protobuf messages could not be regenerated (err=${RET}, python ${PYTHON})."
+ "OUT: ${OUT}, ERR: ${ERR}."
+ "Please read src/device_trezor/trezor/tools/README.md")
+ else()
+ message(STATUS "Trezor protobuf messages regenerated ${OUT}")
+ set(TREZOR_PROTOBUF_GENERATED 1)
+ endif()
+endif()
+
+
+if(TREZOR_PROTOBUF_GENERATED)
+ message(STATUS "Trezor support enabled")
+
+ add_definitions(-DPROTOBUF_INLINE_NOT_IN_HEADERS=0)
+
+ monero_private_headers(device_trezor
+ ${device_private_headers}
+ ${PROTOBUF_INCLUDE_DIR})
+
+ monero_add_library(device_trezor
+ ${trezor_sources}
+ ${trezor_headers}
+ ${trezor_private_headers})
+
+ target_link_libraries(device_trezor
+ PUBLIC
+ device
+ cncrypto
+ ringct_basic
+ cryptonote_core
+ common
+ ${SODIUM_LIBRARY}
+ ${Boost_CHRONO_LIBRARY}
+ ${PROTOBUF_LIBRARY}
+ PRIVATE
+ ${EXTRA_LIBRARIES})
+
+ # set(WITH_DEVICE_TREZOR 1 PARENT_SCOPE)
+ # add_definitions(-DWITH_DEVICE_TREZOR=1)
+
+else()
+ monero_private_headers(device_trezor)
+ monero_add_library(device_trezor device_trezor.cpp)
+ target_link_libraries(device_trezor PUBLIC cncrypto)
+endif()
diff --git a/src/device_trezor/device_trezor.cpp b/src/device_trezor/device_trezor.cpp
new file mode 100644
index 000000000..07c03fc66
--- /dev/null
+++ b/src/device_trezor/device_trezor.cpp
@@ -0,0 +1,363 @@
+// Copyright (c) 2017-2018, The Monero Project
+//
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without modification, are
+// permitted provided that the following conditions are met:
+//
+// 1. Redistributions of source code must retain the above copyright notice, this list of
+// conditions and the following disclaimer.
+//
+// 2. Redistributions in binary form must reproduce the above copyright notice, this list
+// of conditions and the following disclaimer in the documentation and/or other
+// materials provided with the distribution.
+//
+// 3. Neither the name of the copyright holder nor the names of its contributors may be
+// used to endorse or promote products derived from this software without specific
+// prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
+// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
+// THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
+// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+//
+
+#include "device_trezor.hpp"
+
+namespace hw {
+namespace trezor {
+
+#if WITH_DEVICE_TREZOR
+
+#undef MONERO_DEFAULT_LOG_CATEGORY
+#define MONERO_DEFAULT_LOG_CATEGORY "device.trezor"
+
+#define HW_TREZOR_NAME "Trezor"
+#define HW_TREZOR_NAME_LITE "TrezorLite"
+
+ static device_trezor *trezor_device = nullptr;
+ static device_trezor *ensure_trezor_device(){
+ if (!trezor_device) {
+ trezor_device = new device_trezor();
+ trezor_device->set_name(HW_TREZOR_NAME);
+ }
+ return trezor_device;
+ }
+
+ void register_all(std::map<std::string, std::unique_ptr<device>> &registry) {
+ registry.insert(std::make_pair(HW_TREZOR_NAME, std::unique_ptr<device>(ensure_trezor_device())));
+ }
+
+ void register_all() {
+ hw::register_device(HW_TREZOR_NAME, ensure_trezor_device());
+ }
+
+ device_trezor::device_trezor() {
+
+ }
+
+ device_trezor::~device_trezor() {
+ try {
+ disconnect();
+ release();
+ } catch(std::exception const& e){
+ MWARNING("Could not disconnect and release: " << e.what());
+ }
+ }
+
+ /* ======================================================================= */
+ /* WALLET & ADDRESS */
+ /* ======================================================================= */
+
+ bool device_trezor::get_public_address(cryptonote::account_public_address &pubkey) {
+ try {
+ auto res = get_address();
+
+ cryptonote::address_parse_info info{};
+ bool r = cryptonote::get_account_address_from_str(info, this->network_type, res->address());
+ CHECK_AND_ASSERT_MES(r, false, "Could not parse returned address. Address parse failed: " + res->address());
+ CHECK_AND_ASSERT_MES(!info.is_subaddress, false, "Trezor returned a sub address");
+
+ pubkey = info.address;
+ return true;
+
+ } catch(std::exception const& e){
+ MERROR("Get public address exception: " << e.what());
+ return false;
+ }
+ }
+
+ bool device_trezor::get_secret_keys(crypto::secret_key &viewkey , crypto::secret_key &spendkey) {
+ try {
+ MDEBUG("Loading view-only key from the Trezor. Please check the Trezor for a confirmation.");
+ auto res = get_view_key();
+ CHECK_AND_ASSERT_MES(res->watch_key().size() == 32, false, "Trezor returned invalid view key");
+
+ spendkey = crypto::null_skey; // not given
+ memcpy(viewkey.data, res->watch_key().data(), 32);
+
+ return true;
+
+ } catch(std::exception const& e){
+ MERROR("Get secret keys exception: " << e.what());
+ return false;
+ }
+ }
+
+ /* ======================================================================= */
+ /* Helpers */
+ /* ======================================================================= */
+
+ /* ======================================================================= */
+ /* TREZOR PROTOCOL */
+ /* ======================================================================= */
+
+ std::shared_ptr<messages::monero::MoneroAddress> device_trezor::get_address(
+ const boost::optional<std::vector<uint32_t>> & path,
+ const boost::optional<cryptonote::network_type> & network_type){
+ AUTO_LOCK_CMD();
+ require_connected();
+ test_ping();
+
+ auto req = std::make_shared<messages::monero::MoneroGetAddress>();
+ this->set_msg_addr<messages::monero::MoneroGetAddress>(req.get(), path, network_type);
+
+ auto response = this->client_exchange<messages::monero::MoneroAddress>(req);
+ MTRACE("Get address response received");
+ return response;
+ }
+
+ std::shared_ptr<messages::monero::MoneroWatchKey> device_trezor::get_view_key(
+ const boost::optional<std::vector<uint32_t>> & path,
+ const boost::optional<cryptonote::network_type> & network_type){
+ AUTO_LOCK_CMD();
+ require_connected();
+ test_ping();
+
+ auto req = std::make_shared<messages::monero::MoneroGetWatchKey>();
+ this->set_msg_addr<messages::monero::MoneroGetWatchKey>(req.get(), path, network_type);
+
+ auto response = this->client_exchange<messages::monero::MoneroWatchKey>(req);
+ MTRACE("Get watch key response received");
+ return response;
+ }
+
+ void device_trezor::ki_sync(wallet_shim * wallet,
+ const std::vector<tools::wallet2::transfer_details> & transfers,
+ hw::device_cold::exported_key_image & ski)
+ {
+ AUTO_LOCK_CMD();
+ require_connected();
+ test_ping();
+
+ std::shared_ptr<messages::monero::MoneroKeyImageExportInitRequest> req;
+
+ std::vector<protocol::ki::MoneroTransferDetails> mtds;
+ std::vector<protocol::ki::MoneroExportedKeyImage> kis;
+ protocol::ki::key_image_data(wallet, transfers, mtds);
+ protocol::ki::generate_commitment(mtds, transfers, req);
+
+ this->set_msg_addr<messages::monero::MoneroKeyImageExportInitRequest>(req.get());
+ auto ack1 = this->client_exchange<messages::monero::MoneroKeyImageExportInitAck>(req);
+
+ const auto batch_size = 10;
+ const auto num_batches = (mtds.size() + batch_size - 1) / batch_size;
+ for(uint64_t cur = 0; cur < num_batches; ++cur){
+ auto step_req = std::make_shared<messages::monero::MoneroKeyImageSyncStepRequest>();
+ auto idx_finish = std::min(static_cast<uint64_t>((cur + 1) * batch_size), static_cast<uint64_t>(mtds.size()));
+ for(uint64_t idx = cur * batch_size; idx < idx_finish; ++idx){
+ auto added_tdis = step_req->add_tdis();
+ CHECK_AND_ASSERT_THROW_MES(idx < mtds.size(), "Invalid transfer detail index");
+ *added_tdis = mtds[idx];
+ }
+
+ auto step_ack = this->client_exchange<messages::monero::MoneroKeyImageSyncStepAck>(step_req);
+ auto kis_size = step_ack->kis_size();
+ kis.reserve(static_cast<size_t>(kis_size));
+ for(int i = 0; i < kis_size; ++i){
+ auto ckis = step_ack->kis(i);
+ kis.push_back(ckis);
+ }
+
+ MTRACE("Batch " << cur << " / " << num_batches << " batches processed");
+ }
+
+ auto final_req = std::make_shared<messages::monero::MoneroKeyImageSyncFinalRequest>();
+ auto final_ack = this->client_exchange<messages::monero::MoneroKeyImageSyncFinalAck>(final_req);
+ ski.reserve(kis.size());
+
+ for(auto & sub : kis){
+ char buff[32*3];
+ protocol::crypto::chacha::decrypt(sub.blob().data(), sub.blob().size(),
+ reinterpret_cast<const uint8_t *>(final_ack->enc_key().data()),
+ reinterpret_cast<const uint8_t *>(sub.iv().data()), buff);
+
+ ::crypto::signature sig{};
+ ::crypto::key_image ki;
+ memcpy(ki.data, buff, 32);
+ memcpy(sig.c.data, buff + 32, 32);
+ memcpy(sig.r.data, buff + 64, 32);
+ ski.push_back(std::make_pair(ki, sig));
+ }
+ }
+
+
+ void device_trezor::tx_sign(wallet_shim * wallet,
+ const tools::wallet2::unsigned_tx_set & unsigned_tx,
+ tools::wallet2::signed_tx_set & signed_tx,
+ hw::tx_aux_data & aux_data)
+ {
+ size_t num_tx = unsigned_tx.txes.size();
+ signed_tx.key_images.clear();
+ signed_tx.key_images.resize(unsigned_tx.transfers.size());
+
+ for(size_t tx_idx = 0; tx_idx < num_tx; ++tx_idx) {
+ std::shared_ptr<protocol::tx::Signer> signer;
+ tx_sign(wallet, unsigned_tx, tx_idx, aux_data, signer);
+
+ auto & cdata = signer->tdata();
+ auto aux_info_cur = signer->store_tx_aux_info();
+ aux_data.tx_device_aux.emplace_back(aux_info_cur);
+
+ // Pending tx reconstruction
+ signed_tx.ptx.emplace_back();
+ auto & cpend = signed_tx.ptx.back();
+ cpend.tx = cdata.tx;
+ cpend.dust = 0;
+ cpend.fee = 0;
+ cpend.dust_added_to_fee = false;
+ cpend.change_dts = cdata.tx_data.change_dts;
+ cpend.selected_transfers = cdata.tx_data.selected_transfers;
+ cpend.key_images = "";
+ cpend.dests = cdata.tx_data.dests;
+ cpend.construction_data = cdata.tx_data;
+
+ // Transaction check
+ cryptonote::blobdata tx_blob;
+ cryptonote::transaction tx_deserialized;
+ bool r = cryptonote::t_serializable_object_to_blob(cpend.tx, tx_blob);
+ CHECK_AND_ASSERT_THROW_MES(r, "Transaction serialization failed");
+ r = cryptonote::parse_and_validate_tx_from_blob(tx_blob, tx_deserialized);
+ CHECK_AND_ASSERT_THROW_MES(r, "Transaction deserialization failed");
+
+ std::string key_images;
+ bool all_are_txin_to_key = std::all_of(cdata.tx.vin.begin(), cdata.tx.vin.end(), [&](const cryptonote::txin_v& s_e) -> bool
+ {
+ CHECKED_GET_SPECIFIC_VARIANT(s_e, const cryptonote::txin_to_key, in, false);
+ key_images += boost::to_string(in.k_image) + " ";
+ return true;
+ });
+ if(!all_are_txin_to_key) {
+ throw std::invalid_argument("Not all are txin_to_key");
+ }
+ cpend.key_images = key_images;
+
+ // KI sync
+ size_t num_sources = cdata.tx_data.sources.size();
+ CHECK_AND_ASSERT_THROW_MES(num_sources == cdata.source_permutation.size(), "Invalid permutation size");
+ CHECK_AND_ASSERT_THROW_MES(num_sources == cdata.tx.vin.size(), "Invalid tx.vin size");
+ for(size_t src_idx = 0; src_idx < num_sources; ++src_idx){
+ size_t idx_mapped = cdata.source_permutation[src_idx];
+ CHECK_AND_ASSERT_THROW_MES(idx_mapped < cdata.tx_data.selected_transfers.size(), "Invalid idx_mapped");
+ CHECK_AND_ASSERT_THROW_MES(src_idx < cdata.tx.vin.size(), "Invalid idx_mapped");
+
+ size_t idx_map_src = cdata.tx_data.selected_transfers[idx_mapped];
+ auto vini = boost::get<cryptonote::txin_to_key>(cdata.tx.vin[src_idx]);
+
+ CHECK_AND_ASSERT_THROW_MES(idx_map_src < signed_tx.key_images.size(), "Invalid key image index");
+ signed_tx.key_images[idx_map_src] = vini.k_image;
+ }
+ }
+ }
+
+ void device_trezor::tx_sign(wallet_shim * wallet,
+ const tools::wallet2::unsigned_tx_set & unsigned_tx,
+ size_t idx,
+ hw::tx_aux_data & aux_data,
+ std::shared_ptr<protocol::tx::Signer> & signer)
+ {
+ AUTO_LOCK_CMD();
+ require_connected();
+ test_ping();
+
+ CHECK_AND_ASSERT_THROW_MES(idx < unsigned_tx.txes.size(), "Invalid transaction index");
+ signer = std::make_shared<protocol::tx::Signer>(wallet, &unsigned_tx, idx, &aux_data);
+ const tools::wallet2::tx_construction_data & cur_tx = unsigned_tx.txes[idx];
+ unsigned long num_sources = cur_tx.sources.size();
+ unsigned long num_outputs = cur_tx.splitted_dsts.size();
+
+ // Step: Init
+ auto init_msg = signer->step_init();
+ this->set_msg_addr(init_msg.get());
+
+ auto response = this->client_exchange<messages::monero::MoneroTransactionInitAck>(init_msg);
+ signer->step_init_ack(response);
+
+ // Step: Set transaction inputs
+ for(size_t cur_src = 0; cur_src < num_sources; ++cur_src){
+ auto src = signer->step_set_input(cur_src);
+ auto ack = this->client_exchange<messages::monero::MoneroTransactionSetInputAck>(src);
+ signer->step_set_input_ack(ack);
+ }
+
+ // Step: sort
+ auto perm_req = signer->step_permutation();
+ if (perm_req){
+ auto perm_ack = this->client_exchange<messages::monero::MoneroTransactionInputsPermutationAck>(perm_req);
+ signer->step_permutation_ack(perm_ack);
+ }
+
+ // Step: input_vini
+ if (!signer->in_memory()){
+ for(size_t cur_src = 0; cur_src < num_sources; ++cur_src){
+ auto src = signer->step_set_vini_input(cur_src);
+ auto ack = this->client_exchange<messages::monero::MoneroTransactionInputViniAck>(src);
+ signer->step_set_vini_input_ack(ack);
+ }
+ }
+
+ // Step: all inputs set
+ auto all_inputs_set = signer->step_all_inputs_set();
+ auto ack_all_inputs = this->client_exchange<messages::monero::MoneroTransactionAllInputsSetAck>(all_inputs_set);
+ signer->step_all_inputs_set_ack(ack_all_inputs);
+
+ // Step: outputs
+ for(size_t cur_dst = 0; cur_dst < num_outputs; ++cur_dst){
+ auto src = signer->step_set_output(cur_dst);
+ auto ack = this->client_exchange<messages::monero::MoneroTransactionSetOutputAck>(src);
+ signer->step_set_output_ack(ack);
+ }
+
+ // Step: all outs set
+ auto all_out_set = signer->step_all_outs_set();
+ auto ack_all_out_set = this->client_exchange<messages::monero::MoneroTransactionAllOutSetAck>(all_out_set);
+ signer->step_all_outs_set_ack(ack_all_out_set, *this);
+
+ // Step: sign each input
+ for(size_t cur_src = 0; cur_src < num_sources; ++cur_src){
+ auto src = signer->step_sign_input(cur_src);
+ auto ack_sign = this->client_exchange<messages::monero::MoneroTransactionSignInputAck>(src);
+ signer->step_sign_input_ack(ack_sign);
+ }
+
+ // Step: final
+ auto final_msg = signer->step_final();
+ auto ack_final = this->client_exchange<messages::monero::MoneroTransactionFinalAck>(final_msg);
+ signer->step_final_ack(ack_final);
+ }
+
+#else //WITH_DEVICE_TREZOR
+
+ void register_all(std::map<std::string, std::unique_ptr<device>> &registry) {
+ }
+
+ void register_all() {
+ }
+
+#endif //WITH_DEVICE_TREZOR
+}} \ No newline at end of file
diff --git a/src/device_trezor/device_trezor.hpp b/src/device_trezor/device_trezor.hpp
new file mode 100644
index 000000000..765c9b82c
--- /dev/null
+++ b/src/device_trezor/device_trezor.hpp
@@ -0,0 +1,132 @@
+// Copyright (c) 2017-2018, The Monero Project
+//
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without modification, are
+// permitted provided that the following conditions are met:
+//
+// 1. Redistributions of source code must retain the above copyright notice, this list of
+// conditions and the following disclaimer.
+//
+// 2. Redistributions in binary form must reproduce the above copyright notice, this list
+// of conditions and the following disclaimer in the documentation and/or other
+// materials provided with the distribution.
+//
+// 3. Neither the name of the copyright holder nor the names of its contributors may be
+// used to endorse or promote products derived from this software without specific
+// prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
+// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
+// THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
+// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+//
+
+#ifndef MONERO_DEVICE_TREZOR_H
+#define MONERO_DEVICE_TREZOR_H
+
+
+#include <cstddef>
+#include <string>
+#include "device/device.hpp"
+#include "device/device_default.hpp"
+#include "device/device_cold.hpp"
+#include <boost/scope_exit.hpp>
+#include <boost/thread/mutex.hpp>
+#include <boost/thread/recursive_mutex.hpp>
+#include "cryptonote_config.h"
+#include "trezor.hpp"
+#include "device_trezor_base.hpp"
+
+namespace hw {
+namespace trezor {
+
+ void register_all();
+ void register_all(std::map<std::string, std::unique_ptr<device>> &registry);
+
+#if WITH_DEVICE_TREZOR
+ class device_trezor;
+
+ /**
+ * Main device
+ */
+ class device_trezor : public hw::trezor::device_trezor_base, public hw::device_cold {
+ protected:
+ // To speed up blockchain parsing the view key maybe handle here.
+ crypto::secret_key viewkey;
+ bool has_view_key;
+
+ public:
+ device_trezor();
+ virtual ~device_trezor() override;
+
+ device_trezor(const device_trezor &device) = delete ;
+ device_trezor& operator=(const device_trezor &device) = delete;
+
+ explicit operator bool() const override {return true;}
+
+ device_protocol_t device_protocol() const override { return PROTOCOL_COLD; };
+
+ bool has_ki_cold_sync() const override { return true; }
+ bool has_tx_cold_sign() const override { return true; }
+ void set_network_type(cryptonote::network_type network_type) override { this->network_type = network_type; }
+
+ /* ======================================================================= */
+ /* WALLET & ADDRESS */
+ /* ======================================================================= */
+ bool get_public_address(cryptonote::account_public_address &pubkey) override;
+ bool get_secret_keys(crypto::secret_key &viewkey , crypto::secret_key &spendkey) override;
+
+ /* ======================================================================= */
+ /* TREZOR PROTOCOL */
+ /* ======================================================================= */
+
+ /**
+ * Get address. Throws.
+ */
+ std::shared_ptr<messages::monero::MoneroAddress> get_address(
+ const boost::optional<std::vector<uint32_t>> & path = boost::none,
+ const boost::optional<cryptonote::network_type> & network_type = boost::none);
+
+ /**
+ * Get watch key from device. Throws.
+ */
+ std::shared_ptr<messages::monero::MoneroWatchKey> get_view_key(
+ const boost::optional<std::vector<uint32_t>> & path = boost::none,
+ const boost::optional<cryptonote::network_type> & network_type = boost::none);
+
+ /**
+ * Key image sync with the Trezor.
+ */
+ void ki_sync(wallet_shim * wallet,
+ const std::vector<::tools::wallet2::transfer_details> & transfers,
+ hw::device_cold::exported_key_image & ski) override;
+
+ /**
+ * Signs particular transaction idx in the unsigned set, keeps state in the signer
+ */
+ void tx_sign(wallet_shim * wallet,
+ const ::tools::wallet2::unsigned_tx_set & unsigned_tx,
+ size_t idx,
+ hw::tx_aux_data & aux_data,
+ std::shared_ptr<protocol::tx::Signer> & signer);
+
+ /**
+ * Signs unsigned transaction with the Trezor.
+ */
+ void tx_sign(wallet_shim * wallet,
+ const ::tools::wallet2::unsigned_tx_set & unsigned_tx,
+ ::tools::wallet2::signed_tx_set & signed_tx,
+ hw::tx_aux_data & aux_data) override;
+ };
+
+#endif
+
+}
+}
+#endif //MONERO_DEVICE_TREZOR_H
diff --git a/src/device_trezor/device_trezor_base.cpp b/src/device_trezor/device_trezor_base.cpp
new file mode 100644
index 000000000..3a98bba5a
--- /dev/null
+++ b/src/device_trezor/device_trezor_base.cpp
@@ -0,0 +1,301 @@
+// Copyright (c) 2017-2018, The Monero Project
+//
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without modification, are
+// permitted provided that the following conditions are met:
+//
+// 1. Redistributions of source code must retain the above copyright notice, this list of
+// conditions and the following disclaimer.
+//
+// 2. Redistributions in binary form must reproduce the above copyright notice, this list
+// of conditions and the following disclaimer in the documentation and/or other
+// materials provided with the distribution.
+//
+// 3. Neither the name of the copyright holder nor the names of its contributors may be
+// used to endorse or promote products derived from this software without specific
+// prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
+// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
+// THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
+// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+//
+
+#include "device_trezor_base.hpp"
+
+namespace hw {
+namespace trezor {
+
+#if WITH_DEVICE_TREZOR
+
+#undef MONERO_DEFAULT_LOG_CATEGORY
+#define MONERO_DEFAULT_LOG_CATEGORY "device.trezor"
+
+ std::shared_ptr<google::protobuf::Message> trezor_protocol_callback::on_button_request(const messages::common::ButtonRequest * msg){
+ MDEBUG("on_button_request");
+ device.on_button_request();
+ return std::make_shared<messages::common::ButtonAck>();
+ }
+
+ std::shared_ptr<google::protobuf::Message> trezor_protocol_callback::on_pin_matrix_request(const messages::common::PinMatrixRequest * msg){
+ MDEBUG("on_pin_request");
+ epee::wipeable_string pin;
+ device.on_pin_request(pin);
+ auto resp = std::make_shared<messages::common::PinMatrixAck>();
+ resp->set_pin(pin.data(), pin.size());
+ return resp;
+ }
+
+ std::shared_ptr<google::protobuf::Message> trezor_protocol_callback::on_passphrase_request(const messages::common::PassphraseRequest * msg){
+ MDEBUG("on_passhprase_request");
+ epee::wipeable_string passphrase;
+ device.on_passphrase_request(msg->on_device(), passphrase);
+ auto resp = std::make_shared<messages::common::PassphraseAck>();
+ if (!msg->on_device()){
+ resp->set_passphrase(passphrase.data(), passphrase.size());
+ }
+ return resp;
+ }
+
+ std::shared_ptr<google::protobuf::Message> trezor_protocol_callback::on_passphrase_state_request(const messages::common::PassphraseStateRequest * msg){
+ MDEBUG("on_passhprase_state_request");
+ device.on_passphrase_state_request(msg->state());
+ return std::make_shared<messages::common::PassphraseStateAck>();
+ }
+
+ const uint32_t device_trezor_base::DEFAULT_BIP44_PATH[] = {0x8000002c, 0x80000080, 0x80000000};
+
+ device_trezor_base::device_trezor_base() {
+
+ }
+
+ device_trezor_base::~device_trezor_base() {
+ try {
+ disconnect();
+ release();
+ } catch(std::exception const& e){
+ MERROR("Could not disconnect and release: " << e.what());
+ }
+ }
+
+ /* ======================================================================= */
+ /* SETUP/TEARDOWN */
+ /* ======================================================================= */
+
+ bool device_trezor_base::reset() {
+ return false;
+ }
+
+ bool device_trezor_base::set_name(const std::string & name) {
+ this->full_name = name;
+ this->name = "";
+
+ auto delim = name.find(':');
+ if (delim != std::string::npos && delim + 1 < name.length()) {
+ this->name = name.substr(delim + 1);
+ }
+
+ return true;
+ }
+
+ const std::string device_trezor_base::get_name() const {
+ if (this->full_name.empty()) {
+ return std::string("<disconnected:").append(this->name).append(">");
+ }
+ return this->full_name;
+ }
+
+ bool device_trezor_base::init() {
+ if (!release()){
+ MERROR("Release failed");
+ return false;
+ }
+
+ if (!m_protocol_callback){
+ m_protocol_callback = std::make_shared<trezor_protocol_callback>(*this);
+ }
+ return true;
+ }
+
+ bool device_trezor_base::release() {
+ try {
+ disconnect();
+ return true;
+
+ } catch(std::exception const& e){
+ MERROR("Release exception: " << e.what());
+ return false;
+ }
+ }
+
+ bool device_trezor_base::connect() {
+ disconnect();
+
+ // Enumerate all available devices
+ try {
+ hw::trezor::t_transport_vect trans;
+
+ MDEBUG("Enumerating Trezor devices...");
+ enumerate(trans);
+
+ MDEBUG("Enumeration yielded " << trans.size() << " devices");
+ for (auto &cur : trans) {
+ MDEBUG(" device: " << *(cur.get()));
+ std::string cur_path = cur->get_path();
+ if (boost::starts_with(cur_path, this->name)) {
+ MDEBUG("Device Match: " << cur_path);
+ m_transport = cur;
+ break;
+ }
+ }
+
+ if (!m_transport) {
+ MERROR("No matching Trezor device found. Device specifier: \"" + this->name + "\"");
+ return false;
+ }
+
+ m_transport->open();
+ return true;
+
+ } catch(std::exception const& e){
+ MERROR("Open exception: " << e.what());
+ return false;
+ }
+ }
+
+ bool device_trezor_base::disconnect() {
+ if (m_transport){
+ try {
+ m_transport->close();
+ m_transport = nullptr;
+
+ } catch(std::exception const& e){
+ MERROR("Disconnect exception: " << e.what());
+ m_transport = nullptr;
+ return false;
+ }
+ }
+ return true;
+ }
+
+ /* ======================================================================= */
+ /* LOCKER */
+ /* ======================================================================= */
+
+ //lock the device for a long sequence
+ void device_trezor_base::lock() {
+ MTRACE("Ask for LOCKING for device " << this->name << " in thread ");
+ device_locker.lock();
+ MTRACE("Device " << this->name << " LOCKed");
+ }
+
+ //lock the device for a long sequence
+ bool device_trezor_base::try_lock() {
+ MTRACE("Ask for LOCKING(try) for device " << this->name << " in thread ");
+ bool r = device_locker.try_lock();
+ if (r) {
+ MTRACE("Device " << this->name << " LOCKed(try)");
+ } else {
+ MDEBUG("Device " << this->name << " not LOCKed(try)");
+ }
+ return r;
+ }
+
+ //unlock the device
+ void device_trezor_base::unlock() {
+ MTRACE("Ask for UNLOCKING for device " << this->name << " in thread ");
+ device_locker.unlock();
+ MTRACE("Device " << this->name << " UNLOCKed");
+ }
+
+ /* ======================================================================= */
+ /* Helpers */
+ /* ======================================================================= */
+
+ void device_trezor_base::require_connected(){
+ if (!m_transport){
+ throw exc::NotConnectedException();
+ }
+ }
+
+ void device_trezor_base::call_ping_unsafe(){
+ auto pingMsg = std::make_shared<messages::management::Ping>();
+ pingMsg->set_message("PING");
+
+ auto success = this->client_exchange<messages::common::Success>(pingMsg); // messages::MessageType_Success
+ MDEBUG("Ping response " << success->message());
+ (void)success;
+ }
+
+ void device_trezor_base::test_ping(){
+ require_connected();
+
+ try {
+ this->call_ping_unsafe();
+
+ } catch(exc::TrezorException const& e){
+ MINFO("Trezor does not respond: " << e.what());
+ throw exc::DeviceNotResponsiveException(std::string("Trezor not responding: ") + e.what());
+ }
+ }
+
+ /* ======================================================================= */
+ /* TREZOR PROTOCOL */
+ /* ======================================================================= */
+
+ bool device_trezor_base::ping() {
+ AUTO_LOCK_CMD();
+ if (!m_transport){
+ MINFO("Ping failed, device not connected");
+ return false;
+ }
+
+ try {
+ this->call_ping_unsafe();
+ return true;
+
+ } catch(std::exception const& e) {
+ MERROR("Ping failed, exception thrown " << e.what());
+ } catch(...){
+ MERROR("Ping failed, general exception thrown" << boost::current_exception_diagnostic_information());
+ }
+
+ return false;
+ }
+
+ void device_trezor_base::on_button_request()
+ {
+ if (m_callback){
+ m_callback->on_button_request();
+ }
+ }
+
+ void device_trezor_base::on_pin_request(epee::wipeable_string & pin)
+ {
+ if (m_callback){
+ m_callback->on_pin_request(pin);
+ }
+ }
+
+ void device_trezor_base::on_passphrase_request(bool on_device, epee::wipeable_string & passphrase)
+ {
+ if (m_callback){
+ m_callback->on_passphrase_request(on_device, passphrase);
+ }
+ }
+
+ void device_trezor_base::on_passphrase_state_request(const std::string & state)
+ {
+ if (m_callback){
+ m_callback->on_passphrase_state_request(state);
+ }
+ }
+
+#endif //WITH_DEVICE_TREZOR
+}}
diff --git a/src/device_trezor/device_trezor_base.hpp b/src/device_trezor/device_trezor_base.hpp
new file mode 100644
index 000000000..644a49332
--- /dev/null
+++ b/src/device_trezor/device_trezor_base.hpp
@@ -0,0 +1,301 @@
+// Copyright (c) 2017-2018, The Monero Project
+//
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without modification, are
+// permitted provided that the following conditions are met:
+//
+// 1. Redistributions of source code must retain the above copyright notice, this list of
+// conditions and the following disclaimer.
+//
+// 2. Redistributions in binary form must reproduce the above copyright notice, this list
+// of conditions and the following disclaimer in the documentation and/or other
+// materials provided with the distribution.
+//
+// 3. Neither the name of the copyright holder nor the names of its contributors may be
+// used to endorse or promote products derived from this software without specific
+// prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
+// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
+// THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
+// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+//
+
+#ifndef MONERO_DEVICE_TREZOR_BASE_H
+#define MONERO_DEVICE_TREZOR_BASE_H
+
+
+#include <cstddef>
+#include <string>
+#include "device/device.hpp"
+#include "device/device_default.hpp"
+#include "device/device_cold.hpp"
+#include <boost/scope_exit.hpp>
+#include <boost/thread/mutex.hpp>
+#include <boost/thread/recursive_mutex.hpp>
+#include "cryptonote_config.h"
+#include "trezor.hpp"
+
+//automatic lock one more level on device ensuring the current thread is allowed to use it
+#define AUTO_LOCK_CMD() \
+ /* lock both mutexes without deadlock*/ \
+ boost::lock(device_locker, command_locker); \
+ /* make sure both already-locked mutexes are unlocked at the end of scope */ \
+ boost::lock_guard<boost::recursive_mutex> lock1(device_locker, boost::adopt_lock); \
+ boost::lock_guard<boost::mutex> lock2(command_locker, boost::adopt_lock)
+
+
+namespace hw {
+namespace trezor {
+
+#if WITH_DEVICE_TREZOR
+ class device_trezor_base;
+
+ /**
+ * Trezor device callbacks
+ */
+ class trezor_callback {
+ public:
+ virtual void on_button_request() {};
+ virtual void on_pin_request(epee::wipeable_string & pin) {};
+ virtual void on_passphrase_request(bool on_device, epee::wipeable_string & passphrase) {};
+ virtual void on_passphrase_state_request(const std::string & state) {};
+ };
+
+ /**
+ * Default Trezor protocol client callback
+ */
+ class trezor_protocol_callback {
+ protected:
+ device_trezor_base & device;
+
+ public:
+ explicit trezor_protocol_callback(device_trezor_base & device): device(device) {}
+
+ std::shared_ptr<google::protobuf::Message> on_button_request(const messages::common::ButtonRequest * msg);
+ std::shared_ptr<google::protobuf::Message> on_pin_matrix_request(const messages::common::PinMatrixRequest * msg);
+ std::shared_ptr<google::protobuf::Message> on_passphrase_request(const messages::common::PassphraseRequest * msg);
+ std::shared_ptr<google::protobuf::Message> on_passphrase_state_request(const messages::common::PassphraseStateRequest * msg);
+
+ std::shared_ptr<google::protobuf::Message> on_message(const google::protobuf::Message * msg, messages::MessageType message_type){
+ MDEBUG("on_general_message");
+ return on_message_dispatch(msg, message_type);
+ }
+
+ std::shared_ptr<google::protobuf::Message> on_message_dispatch(const google::protobuf::Message * msg, messages::MessageType message_type){
+ if (message_type == messages::MessageType_ButtonRequest){
+ return on_button_request(dynamic_cast<const messages::common::ButtonRequest*>(msg));
+ } else if (message_type == messages::MessageType_PassphraseRequest) {
+ return on_passphrase_request(dynamic_cast<const messages::common::PassphraseRequest*>(msg));
+ } else if (message_type == messages::MessageType_PassphraseStateRequest) {
+ return on_passphrase_state_request(dynamic_cast<const messages::common::PassphraseStateRequest*>(msg));
+ } else if (message_type == messages::MessageType_PinMatrixRequest) {
+ return on_pin_matrix_request(dynamic_cast<const messages::common::PinMatrixRequest*>(msg));
+ } else {
+ return nullptr;
+ }
+ }
+ };
+
+ /**
+ * TREZOR device template with basic functions
+ */
+ class device_trezor_base : public hw::core::device_default {
+ protected:
+
+ // Locker for concurrent access
+ mutable boost::recursive_mutex device_locker;
+ mutable boost::mutex command_locker;
+
+ std::shared_ptr<Transport> m_transport;
+ std::shared_ptr<trezor_protocol_callback> m_protocol_callback;
+ std::shared_ptr<trezor_callback> m_callback;
+
+ std::string full_name;
+
+ cryptonote::network_type network_type;
+
+ //
+ // Internal methods
+ //
+
+ void require_connected();
+ void call_ping_unsafe();
+ void test_ping();
+
+ /**
+ * Client communication wrapper, handles specific Trezor protocol.
+ *
+ * @throws UnexpectedMessageException if the response message type is different than expected.
+ * Exception contains message type and the message itself.
+ */
+ template<class t_message>
+ std::shared_ptr<t_message>
+ client_exchange(const std::shared_ptr<const google::protobuf::Message> &req,
+ const boost::optional<messages::MessageType> & resp_type = boost::none,
+ const boost::optional<std::vector<messages::MessageType>> & resp_types = boost::none,
+ const boost::optional<messages::MessageType*> & resp_type_ptr = boost::none,
+ bool open_session = false,
+ unsigned depth=0)
+ {
+ // Require strictly protocol buffers response in the template.
+ BOOST_STATIC_ASSERT(boost::is_base_of<google::protobuf::Message, t_message>::value);
+ const bool accepting_base = boost::is_same<google::protobuf::Message, t_message>::value;
+ if (resp_types && !accepting_base){
+ throw std::invalid_argument("Cannot specify list of accepted types and not using generic response");
+ }
+
+ // Open session if required
+ if (open_session && depth == 0){
+ try {
+ m_transport->open();
+ } catch (const std::exception& e) {
+ std::throw_with_nested(exc::SessionException("Could not open session"));
+ }
+ }
+
+ // Scoped session closer
+ BOOST_SCOPE_EXIT_ALL(&, this) {
+ if (open_session && depth == 0){
+ this->getTransport()->close();
+ }
+ };
+
+ // Write the request
+ CHECK_AND_ASSERT_THROW_MES(req, "Request is null");
+ this->getTransport()->write(*req);
+
+ // Read the response
+ std::shared_ptr<google::protobuf::Message> msg_resp;
+ hw::trezor::messages::MessageType msg_resp_type;
+
+ // We may have several roundtrips with the handler
+ this->getTransport()->read(msg_resp, &msg_resp_type);
+ if (resp_type_ptr){
+ *(resp_type_ptr.get()) = msg_resp_type;
+ }
+
+ // Determine type of expected message response
+ messages::MessageType required_type = accepting_base ? messages::MessageType_Success :
+ (resp_type ? resp_type.get() : MessageMapper::get_message_wire_number<t_message>());
+
+ if (msg_resp_type == messages::MessageType_Failure) {
+ throw_failure_exception(dynamic_cast<messages::common::Failure *>(msg_resp.get()));
+
+ } else if (!accepting_base && msg_resp_type == required_type) {
+ return message_ptr_retype<t_message>(msg_resp);
+
+ } else {
+ auto resp = this->getProtocolCallback()->on_message(msg_resp.get(), msg_resp_type);
+ if (resp) {
+ return this->client_exchange<t_message>(resp, boost::none, resp_types, resp_type_ptr, false, depth + 1);
+
+ } else if (accepting_base && (!resp_types ||
+ std::find(resp_types.get().begin(), resp_types.get().end(), msg_resp_type) != resp_types.get().end())) {
+ return message_ptr_retype<t_message>(msg_resp);
+
+ } else {
+ throw exc::UnexpectedMessageException(msg_resp_type, msg_resp);
+ }
+ }
+ }
+
+ /**
+ * Utility method to set address_n and network type to the message requets.
+ */
+ template<class t_message>
+ void set_msg_addr(t_message * msg,
+ const boost::optional<std::vector<uint32_t>> & path = boost::none,
+ const boost::optional<cryptonote::network_type> & network_type = boost::none)
+ {
+ CHECK_AND_ASSERT_THROW_MES(msg, "Message is null");
+ msg->clear_address_n();
+ if (path){
+ for(auto x : path.get()){
+ msg->add_address_n(x);
+ }
+ } else {
+ for (unsigned int i : DEFAULT_BIP44_PATH) {
+ msg->add_address_n(i);
+ }
+ }
+
+ if (network_type){
+ msg->set_network_type(static_cast<uint32_t>(network_type.get()));
+ } else {
+ msg->set_network_type(static_cast<uint32_t>(this->network_type));
+ }
+ }
+
+ public:
+ device_trezor_base();
+ ~device_trezor_base() override;
+
+ device_trezor_base(const device_trezor_base &device) = delete ;
+ device_trezor_base& operator=(const device_trezor_base &device) = delete;
+
+ explicit operator bool() const override {return true;}
+ device_type get_type() const override {return device_type::TREZOR;};
+
+ bool reset();
+
+ // Default derivation path for Monero
+ static const uint32_t DEFAULT_BIP44_PATH[3];
+
+ std::shared_ptr<Transport> getTransport(){
+ return m_transport;
+ }
+
+ std::shared_ptr<trezor_protocol_callback> getProtocolCallback(){
+ return m_protocol_callback;
+ }
+
+ std::shared_ptr<trezor_callback> getCallback(){
+ return m_callback;
+ }
+
+ /* ======================================================================= */
+ /* SETUP/TEARDOWN */
+ /* ======================================================================= */
+ bool set_name(const std::string &name) override;
+
+ const std::string get_name() const override;
+ bool init() override;
+ bool release() override;
+ bool connect() override;
+ bool disconnect() override;
+
+ /* ======================================================================= */
+ /* LOCKER */
+ /* ======================================================================= */
+ void lock() override;
+ void unlock() override;
+ bool try_lock() override;
+
+ /* ======================================================================= */
+ /* TREZOR PROTOCOL */
+ /* ======================================================================= */
+
+ /**
+ * Device ping, no-throw
+ */
+ bool ping();
+
+ // Protocol callbacks
+ void on_button_request();
+ void on_pin_request(epee::wipeable_string & pin);
+ void on_passphrase_request(bool on_device, epee::wipeable_string & passphrase);
+ void on_passphrase_state_request(const std::string & state);
+ };
+
+#endif
+
+}
+}
+#endif //MONERO_DEVICE_TREZOR_BASE_H
diff --git a/src/device_trezor/trezor.hpp b/src/device_trezor/trezor.hpp
new file mode 100644
index 000000000..8abdd2c18
--- /dev/null
+++ b/src/device_trezor/trezor.hpp
@@ -0,0 +1,44 @@
+// Copyright (c) 2017-2018, The Monero Project
+//
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without modification, are
+// permitted provided that the following conditions are met:
+//
+// 1. Redistributions of source code must retain the above copyright notice, this list of
+// conditions and the following disclaimer.
+//
+// 2. Redistributions in binary form must reproduce the above copyright notice, this list
+// of conditions and the following disclaimer in the documentation and/or other
+// materials provided with the distribution.
+//
+// 3. Neither the name of the copyright holder nor the names of its contributors may be
+// used to endorse or promote products derived from this software without specific
+// prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
+// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
+// THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
+// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+//
+
+#ifndef MONERO_TREZOR_HPP
+#define MONERO_TREZOR_HPP
+
+#include "trezor/trezor_defs.hpp"
+
+#ifdef HAVE_PROTOBUF
+#include "trezor/transport.hpp"
+#include "trezor/messages/messages.pb.h"
+#include "trezor/messages/messages-common.pb.h"
+#include "trezor/messages/messages-management.pb.h"
+#include "trezor/messages/messages-monero.pb.h"
+#include "trezor/protocol.hpp"
+#endif
+
+#endif //MONERO_TREZOR_HPP
diff --git a/src/device_trezor/trezor/exceptions.hpp b/src/device_trezor/trezor/exceptions.hpp
new file mode 100644
index 000000000..197dc43a4
--- /dev/null
+++ b/src/device_trezor/trezor/exceptions.hpp
@@ -0,0 +1,193 @@
+// Copyright (c) 2017-2018, The Monero Project
+//
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without modification, are
+// permitted provided that the following conditions are met:
+//
+// 1. Redistributions of source code must retain the above copyright notice, this list of
+// conditions and the following disclaimer.
+//
+// 2. Redistributions in binary form must reproduce the above copyright notice, this list
+// of conditions and the following disclaimer in the documentation and/or other
+// materials provided with the distribution.
+//
+// 3. Neither the name of the copyright holder nor the names of its contributors may be
+// used to endorse or promote products derived from this software without specific
+// prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
+// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
+// THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
+// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+//
+
+#ifndef MONERO_EXCEPTIONS_H
+#define MONERO_EXCEPTIONS_H
+
+#include <exception>
+#include <string>
+#include <boost/optional.hpp>
+
+namespace hw {
+namespace trezor {
+namespace exc {
+
+ class SecurityException : public std::exception {
+ protected:
+ boost::optional<std::string> reason;
+
+ public:
+ SecurityException(): reason("General Security exception"){}
+ explicit SecurityException(std::string what): reason(what){}
+
+ virtual const char* what() const throw() {
+ return reason.get().c_str();
+ }
+ };
+
+ class Poly1305TagInvalid: public SecurityException {
+ public:
+ using SecurityException::SecurityException;
+ Poly1305TagInvalid(): SecurityException("Poly1305 authentication tag invalid"){}
+ };
+
+ class TrezorException : public std::exception {
+ protected:
+ boost::optional<std::string> reason;
+
+ public:
+ TrezorException(): reason("General Trezor exception"){}
+ explicit TrezorException(std::string what): reason(what){}
+
+ virtual const char* what() const throw() {
+ return reason.get().c_str();
+ }
+ };
+
+ class CommunicationException: public TrezorException {
+ public:
+ using TrezorException::TrezorException;
+ CommunicationException(): TrezorException("Trezor communication error"){}
+ };
+
+ class EncodingException: public CommunicationException {
+ public:
+ using CommunicationException::CommunicationException;
+ EncodingException(): CommunicationException("Trezor message encoding error"){}
+ };
+
+ class NotConnectedException : public CommunicationException {
+ public:
+ using CommunicationException::CommunicationException;
+ NotConnectedException(): CommunicationException("Trezor not connected"){}
+ };
+
+ class DeviceNotResponsiveException : public CommunicationException {
+ public:
+ using CommunicationException::CommunicationException;
+ DeviceNotResponsiveException(): CommunicationException("Trezor does not respond to ping"){}
+ };
+
+ class DeviceAcquireException : public CommunicationException {
+ public:
+ using CommunicationException::CommunicationException;
+ DeviceAcquireException(): CommunicationException("Trezor could not be acquired"){}
+ };
+
+ class SessionException: public CommunicationException {
+ public:
+ using CommunicationException::CommunicationException;
+ SessionException(): CommunicationException("Trezor session expired"){}
+ };
+
+ class TimeoutException: public CommunicationException {
+ public:
+ using CommunicationException::CommunicationException;
+ TimeoutException(): CommunicationException("Trezor communication timeout"){}
+ };
+
+ class ProtocolException: public CommunicationException {
+ public:
+ using CommunicationException::CommunicationException;
+ ProtocolException(): CommunicationException("Trezor protocol error"){}
+ };
+
+ // Communication protocol namespace
+ // Separated to distinguish between client and Trezor side exceptions.
+namespace proto {
+
+ class SecurityException : public ProtocolException {
+ public:
+ using ProtocolException::ProtocolException;
+ SecurityException(): ProtocolException("Security assertion violated in the protocol"){}
+ };
+
+ class FailureException : public ProtocolException {
+ private:
+ boost::optional<uint32_t> code;
+ boost::optional<std::string> message;
+ public:
+ using ProtocolException::ProtocolException;
+ FailureException(): ProtocolException("Trezor returned failure"){}
+ FailureException(boost::optional<uint32_t> code,
+ boost::optional<std::string> message)
+ : code(code), message(message) {
+ reason = "Trezor returned failure: code="
+ + (code ? std::to_string(code.get()) : "")
+ + ", message=" + (message ? message.get() : "");
+ };
+ };
+
+ class UnexpectedMessageException : public FailureException {
+ public:
+ using FailureException::FailureException;
+ UnexpectedMessageException(): FailureException("Trezor claims unexpected message received"){}
+ };
+
+ class CancelledException : public FailureException {
+ public:
+ using FailureException::FailureException;
+ CancelledException(): FailureException("Trezor returned: cancelled operation"){}
+ };
+
+ class PinExpectedException : public FailureException {
+ public:
+ using FailureException::FailureException;
+ PinExpectedException(): FailureException("Trezor claims PIN is expected"){}
+ };
+
+ class InvalidPinException : public FailureException {
+ public:
+ using FailureException::FailureException;
+ InvalidPinException(): FailureException("Trezor claims PIN is invalid"){}
+ };
+
+ class NotEnoughFundsException : public FailureException {
+ public:
+ using FailureException::FailureException;
+ NotEnoughFundsException(): FailureException("Trezor claims not enough funds"){}
+ };
+
+ class NotInitializedException : public FailureException {
+ public:
+ using FailureException::FailureException;
+ NotInitializedException(): FailureException("Trezor claims not initialized"){}
+ };
+
+ class FirmwareErrorException : public FailureException {
+ public:
+ using FailureException::FailureException;
+ FirmwareErrorException(): FailureException("Trezor returned firmware error"){}
+ };
+
+}
+}
+}
+}
+#endif //MONERO_EXCEPTIONS_H
diff --git a/src/device_trezor/trezor/messages/.gitignore b/src/device_trezor/trezor/messages/.gitignore
new file mode 100644
index 000000000..32f7a77e5
--- /dev/null
+++ b/src/device_trezor/trezor/messages/.gitignore
@@ -0,0 +1,2 @@
+# protobuf generated code
+*
diff --git a/src/device_trezor/trezor/messages_map.cpp b/src/device_trezor/trezor/messages_map.cpp
new file mode 100644
index 000000000..b0d1aa254
--- /dev/null
+++ b/src/device_trezor/trezor/messages_map.cpp
@@ -0,0 +1,125 @@
+// Copyright (c) 2017-2018, The Monero Project
+//
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without modification, are
+// permitted provided that the following conditions are met:
+//
+// 1. Redistributions of source code must retain the above copyright notice, this list of
+// conditions and the following disclaimer.
+//
+// 2. Redistributions in binary form must reproduce the above copyright notice, this list
+// of conditions and the following disclaimer in the documentation and/or other
+// materials provided with the distribution.
+//
+// 3. Neither the name of the copyright holder nor the names of its contributors may be
+// used to endorse or promote products derived from this software without specific
+// prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
+// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
+// THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
+// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+//
+
+#include "messages_map.hpp"
+#include "messages/messages.pb.h"
+#include "messages/messages-common.pb.h"
+#include "messages/messages-management.pb.h"
+#include "messages/messages-monero.pb.h"
+
+using namespace std;
+using namespace hw::trezor;
+
+namespace hw{
+namespace trezor
+{
+
+ const char * TYPE_PREFIX = "MessageType_";
+ const char * PACKAGES[] = {
+ "hw.trezor.messages.",
+ "hw.trezor.messages.common.",
+ "hw.trezor.messages.management.",
+ "hw.trezor.messages.monero."
+ };
+
+ google::protobuf::Message * MessageMapper::get_message(int wire_number) {
+ return MessageMapper::get_message(static_cast<messages::MessageType>(wire_number));
+ }
+
+ google::protobuf::Message * MessageMapper::get_message(messages::MessageType wire_number) {
+ const string &messageTypeName = hw::trezor::messages::MessageType_Name(wire_number);
+ if (messageTypeName.empty()) {
+ throw exc::EncodingException(std::string("Message descriptor not found: ") + std::to_string(wire_number));
+ }
+
+ string messageName = messageTypeName.substr(strlen(TYPE_PREFIX));
+ return MessageMapper::get_message(messageName);
+ }
+
+ google::protobuf::Message * MessageMapper::get_message(const std::string & msg_name) {
+ // Each package instantiation so lookup works
+ hw::trezor::messages::common::Success::default_instance();
+ hw::trezor::messages::management::Cancel::default_instance();
+ hw::trezor::messages::monero::MoneroGetAddress::default_instance();
+
+ google::protobuf::Descriptor const * desc = nullptr;
+ for(const string &text : PACKAGES){
+ desc = google::protobuf::DescriptorPool::generated_pool()
+ ->FindMessageTypeByName(text + msg_name);
+ if (desc != nullptr){
+ break;
+ }
+ }
+
+ if (desc == nullptr){
+ throw exc::EncodingException(std::string("Message not found: ") + msg_name);
+ }
+
+ google::protobuf::Message* message =
+ google::protobuf::MessageFactory::generated_factory()
+ ->GetPrototype(desc)->New();
+
+ return message;
+
+// // CODEGEN way, fast
+// switch(wire_number){
+// case 501:
+// return new messages::monero::MoneroTransactionSignRequest();
+// default:
+// throw std::runtime_error("not implemented");
+// }
+//
+// // CODEGEN message -> number: specification
+// // messages::MessageType get_message_wire_number(const messages::monero::MoneroTransactionSignRequest * msg) { return 501; }
+// // messages::MessageType get_message_wire_number(const messages::management::ping * msg)
+//
+ }
+
+ messages::MessageType MessageMapper::get_message_wire_number(const google::protobuf::Message * msg){
+ return MessageMapper::get_message_wire_number(msg->GetDescriptor()->name());
+ }
+
+ messages::MessageType MessageMapper::get_message_wire_number(const google::protobuf::Message & msg){
+ return MessageMapper::get_message_wire_number(msg.GetDescriptor()->name());
+ }
+
+ messages::MessageType MessageMapper::get_message_wire_number(const std::string & msg_name){
+ string enumMessageName = std::string(TYPE_PREFIX) + msg_name;
+
+ messages::MessageType res;
+ bool r = hw::trezor::messages::MessageType_Parse(enumMessageName, &res);
+ if (!r){
+ throw exc::EncodingException(std::string("Message ") + msg_name + " not found");
+ }
+
+ return res;
+ }
+
+}
+}
diff --git a/src/device_trezor/trezor/messages_map.hpp b/src/device_trezor/trezor/messages_map.hpp
new file mode 100644
index 000000000..f61338f09
--- /dev/null
+++ b/src/device_trezor/trezor/messages_map.hpp
@@ -0,0 +1,94 @@
+// Copyright (c) 2017-2018, The Monero Project
+//
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without modification, are
+// permitted provided that the following conditions are met:
+//
+// 1. Redistributions of source code must retain the above copyright notice, this list of
+// conditions and the following disclaimer.
+//
+// 2. Redistributions in binary form must reproduce the above copyright notice, this list
+// of conditions and the following disclaimer in the documentation and/or other
+// materials provided with the distribution.
+//
+// 3. Neither the name of the copyright holder nor the names of its contributors may be
+// used to endorse or promote products derived from this software without specific
+// prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
+// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
+// THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
+// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+//
+
+#ifndef MONERO_MESSAGES_MAP_H
+#define MONERO_MESSAGES_MAP_H
+
+#include <string>
+#include <type_traits>
+#include <memory>
+#include "exceptions.hpp"
+
+#include "trezor_defs.hpp"
+
+#include <google/protobuf/stubs/common.h>
+#include <google/protobuf/generated_message_util.h>
+#include <google/protobuf/repeated_field.h>
+#include <google/protobuf/extension_set.h>
+#include <google/protobuf/generated_enum_reflection.h>
+#include "google/protobuf/descriptor.pb.h"
+
+#include "messages/messages.pb.h"
+
+namespace hw {
+namespace trezor {
+
+ class MessageMapper{
+ public:
+ MessageMapper() {
+
+ }
+
+ static ::google::protobuf::Message * get_message(int wire_number);
+ static ::google::protobuf::Message * get_message(messages::MessageType);
+ static ::google::protobuf::Message * get_message(const std::string & msg_name);
+ static messages::MessageType get_message_wire_number(const google::protobuf::Message * msg);
+ static messages::MessageType get_message_wire_number(const google::protobuf::Message & msg);
+ static messages::MessageType get_message_wire_number(const std::string & msg_name);
+
+ template<class t_message>
+ static messages::MessageType get_message_wire_number() {
+ BOOST_STATIC_ASSERT(boost::is_base_of<google::protobuf::Message, t_message>::value);
+ return get_message_wire_number(t_message::default_instance().GetDescriptor()->name());
+ }
+ };
+
+ template<class t_message>
+ std::shared_ptr<t_message> message_ptr_retype(std::shared_ptr<google::protobuf::Message> & in){
+ BOOST_STATIC_ASSERT(boost::is_base_of<google::protobuf::Message, t_message>::value);
+ if (!in){
+ return nullptr;
+ }
+
+ return std::dynamic_pointer_cast<t_message>(in);
+ }
+
+ template<class t_message>
+ std::shared_ptr<t_message> message_ptr_retype_static(std::shared_ptr<google::protobuf::Message> & in){
+ BOOST_STATIC_ASSERT(boost::is_base_of<google::protobuf::Message, t_message>::value);
+ if (!in){
+ return nullptr;
+ }
+
+ return std::static_pointer_cast<t_message>(in);
+ }
+
+}}
+
+#endif //MONERO_MESSAGES_MAP_H
diff --git a/src/device_trezor/trezor/protocol.cpp b/src/device_trezor/trezor/protocol.cpp
new file mode 100644
index 000000000..c4a92426c
--- /dev/null
+++ b/src/device_trezor/trezor/protocol.cpp
@@ -0,0 +1,891 @@
+// Copyright (c) 2017-2018, The Monero Project
+//
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without modification, are
+// permitted provided that the following conditions are met:
+//
+// 1. Redistributions of source code must retain the above copyright notice, this list of
+// conditions and the following disclaimer.
+//
+// 2. Redistributions in binary form must reproduce the above copyright notice, this list
+// of conditions and the following disclaimer in the documentation and/or other
+// materials provided with the distribution.
+//
+// 3. Neither the name of the copyright holder nor the names of its contributors may be
+// used to endorse or promote products derived from this software without specific
+// prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
+// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
+// THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
+// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+//
+
+#include "protocol.hpp"
+#include <unordered_map>
+#include <set>
+#include <utility>
+#include <boost/endian/conversion.hpp>
+#include <common/apply_permutation.h>
+#include <ringct/rctSigs.h>
+#include <ringct/bulletproofs.h>
+#include "cryptonote_config.h"
+#include <sodium.h>
+#include <sodium/crypto_verify_32.h>
+#include <sodium/crypto_aead_chacha20poly1305.h>
+
+namespace hw{
+namespace trezor{
+namespace protocol{
+
+ std::string key_to_string(const ::crypto::ec_point & key){
+ return std::string(key.data, sizeof(key.data));
+ }
+
+ std::string key_to_string(const ::crypto::ec_scalar & key){
+ return std::string(key.data, sizeof(key.data));
+ }
+
+ std::string key_to_string(const ::crypto::hash & key){
+ return std::string(key.data, sizeof(key.data));
+ }
+
+ std::string key_to_string(const ::rct::key & key){
+ return std::string(reinterpret_cast<const char*>(key.bytes), sizeof(key.bytes));
+ }
+
+ void string_to_key(::crypto::ec_scalar & key, const std::string & str){
+ if (str.size() != sizeof(key.data)){
+ throw std::invalid_argument(std::string("Key has to have ") + std::to_string(sizeof(key.data)) + " B");
+ }
+ memcpy(key.data, str.data(), sizeof(key.data));
+ }
+
+ void string_to_key(::crypto::ec_point & key, const std::string & str){
+ if (str.size() != sizeof(key.data)){
+ throw std::invalid_argument(std::string("Key has to have ") + std::to_string(sizeof(key.data)) + " B");
+ }
+ memcpy(key.data, str.data(), sizeof(key.data));
+ }
+
+ void string_to_key(::rct::key & key, const std::string & str){
+ if (str.size() != sizeof(key.bytes)){
+ throw std::invalid_argument(std::string("Key has to have ") + std::to_string(sizeof(key.bytes)) + " B");
+ }
+ memcpy(key.bytes, str.data(), sizeof(key.bytes));
+ }
+
+namespace crypto {
+namespace chacha {
+
+ void decrypt(const void* ciphertext, size_t length, const uint8_t* key, const uint8_t* iv, char* plaintext){
+ if (length < 16){
+ throw std::invalid_argument("Ciphertext length too small");
+ }
+
+ unsigned long long int cip_len = length;
+ auto r = crypto_aead_chacha20poly1305_ietf_decrypt(
+ reinterpret_cast<unsigned char *>(plaintext), &cip_len, nullptr,
+ static_cast<const unsigned char *>(ciphertext), length, nullptr, 0, iv, key);
+
+ if (r != 0){
+ throw exc::Poly1305TagInvalid();
+ }
+ }
+
+}
+}
+
+
+// Cold Key image sync
+namespace ki {
+
+ bool key_image_data(wallet_shim * wallet,
+ const std::vector<tools::wallet2::transfer_details> & transfers,
+ std::vector<MoneroTransferDetails> & res)
+ {
+ for(auto & td : transfers){
+ ::crypto::public_key tx_pub_key = wallet->get_tx_pub_key_from_received_outs(td);
+ const std::vector<::crypto::public_key> additional_tx_pub_keys = cryptonote::get_additional_tx_pub_keys_from_extra(td.m_tx);
+
+ res.emplace_back();
+ auto & cres = res.back();
+
+ cres.set_out_key(key_to_string(boost::get<cryptonote::txout_to_key>(td.m_tx.vout[td.m_internal_output_index].target).key));
+ cres.set_tx_pub_key(key_to_string(tx_pub_key));
+ cres.set_internal_output_index(td.m_internal_output_index);
+ for(auto & aux : additional_tx_pub_keys){
+ cres.add_additional_tx_pub_keys(key_to_string(aux));
+ }
+ }
+
+ return true;
+ }
+
+ std::string compute_hash(const MoneroTransferDetails & rr){
+ KECCAK_CTX kck;
+ uint8_t md[32];
+
+ CHECK_AND_ASSERT_THROW_MES(rr.out_key().size() == 32, "Invalid out_key size");
+ CHECK_AND_ASSERT_THROW_MES(rr.tx_pub_key().size() == 32, "Invalid tx_pub_key size");
+
+ keccak_init(&kck);
+ keccak_update(&kck, reinterpret_cast<const uint8_t *>(rr.out_key().data()), 32);
+ keccak_update(&kck, reinterpret_cast<const uint8_t *>(rr.tx_pub_key().data()), 32);
+ for (const auto &aux : rr.additional_tx_pub_keys()){
+ CHECK_AND_ASSERT_THROW_MES(aux.size() == 32, "Invalid aux size");
+ keccak_update(&kck, reinterpret_cast<const uint8_t *>(aux.data()), 32);
+ }
+
+ auto index_serialized = tools::get_varint_data(rr.internal_output_index());
+ keccak_update(&kck, reinterpret_cast<const uint8_t *>(index_serialized.data()), index_serialized.size());
+ keccak_finish(&kck, md);
+ return std::string(reinterpret_cast<const char*>(md), sizeof(md));
+ }
+
+ void generate_commitment(std::vector<MoneroTransferDetails> & mtds,
+ const std::vector<tools::wallet2::transfer_details> & transfers,
+ std::shared_ptr<messages::monero::MoneroKeyImageExportInitRequest> & req)
+ {
+ req = std::make_shared<messages::monero::MoneroKeyImageExportInitRequest>();
+
+ KECCAK_CTX kck;
+ uint8_t final_hash[32];
+ keccak_init(&kck);
+
+ for(auto &cur : mtds){
+ auto hash = compute_hash(cur);
+ keccak_update(&kck, reinterpret_cast<const uint8_t *>(hash.data()), hash.size());
+ }
+ keccak_finish(&kck, final_hash);
+
+ req = std::make_shared<messages::monero::MoneroKeyImageExportInitRequest>();
+ req->set_hash(std::string(reinterpret_cast<const char*>(final_hash), 32));
+ req->set_num(transfers.size());
+
+ std::unordered_map<uint32_t, std::set<uint32_t>> sub_indices;
+ for (auto &cur : transfers){
+ auto search = sub_indices.emplace(cur.m_subaddr_index.major, std::set<uint32_t>());
+ auto & st = search.first->second;
+ st.insert(cur.m_subaddr_index.minor);
+ }
+
+ for (auto& x: sub_indices){
+ auto subs = req->add_subs();
+ subs->set_account(x.first);
+ for(auto minor : x.second){
+ subs->add_minor_indices(minor);
+ }
+ }
+ }
+
+}
+
+// Cold transaction signing
+namespace tx {
+
+ void translate_address(MoneroAccountPublicAddress * dst, const cryptonote::account_public_address * src){
+ dst->set_view_public_key(key_to_string(src->m_view_public_key));
+ dst->set_spend_public_key(key_to_string(src->m_spend_public_key));
+ }
+
+ void translate_dst_entry(MoneroTransactionDestinationEntry * dst, const cryptonote::tx_destination_entry * src){
+ dst->set_amount(src->amount);
+ dst->set_is_subaddress(src->is_subaddress);
+ translate_address(dst->mutable_addr(), &(src->addr));
+ }
+
+ void translate_src_entry(MoneroTransactionSourceEntry * dst, const cryptonote::tx_source_entry * src){
+ for(auto & cur : src->outputs){
+ auto out = dst->add_outputs();
+ out->set_idx(cur.first);
+ translate_rct_key(out->mutable_key(), &(cur.second));
+ }
+
+ dst->set_real_output(src->real_output);
+ dst->set_real_out_tx_key(key_to_string(src->real_out_tx_key));
+ for(auto & cur : src->real_out_additional_tx_keys){
+ dst->add_real_out_additional_tx_keys(key_to_string(cur));
+ }
+
+ dst->set_real_output_in_tx_index(src->real_output_in_tx_index);
+ dst->set_amount(src->amount);
+ dst->set_rct(src->rct);
+ dst->set_mask(key_to_string(src->mask));
+ translate_klrki(dst->mutable_multisig_klrki(), &(src->multisig_kLRki));
+ }
+
+ void translate_klrki(MoneroMultisigKLRki * dst, const rct::multisig_kLRki * src){
+ dst->set_k(key_to_string(src->k));
+ dst->set_l(key_to_string(src->L));
+ dst->set_r(key_to_string(src->R));
+ dst->set_ki(key_to_string(src->ki));
+ }
+
+ void translate_rct_key(MoneroRctKey * dst, const rct::ctkey * src){
+ dst->set_dest(key_to_string(src->dest));
+ dst->set_commitment(key_to_string(src->mask));
+ }
+
+ std::string hash_addr(const MoneroAccountPublicAddress * addr, boost::optional<uint64_t> amount, boost::optional<bool> is_subaddr){
+ return hash_addr(addr->spend_public_key(), addr->view_public_key(), amount, is_subaddr);
+ }
+
+ std::string hash_addr(const std::string & spend_key, const std::string & view_key, boost::optional<uint64_t> amount, boost::optional<bool> is_subaddr){
+ ::crypto::public_key spend{}, view{};
+ if (spend_key.size() != 32 || view_key.size() != 32){
+ throw std::invalid_argument("Public keys have invalid sizes");
+ }
+
+ memcpy(spend.data, spend_key.data(), 32);
+ memcpy(view.data, view_key.data(), 32);
+ return hash_addr(&spend, &view, amount, is_subaddr);
+ }
+
+ std::string hash_addr(const ::crypto::public_key * spend_key, const ::crypto::public_key * view_key, boost::optional<uint64_t> amount, boost::optional<bool> is_subaddr){
+ char buff[64+8+1];
+ size_t offset = 0;
+
+ memcpy(buff + offset, spend_key->data, 32); offset += 32;
+ memcpy(buff + offset, view_key->data, 32); offset += 32;
+
+ if (amount){
+ memcpy(buff + offset, (uint8_t*) &(amount.get()), sizeof(amount.get())); offset += sizeof(amount.get());
+ }
+
+ if (is_subaddr){
+ buff[offset] = is_subaddr.get();
+ offset += 1;
+ }
+
+ return std::string(buff, offset);
+ }
+
+ TData::TData() {
+ in_memory = false;
+ rsig_type = 0;
+ cur_input_idx = 0;
+ cur_output_idx = 0;
+ cur_batch_idx = 0;
+ cur_output_in_batch_idx = 0;
+ }
+
+ Signer::Signer(wallet_shim *wallet2, const unsigned_tx_set * unsigned_tx, size_t tx_idx, hw::tx_aux_data * aux_data) {
+ m_wallet2 = wallet2;
+ m_unsigned_tx = unsigned_tx;
+ m_aux_data = aux_data;
+ m_tx_idx = tx_idx;
+ m_ct.tx_data = cur_tx();
+ m_multisig = false;
+ }
+
+ void Signer::extract_payment_id(){
+ const std::vector<uint8_t>& tx_extra = cur_tx().extra;
+ m_ct.tsx_data.set_payment_id("");
+
+ std::vector<cryptonote::tx_extra_field> tx_extra_fields;
+ cryptonote::parse_tx_extra(tx_extra, tx_extra_fields); // ok if partially parsed
+ cryptonote::tx_extra_nonce extra_nonce;
+
+ ::crypto::hash payment_id{};
+ if (find_tx_extra_field_by_type(tx_extra_fields, extra_nonce))
+ {
+ ::crypto::hash8 payment_id8{};
+ if(cryptonote::get_encrypted_payment_id_from_tx_extra_nonce(extra_nonce.nonce, payment_id8))
+ {
+ m_ct.tsx_data.set_payment_id(std::string(payment_id8.data, 8));
+ }
+ else if (cryptonote::get_payment_id_from_tx_extra_nonce(extra_nonce.nonce, payment_id))
+ {
+ m_ct.tsx_data.set_payment_id(std::string(payment_id.data, 32));
+ }
+ }
+ }
+
+ static unsigned get_rsig_type(bool use_bulletproof, size_t num_outputs){
+ if (!use_bulletproof){
+ return rct::RangeProofBorromean;
+ } else if (num_outputs > BULLETPROOF_MAX_OUTPUTS){
+ return rct::RangeProofMultiOutputBulletproof;
+ } else {
+ return rct::RangeProofPaddedBulletproof;
+ }
+ }
+
+ static void generate_rsig_batch_sizes(std::vector<uint64_t> &batches, unsigned rsig_type, size_t num_outputs){
+ size_t amount_batched = 0;
+
+ while(amount_batched < num_outputs){
+ if (rsig_type == rct::RangeProofBorromean || rsig_type == rct::RangeProofBulletproof) {
+ batches.push_back(1);
+ amount_batched += 1;
+
+ } else if (rsig_type == rct::RangeProofPaddedBulletproof){
+ if (num_outputs > BULLETPROOF_MAX_OUTPUTS){
+ throw std::invalid_argument("BP padded can support only BULLETPROOF_MAX_OUTPUTS statements");
+ }
+ batches.push_back(num_outputs);
+ amount_batched += num_outputs;
+
+ } else if (rsig_type == rct::RangeProofMultiOutputBulletproof){
+ size_t batch_size = 1;
+ while (batch_size * 2 + amount_batched <= num_outputs && batch_size * 2 <= BULLETPROOF_MAX_OUTPUTS){
+ batch_size *= 2;
+ }
+ batch_size = std::min(batch_size, num_outputs - amount_batched);
+ batches.push_back(batch_size);
+ amount_batched += batch_size;
+
+ } else {
+ throw std::invalid_argument("Unknown rsig type");
+ }
+ }
+ }
+
+ void Signer::compute_integrated_indices(TsxData * tsx_data){
+ if (m_aux_data == nullptr || m_aux_data->tx_recipients.empty()){
+ return;
+ }
+
+ auto & chg = tsx_data->change_dts();
+ std::string change_hash = hash_addr(&chg.addr(), chg.amount(), chg.is_subaddress());
+
+ std::vector<uint32_t> integrated_indices;
+ std::set<std::string> integrated_hashes;
+ for (auto & cur : m_aux_data->tx_recipients){
+ if (!cur.has_payment_id){
+ continue;
+ }
+ integrated_hashes.emplace(hash_addr(&cur.address.m_spend_public_key, &cur.address.m_view_public_key));
+ }
+
+ ssize_t idx = -1;
+ for (auto & cur : tsx_data->outputs()){
+ idx += 1;
+
+ std::string c_hash = hash_addr(&cur.addr(), cur.amount(), cur.is_subaddress());
+ if (c_hash == change_hash || cur.is_subaddress()){
+ continue;
+ }
+
+ c_hash = hash_addr(&cur.addr());
+ if (integrated_hashes.find(c_hash) != integrated_hashes.end()){
+ integrated_indices.push_back((uint32_t)idx);
+ }
+ }
+
+ if (!integrated_indices.empty()){
+ assign_to_repeatable(tsx_data->mutable_integrated_indices(), integrated_indices.begin(), integrated_indices.end());
+ }
+ }
+
+ std::shared_ptr<messages::monero::MoneroTransactionInitRequest> Signer::step_init(){
+ // extract payment ID from construction data
+ auto & tsx_data = m_ct.tsx_data;
+ auto & tx = cur_tx();
+
+ m_ct.tx.version = 2;
+ m_ct.tx.unlock_time = tx.unlock_time;
+
+ tsx_data.set_version(1);
+ tsx_data.set_unlock_time(tx.unlock_time);
+ tsx_data.set_num_inputs(static_cast<google::protobuf::uint32>(tx.sources.size()));
+ tsx_data.set_mixin(static_cast<google::protobuf::uint32>(tx.sources[0].outputs.size() - 1));
+ tsx_data.set_account(tx.subaddr_account);
+ assign_to_repeatable(tsx_data.mutable_minor_indices(), tx.subaddr_indices.begin(), tx.subaddr_indices.end());
+
+ // Rsig decision
+ auto rsig_data = tsx_data.mutable_rsig_data();
+ m_ct.rsig_type = get_rsig_type(tx.use_bulletproofs, tx.splitted_dsts.size());
+ rsig_data->set_rsig_type(m_ct.rsig_type);
+
+ generate_rsig_batch_sizes(m_ct.grouping_vct, m_ct.rsig_type, tx.splitted_dsts.size());
+ assign_to_repeatable(rsig_data->mutable_grouping(), m_ct.grouping_vct.begin(), m_ct.grouping_vct.end());
+
+ translate_dst_entry(tsx_data.mutable_change_dts(), &(tx.change_dts));
+ for(auto & cur : tx.splitted_dsts){
+ auto dst = tsx_data.mutable_outputs()->Add();
+ translate_dst_entry(dst, &cur);
+ }
+
+ compute_integrated_indices(&tsx_data);
+
+ int64_t fee = 0;
+ for(auto & cur_in : tx.sources){
+ fee += cur_in.amount;
+ }
+ for(auto & cur_out : tx.splitted_dsts){
+ fee -= cur_out.amount;
+ }
+ if (fee < 0){
+ throw std::invalid_argument("Fee cannot be negative");
+ }
+
+ tsx_data.set_fee(static_cast<google::protobuf::uint64>(fee));
+ this->extract_payment_id();
+
+ auto init_req = std::make_shared<messages::monero::MoneroTransactionInitRequest>();
+ init_req->set_version(0);
+ init_req->mutable_tsx_data()->CopyFrom(tsx_data);
+ return init_req;
+ }
+
+ void Signer::step_init_ack(std::shared_ptr<const messages::monero::MoneroTransactionInitAck> ack){
+ m_ct.in_memory = false;
+ if (ack->has_rsig_data()){
+ m_ct.rsig_param = std::make_shared<MoneroRsigData>(ack->rsig_data());
+ }
+
+ assign_from_repeatable(&(m_ct.tx_out_entr_hmacs), ack->hmacs().begin(), ack->hmacs().end());
+ }
+
+ std::shared_ptr<messages::monero::MoneroTransactionSetInputRequest> Signer::step_set_input(size_t idx){
+ CHECK_AND_ASSERT_THROW_MES(idx < cur_tx().sources.size(), "Invalid source index");
+ m_ct.cur_input_idx = idx;
+ auto res = std::make_shared<messages::monero::MoneroTransactionSetInputRequest>();
+ translate_src_entry(res->mutable_src_entr(), &(cur_tx().sources[idx]));
+ return res;
+ }
+
+ void Signer::step_set_input_ack(std::shared_ptr<const messages::monero::MoneroTransactionSetInputAck> ack){
+ auto & vini_str = ack->vini();
+
+ cryptonote::txin_v vini;
+ if (!cn_deserialize(vini_str.data(), vini_str.size(), vini)){
+ throw exc::ProtocolException("Cannot deserialize vin[i]");
+ }
+
+ m_ct.tx.vin.emplace_back(vini);
+ m_ct.tx_in_hmacs.push_back(ack->vini_hmac());
+ m_ct.pseudo_outs.push_back(ack->pseudo_out());
+ m_ct.pseudo_outs_hmac.push_back(ack->pseudo_out_hmac());
+ m_ct.alphas.push_back(ack->pseudo_out_alpha());
+ m_ct.spend_encs.push_back(ack->spend_key());
+ }
+
+ void Signer::sort_ki(){
+ const size_t input_size = cur_tx().sources.size();
+
+ m_ct.source_permutation.clear();
+ for (size_t n = 0; n < input_size; ++n){
+ m_ct.source_permutation.push_back(n);
+ }
+
+ CHECK_AND_ASSERT_THROW_MES(m_ct.tx.vin.size() == input_size, "Invalid vector size");
+ std::sort(m_ct.source_permutation.begin(), m_ct.source_permutation.end(), [&](const size_t i0, const size_t i1) {
+ const cryptonote::txin_to_key &tk0 = boost::get<cryptonote::txin_to_key>(m_ct.tx.vin[i0]);
+ const cryptonote::txin_to_key &tk1 = boost::get<cryptonote::txin_to_key>(m_ct.tx.vin[i1]);
+ return memcmp(&tk0.k_image, &tk1.k_image, sizeof(tk0.k_image)) > 0;
+ });
+
+ CHECK_AND_ASSERT_THROW_MES(m_ct.tx_in_hmacs.size() == input_size, "Invalid vector size");
+ CHECK_AND_ASSERT_THROW_MES(m_ct.pseudo_outs.size() == input_size, "Invalid vector size");
+ CHECK_AND_ASSERT_THROW_MES(m_ct.pseudo_outs_hmac.size() == input_size, "Invalid vector size");
+ CHECK_AND_ASSERT_THROW_MES(m_ct.alphas.size() == input_size, "Invalid vector size");
+ CHECK_AND_ASSERT_THROW_MES(m_ct.spend_encs.size() == input_size, "Invalid vector size");
+ CHECK_AND_ASSERT_THROW_MES(m_ct.tx_data.sources.size() == input_size, "Invalid vector size");
+
+ tools::apply_permutation(m_ct.source_permutation, [&](size_t i0, size_t i1){
+ std::swap(m_ct.tx.vin[i0], m_ct.tx.vin[i1]);
+ std::swap(m_ct.tx_in_hmacs[i0], m_ct.tx_in_hmacs[i1]);
+ std::swap(m_ct.pseudo_outs[i0], m_ct.pseudo_outs[i1]);
+ std::swap(m_ct.pseudo_outs_hmac[i0], m_ct.pseudo_outs_hmac[i1]);
+ std::swap(m_ct.alphas[i0], m_ct.alphas[i1]);
+ std::swap(m_ct.spend_encs[i0], m_ct.spend_encs[i1]);
+ std::swap(m_ct.tx_data.sources[i0], m_ct.tx_data.sources[i1]);
+ });
+ }
+
+ std::shared_ptr<messages::monero::MoneroTransactionInputsPermutationRequest> Signer::step_permutation(){
+ sort_ki();
+
+ if (in_memory()){
+ return nullptr;
+ }
+
+ auto res = std::make_shared<messages::monero::MoneroTransactionInputsPermutationRequest>();
+ assign_to_repeatable(res->mutable_perm(), m_ct.source_permutation.begin(), m_ct.source_permutation.end());
+
+ return res;
+ }
+
+ void Signer::step_permutation_ack(std::shared_ptr<const messages::monero::MoneroTransactionInputsPermutationAck> ack){
+ if (in_memory()){
+ return;
+ }
+ }
+
+ std::shared_ptr<messages::monero::MoneroTransactionInputViniRequest> Signer::step_set_vini_input(size_t idx){
+ if (in_memory()){
+ return nullptr;
+ }
+ CHECK_AND_ASSERT_THROW_MES(idx < m_ct.tx_data.sources.size(), "Invalid transaction index");
+ CHECK_AND_ASSERT_THROW_MES(idx < m_ct.tx.vin.size(), "Invalid transaction index");
+ CHECK_AND_ASSERT_THROW_MES(idx < m_ct.tx_in_hmacs.size(), "Invalid transaction index");
+
+ m_ct.cur_input_idx = idx;
+ auto tx = m_ct.tx_data;
+ auto res = std::make_shared<messages::monero::MoneroTransactionInputViniRequest>();
+ auto & vini = m_ct.tx.vin[idx];
+ translate_src_entry(res->mutable_src_entr(), &(tx.sources[idx]));
+ res->set_vini(cryptonote::t_serializable_object_to_blob(vini));
+ res->set_vini_hmac(m_ct.tx_in_hmacs[idx]);
+ if (!in_memory()) {
+ CHECK_AND_ASSERT_THROW_MES(idx < m_ct.pseudo_outs.size(), "Invalid transaction index");
+ CHECK_AND_ASSERT_THROW_MES(idx < m_ct.pseudo_outs_hmac.size(), "Invalid transaction index");
+ res->set_pseudo_out(m_ct.pseudo_outs[idx]);
+ res->set_pseudo_out_hmac(m_ct.pseudo_outs_hmac[idx]);
+ }
+
+ return res;
+ }
+
+ void Signer::step_set_vini_input_ack(std::shared_ptr<const messages::monero::MoneroTransactionInputViniAck> ack){
+ if (in_memory()){
+ return;
+ }
+ }
+
+ std::shared_ptr<messages::monero::MoneroTransactionAllInputsSetRequest> Signer::step_all_inputs_set(){
+ return std::make_shared<messages::monero::MoneroTransactionAllInputsSetRequest>();
+ }
+
+ void Signer::step_all_inputs_set_ack(std::shared_ptr<const messages::monero::MoneroTransactionAllInputsSetAck> ack){
+ if (is_offloading()){
+ // If offloading, expect rsig configuration.
+ if (!ack->has_rsig_data()){
+ throw exc::ProtocolException("Rsig offloading requires rsig param");
+ }
+
+ auto & rsig_data = ack->rsig_data();
+ if (!rsig_data.has_mask()){
+ throw exc::ProtocolException("Gamma masks not present in offloaded version");
+ }
+
+ auto & mask = rsig_data.mask();
+ if (mask.size() != 32 * num_outputs()){
+ throw exc::ProtocolException("Invalid number of gamma masks");
+ }
+
+ m_ct.rsig_gamma.reserve(num_outputs());
+ for(size_t c=0; c < num_outputs(); ++c){
+ rct::key cmask{};
+ memcpy(cmask.bytes, mask.data() + c * 32, 32);
+ m_ct.rsig_gamma.emplace_back(cmask);
+ }
+ }
+ }
+
+ std::shared_ptr<messages::monero::MoneroTransactionSetOutputRequest> Signer::step_set_output(size_t idx){
+ CHECK_AND_ASSERT_THROW_MES(idx < m_ct.tx_data.splitted_dsts.size(), "Invalid transaction index");
+ CHECK_AND_ASSERT_THROW_MES(idx < m_ct.tx_out_entr_hmacs.size(), "Invalid transaction index");
+
+ m_ct.cur_output_idx = idx;
+ m_ct.cur_output_in_batch_idx += 1; // assumes sequential call to step_set_output()
+
+ auto res = std::make_shared<messages::monero::MoneroTransactionSetOutputRequest>();
+ auto & cur_dst = m_ct.tx_data.splitted_dsts[idx];
+ translate_dst_entry(res->mutable_dst_entr(), &cur_dst);
+ res->set_dst_entr_hmac(m_ct.tx_out_entr_hmacs[idx]);
+
+ // Range sig offloading to the host
+ if (!is_offloading()) {
+ return res;
+ }
+
+ CHECK_AND_ASSERT_THROW_MES(m_ct.cur_batch_idx < m_ct.grouping_vct.size(), "Invalid batch index");
+ if (m_ct.grouping_vct[m_ct.cur_batch_idx] > m_ct.cur_output_in_batch_idx) {
+ return res;
+ }
+
+ auto rsig_data = res->mutable_rsig_data();
+ auto batch_size = m_ct.grouping_vct[m_ct.cur_batch_idx];
+
+ if (!is_req_bulletproof()){
+ if (batch_size > 1){
+ throw std::invalid_argument("Borromean cannot batch outputs");
+ }
+
+ CHECK_AND_ASSERT_THROW_MES(idx < m_ct.rsig_gamma.size(), "Invalid gamma index");
+ rct::key C{}, mask = m_ct.rsig_gamma[idx];
+ auto genRsig = rct::proveRange(C, mask, cur_dst.amount); // TODO: rsig with given mask
+ auto serRsig = cn_serialize(genRsig);
+ m_ct.tx_out_rsigs.emplace_back(genRsig);
+ rsig_data->set_rsig(serRsig);
+
+ } else {
+ std::vector<uint64_t> amounts;
+ rct::keyV masks;
+ CHECK_AND_ASSERT_THROW_MES(idx + 1 >= batch_size, "Invalid index for batching");
+
+ for(size_t i = 0; i < batch_size; ++i){
+ const size_t bidx = 1 + idx - batch_size + i;
+ CHECK_AND_ASSERT_THROW_MES(bidx < m_ct.tx_data.splitted_dsts.size(), "Invalid gamma index");
+ CHECK_AND_ASSERT_THROW_MES(bidx < m_ct.rsig_gamma.size(), "Invalid gamma index");
+
+ amounts.push_back(m_ct.tx_data.splitted_dsts[bidx].amount);
+ masks.push_back(m_ct.rsig_gamma[bidx]);
+ }
+
+ auto bp = bulletproof_PROVE(amounts, masks);
+ auto serRsig = cn_serialize(bp);
+ m_ct.tx_out_rsigs.emplace_back(bp);
+ rsig_data->set_rsig(serRsig);
+ }
+
+ return res;
+ }
+
+ void Signer::step_set_output_ack(std::shared_ptr<const messages::monero::MoneroTransactionSetOutputAck> ack){
+ cryptonote::tx_out tx_out;
+ rct::rangeSig range_sig{};
+ rct::Bulletproof bproof{};
+ rct::ctkey out_pk{};
+ rct::ecdhTuple ecdh{};
+
+ bool has_rsig = false;
+ std::string rsig_buff;
+
+ if (ack->has_rsig_data()){
+ auto & rsig_data = ack->rsig_data();
+
+ if (rsig_data.has_rsig() && !rsig_data.rsig().empty()){
+ has_rsig = true;
+ rsig_buff = rsig_data.rsig();
+
+ } else if (rsig_data.rsig_parts_size() > 0){
+ has_rsig = true;
+ for (const auto &it : rsig_data.rsig_parts()) {
+ rsig_buff += it;
+ }
+ }
+ }
+
+ if (!cn_deserialize(ack->tx_out(), tx_out)){
+ throw exc::ProtocolException("Cannot deserialize vout[i]");
+ }
+
+ if (!cn_deserialize(ack->out_pk(), out_pk)){
+ throw exc::ProtocolException("Cannot deserialize out_pk");
+ }
+
+ if (!cn_deserialize(ack->ecdh_info(), ecdh)){
+ throw exc::ProtocolException("Cannot deserialize ecdhtuple");
+ }
+
+ if (has_rsig && !is_req_bulletproof() && !cn_deserialize(rsig_buff, range_sig)){
+ throw exc::ProtocolException("Cannot deserialize rangesig");
+ }
+
+ if (has_rsig && is_req_bulletproof() && !cn_deserialize(rsig_buff, bproof)){
+ throw exc::ProtocolException("Cannot deserialize bulletproof rangesig");
+ }
+
+ m_ct.tx.vout.emplace_back(tx_out);
+ m_ct.tx_out_hmacs.push_back(ack->vouti_hmac());
+ m_ct.tx_out_pk.emplace_back(out_pk);
+ m_ct.tx_out_ecdh.emplace_back(ecdh);
+
+ if (!has_rsig){
+ return;
+ }
+
+ if (is_req_bulletproof()){
+ CHECK_AND_ASSERT_THROW_MES(m_ct.cur_batch_idx < m_ct.grouping_vct.size(), "Invalid batch index");
+ auto batch_size = m_ct.grouping_vct[m_ct.cur_batch_idx];
+ for (size_t i = 0; i < batch_size; ++i){
+ const size_t bidx = 1 + m_ct.cur_output_idx - batch_size + i;
+ CHECK_AND_ASSERT_THROW_MES(bidx < m_ct.tx_out_pk.size(), "Invalid out index");
+
+ rct::key commitment = m_ct.tx_out_pk[bidx].mask;
+ commitment = rct::scalarmultKey(commitment, rct::INV_EIGHT);
+ bproof.V.push_back(commitment);
+ }
+
+ m_ct.tx_out_rsigs.emplace_back(bproof);
+ if (!rct::bulletproof_VERIFY(boost::get<rct::Bulletproof>(m_ct.tx_out_rsigs.back()))) {
+ throw exc::ProtocolException("Returned range signature is invalid");
+ }
+
+ } else {
+ m_ct.tx_out_rsigs.emplace_back(range_sig);
+
+ if (!rct::verRange(out_pk.mask, boost::get<rct::rangeSig>(m_ct.tx_out_rsigs.back()))) {
+ throw exc::ProtocolException("Returned range signature is invalid");
+ }
+ }
+
+ m_ct.cur_batch_idx += 1;
+ m_ct.cur_output_in_batch_idx = 0;
+ }
+
+ std::shared_ptr<messages::monero::MoneroTransactionAllOutSetRequest> Signer::step_all_outs_set(){
+ return std::make_shared<messages::monero::MoneroTransactionAllOutSetRequest>();
+ }
+
+ void Signer::step_all_outs_set_ack(std::shared_ptr<const messages::monero::MoneroTransactionAllOutSetAck> ack, hw::device &hwdev){
+ m_ct.rv = std::make_shared<rct::rctSig>();
+ m_ct.rv->txnFee = ack->rv().txn_fee();
+ m_ct.rv->type = static_cast<uint8_t>(ack->rv().rv_type());
+ string_to_key(m_ct.rv->message, ack->rv().message());
+
+ // Extra copy
+ m_ct.tx.extra.clear();
+ auto extra = ack->extra();
+ auto extra_data = extra.data();
+ m_ct.tx.extra.reserve(extra.size());
+ for(size_t i = 0; i < extra.size(); ++i){
+ m_ct.tx.extra.push_back(static_cast<uint8_t>(extra_data[i]));
+ }
+
+ ::crypto::hash tx_prefix_hash{};
+ cryptonote::get_transaction_prefix_hash(m_ct.tx, tx_prefix_hash);
+ m_ct.tx_prefix_hash = key_to_string(tx_prefix_hash);
+ if (crypto_verify_32(reinterpret_cast<const unsigned char *>(tx_prefix_hash.data),
+ reinterpret_cast<const unsigned char *>(ack->tx_prefix_hash().data()))){
+ throw exc::proto::SecurityException("Transaction prefix has does not match to the computed value");
+ }
+
+ // RctSig
+ auto num_sources = m_ct.tx_data.sources.size();
+ if (is_simple() || is_req_bulletproof()){
+ auto dst = &m_ct.rv->pseudoOuts;
+ if (is_bulletproof()){
+ dst = &m_ct.rv->p.pseudoOuts;
+ }
+
+ dst->clear();
+ for (const auto &pseudo_out : m_ct.pseudo_outs) {
+ dst->emplace_back();
+ string_to_key(dst->back(), pseudo_out);
+ }
+
+ m_ct.rv->mixRing.resize(num_sources);
+ } else {
+ m_ct.rv->mixRing.resize(m_ct.tsx_data.mixin());
+ m_ct.rv->mixRing[0].resize(num_sources);
+ }
+
+ CHECK_AND_ASSERT_THROW_MES(m_ct.tx_out_pk.size() == m_ct.tx_out_ecdh.size(), "Invalid vector sizes");
+ for(size_t i = 0; i < m_ct.tx_out_ecdh.size(); ++i){
+ m_ct.rv->outPk.push_back(m_ct.tx_out_pk[i]);
+ m_ct.rv->ecdhInfo.push_back(m_ct.tx_out_ecdh[i]);
+ }
+
+ for(size_t i = 0; i < m_ct.tx_out_rsigs.size(); ++i){
+ if (is_bulletproof()){
+ m_ct.rv->p.bulletproofs.push_back(boost::get<rct::Bulletproof>(m_ct.tx_out_rsigs[i]));
+ } else {
+ m_ct.rv->p.rangeSigs.push_back(boost::get<rct::rangeSig>(m_ct.tx_out_rsigs[i]));
+ }
+ }
+
+ rct::key hash_computed = rct::get_pre_mlsag_hash(*(m_ct.rv), hwdev);
+ auto & hash = ack->full_message_hash();
+
+ if (hash.size() != 32){
+ throw exc::ProtocolException("Returned mlsag hash has invalid size");
+ }
+
+ if (crypto_verify_32(reinterpret_cast<const unsigned char *>(hash_computed.bytes),
+ reinterpret_cast<const unsigned char *>(hash.data()))){
+ throw exc::proto::SecurityException("Computed MLSAG does not match");
+ }
+ }
+
+ std::shared_ptr<messages::monero::MoneroTransactionSignInputRequest> Signer::step_sign_input(size_t idx){
+ m_ct.cur_input_idx = idx;
+
+ CHECK_AND_ASSERT_THROW_MES(idx < m_ct.tx_data.sources.size(), "Invalid transaction index");
+ CHECK_AND_ASSERT_THROW_MES(idx < m_ct.tx.vin.size(), "Invalid transaction index");
+ CHECK_AND_ASSERT_THROW_MES(idx < m_ct.tx_in_hmacs.size(), "Invalid transaction index");
+ CHECK_AND_ASSERT_THROW_MES(idx < m_ct.alphas.size(), "Invalid transaction index");
+ CHECK_AND_ASSERT_THROW_MES(idx < m_ct.spend_encs.size(), "Invalid transaction index");
+
+ auto res = std::make_shared<messages::monero::MoneroTransactionSignInputRequest>();
+ translate_src_entry(res->mutable_src_entr(), &(m_ct.tx_data.sources[idx]));
+ res->set_vini(cryptonote::t_serializable_object_to_blob(m_ct.tx.vin[idx]));
+ res->set_vini_hmac(m_ct.tx_in_hmacs[idx]);
+ res->set_pseudo_out_alpha(m_ct.alphas[idx]);
+ res->set_spend_key(m_ct.spend_encs[idx]);
+ if (!in_memory()){
+ CHECK_AND_ASSERT_THROW_MES(idx < m_ct.pseudo_outs.size(), "Invalid transaction index");
+ CHECK_AND_ASSERT_THROW_MES(idx < m_ct.pseudo_outs_hmac.size(), "Invalid transaction index");
+ res->set_pseudo_out(m_ct.pseudo_outs[idx]);
+ res->set_pseudo_out_hmac(m_ct.pseudo_outs_hmac[idx]);
+ }
+ return res;
+ }
+
+ void Signer::step_sign_input_ack(std::shared_ptr<const messages::monero::MoneroTransactionSignInputAck> ack){
+ rct::mgSig mg;
+ if (!cn_deserialize(ack->signature(), mg)){
+ throw exc::ProtocolException("Cannot deserialize mg[i]");
+ }
+
+ m_ct.rv->p.MGs.push_back(mg);
+ }
+
+ std::shared_ptr<messages::monero::MoneroTransactionFinalRequest> Signer::step_final(){
+ m_ct.tx.rct_signatures = *(m_ct.rv);
+ return std::make_shared<messages::monero::MoneroTransactionFinalRequest>();
+ }
+
+ void Signer::step_final_ack(std::shared_ptr<const messages::monero::MoneroTransactionFinalAck> ack){
+ if (m_multisig){
+ auto & cout_key = ack->cout_key();
+ for(auto & cur : m_ct.couts){
+ if (cur.size() != 12 + 32){
+ throw std::invalid_argument("Encrypted cout has invalid length");
+ }
+
+ char buff[32];
+ auto data = cur.data();
+
+ crypto::chacha::decrypt(data + 12, 32, reinterpret_cast<const uint8_t *>(cout_key.data()), reinterpret_cast<const uint8_t *>(data), buff);
+ m_ct.couts_dec.emplace_back(buff, 32);
+ }
+ }
+
+ m_ct.enc_salt1 = ack->salt();
+ m_ct.enc_salt2 = ack->rand_mult();
+ m_ct.enc_keys = ack->tx_enc_keys();
+ }
+
+ std::string Signer::store_tx_aux_info(){
+ rapidjson::StringBuffer sb;
+ rapidjson::Writer<rapidjson::StringBuffer> writer(sb);
+
+ rapidjson::Document json;
+ json.SetObject();
+
+ rapidjson::Value valueS(rapidjson::kStringType);
+ rapidjson::Value valueI(rapidjson::kNumberType);
+
+ valueI.SetInt(1);
+ json.AddMember("version", valueI, json.GetAllocator());
+
+ valueS.SetString(m_ct.enc_salt1.c_str(), m_ct.enc_salt1.size());
+ json.AddMember("salt1", valueS, json.GetAllocator());
+
+ valueS.SetString(m_ct.enc_salt2.c_str(), m_ct.enc_salt2.size());
+ json.AddMember("salt2", valueS, json.GetAllocator());
+
+ valueS.SetString(m_ct.enc_keys.c_str(), m_ct.enc_keys.size());
+ json.AddMember("enc_keys", valueS, json.GetAllocator());
+
+ json.Accept(writer);
+ return sb.GetString();
+ }
+
+
+}
+}
+}
+}
diff --git a/src/device_trezor/trezor/protocol.hpp b/src/device_trezor/trezor/protocol.hpp
new file mode 100644
index 000000000..99211efed
--- /dev/null
+++ b/src/device_trezor/trezor/protocol.hpp
@@ -0,0 +1,300 @@
+// Copyright (c) 2017-2018, The Monero Project
+//
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without modification, are
+// permitted provided that the following conditions are met:
+//
+// 1. Redistributions of source code must retain the above copyright notice, this list of
+// conditions and the following disclaimer.
+//
+// 2. Redistributions in binary form must reproduce the above copyright notice, this list
+// of conditions and the following disclaimer in the documentation and/or other
+// materials provided with the distribution.
+//
+// 3. Neither the name of the copyright holder nor the names of its contributors may be
+// used to endorse or promote products derived from this software without specific
+// prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
+// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
+// THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
+// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+//
+
+#ifndef MONERO_PROTOCOL_H
+#define MONERO_PROTOCOL_H
+
+#include "trezor_defs.hpp"
+#include "device/device_cold.hpp"
+#include "messages_map.hpp"
+#include "transport.hpp"
+#include "wallet/wallet2.h"
+
+namespace hw{
+namespace trezor{
+namespace protocol{
+
+ std::string key_to_string(const ::crypto::ec_point & key);
+ std::string key_to_string(const ::crypto::ec_scalar & key);
+ std::string key_to_string(const ::crypto::hash & key);
+ std::string key_to_string(const ::rct::key & key);
+
+ void string_to_key(::crypto::ec_scalar & key, const std::string & str);
+ void string_to_key(::crypto::ec_point & key, const std::string & str);
+ void string_to_key(::rct::key & key, const std::string & str);
+
+ template<class sub_t, class InputIterator>
+ void assign_to_repeatable(::google::protobuf::RepeatedField<sub_t> * dst, const InputIterator begin, const InputIterator end){
+ for (InputIterator it = begin; it != end; it++) {
+ auto s = dst->Add();
+ *s = *it;
+ }
+ }
+
+ template<class sub_t, class InputIterator>
+ void assign_from_repeatable(std::vector<sub_t> * dst, const InputIterator begin, const InputIterator end){
+ for (InputIterator it = begin; it != end; it++) {
+ dst->push_back(*it);
+ }
+ };
+
+ template<typename T>
+ bool cn_deserialize(const void * buff, size_t len, T & dst){
+ std::stringstream ss;
+ ss.write(static_cast<const char *>(buff), len); //ss << tx_blob;
+ binary_archive<false> ba(ss);
+ bool r = ::serialization::serialize(ba, dst);
+ return r;
+ }
+
+ template<typename T>
+ bool cn_deserialize(const std::string & str, T & dst){
+ return cn_deserialize(str.data(), str.size(), dst);
+ }
+
+ template<typename T>
+ std::string cn_serialize(T & obj){
+ std::ostringstream oss;
+ binary_archive<true> oar(oss);
+ bool success = ::serialization::serialize(oar, obj);
+ if (!success){
+ throw exc::EncodingException("Could not CN serialize given object");
+ }
+ return oss.str();
+ }
+
+// Crypto / encryption
+namespace crypto {
+namespace chacha {
+
+ /**
+ * Chacha20Poly1305 decryption with tag verification. RFC 7539.
+ */
+ void decrypt(const void* ciphertext, size_t length, const uint8_t* key, const uint8_t* iv, char* plaintext);
+
+}
+}
+
+
+// Cold Key image sync
+namespace ki {
+
+ using MoneroTransferDetails = messages::monero::MoneroKeyImageSyncStepRequest_MoneroTransferDetails;
+ using MoneroSubAddressIndicesList = messages::monero::MoneroKeyImageExportInitRequest_MoneroSubAddressIndicesList;
+ using MoneroExportedKeyImage = messages::monero::MoneroKeyImageSyncStepAck_MoneroExportedKeyImage;
+ using exported_key_image = hw::device_cold::exported_key_image;
+
+ /**
+ * Converts transfer details to the MoneroTransferDetails required for KI sync
+ */
+ bool key_image_data(wallet_shim * wallet,
+ const std::vector<tools::wallet2::transfer_details> & transfers,
+ std::vector<MoneroTransferDetails> & res);
+
+ /**
+ * Computes a hash over MoneroTransferDetails. Commitment used in the KI sync.
+ */
+ std::string compute_hash(const MoneroTransferDetails & rr);
+
+ /**
+ * Generates KI sync request with commitments computed.
+ */
+ void generate_commitment(std::vector<MoneroTransferDetails> & mtds,
+ const std::vector<tools::wallet2::transfer_details> & transfers,
+ std::shared_ptr<messages::monero::MoneroKeyImageExportInitRequest> & req);
+
+}
+
+// Cold transaction signing
+namespace tx {
+ using TsxData = messages::monero::MoneroTransactionInitRequest_MoneroTransactionData;
+ using MoneroTransactionDestinationEntry = messages::monero::MoneroTransactionDestinationEntry;
+ using MoneroAccountPublicAddress = messages::monero::MoneroTransactionDestinationEntry_MoneroAccountPublicAddress;
+ using MoneroTransactionSourceEntry = messages::monero::MoneroTransactionSourceEntry;
+ using MoneroMultisigKLRki = messages::monero::MoneroTransactionSourceEntry_MoneroMultisigKLRki;
+ using MoneroOutputEntry = messages::monero::MoneroTransactionSourceEntry_MoneroOutputEntry;
+ using MoneroRctKey = messages::monero::MoneroTransactionSourceEntry_MoneroOutputEntry_MoneroRctKeyPublic;
+ using MoneroRsigData = messages::monero::MoneroTransactionRsigData;
+
+ using tx_construction_data = tools::wallet2::tx_construction_data;
+ using unsigned_tx_set = tools::wallet2::unsigned_tx_set;
+
+ void translate_address(MoneroAccountPublicAddress * dst, const cryptonote::account_public_address * src);
+ void translate_dst_entry(MoneroTransactionDestinationEntry * dst, const cryptonote::tx_destination_entry * src);
+ void translate_src_entry(MoneroTransactionSourceEntry * dst, const cryptonote::tx_source_entry * src);
+ void translate_klrki(MoneroMultisigKLRki * dst, const rct::multisig_kLRki * src);
+ void translate_rct_key(MoneroRctKey * dst, const rct::ctkey * src);
+ std::string hash_addr(const MoneroAccountPublicAddress * addr, boost::optional<uint64_t> amount = boost::none, boost::optional<bool> is_subaddr = boost::none);
+ std::string hash_addr(const std::string & spend_key, const std::string & view_key, boost::optional<uint64_t> amount = boost::none, boost::optional<bool> is_subaddr = boost::none);
+ std::string hash_addr(const ::crypto::public_key * spend_key, const ::crypto::public_key * view_key, boost::optional<uint64_t> amount = boost::none, boost::optional<bool> is_subaddr = boost::none);
+
+ typedef boost::variant<rct::rangeSig, rct::Bulletproof> rsig_v;
+
+ /**
+ * Transaction signer state holder.
+ */
+ class TData {
+ public:
+ TsxData tsx_data;
+ tx_construction_data tx_data;
+ cryptonote::transaction tx;
+ bool in_memory;
+ unsigned rsig_type;
+ std::vector<uint64_t> grouping_vct;
+ std::shared_ptr<MoneroRsigData> rsig_param;
+ size_t cur_input_idx;
+ size_t cur_output_idx;
+ size_t cur_batch_idx;
+ size_t cur_output_in_batch_idx;
+
+ std::vector<std::string> tx_in_hmacs;
+ std::vector<std::string> tx_out_entr_hmacs;
+ std::vector<std::string> tx_out_hmacs;
+ std::vector<rsig_v> tx_out_rsigs;
+ std::vector<rct::ctkey> tx_out_pk;
+ std::vector<rct::ecdhTuple> tx_out_ecdh;
+ std::vector<size_t> source_permutation;
+ std::vector<std::string> alphas;
+ std::vector<std::string> spend_encs;
+ std::vector<std::string> pseudo_outs;
+ std::vector<std::string> pseudo_outs_hmac;
+ std::vector<std::string> couts;
+ std::vector<std::string> couts_dec;
+ std::vector<rct::key> rsig_gamma;
+ std::string tx_prefix_hash;
+ std::string enc_salt1;
+ std::string enc_salt2;
+ std::string enc_keys;
+
+ std::shared_ptr<rct::rctSig> rv;
+
+ TData();
+ };
+
+ class Signer {
+ private:
+ TData m_ct;
+ wallet_shim * m_wallet2;
+
+ size_t m_tx_idx;
+ const unsigned_tx_set * m_unsigned_tx;
+ hw::tx_aux_data * m_aux_data;
+
+ bool m_multisig;
+
+ const tx_construction_data & cur_tx(){
+ CHECK_AND_ASSERT_THROW_MES(m_tx_idx < m_unsigned_tx->txes.size(), "Invalid transaction index");
+ return m_unsigned_tx->txes[m_tx_idx];
+ }
+
+ void extract_payment_id();
+ void compute_integrated_indices(TsxData * tsx_data);
+
+ public:
+ Signer(wallet_shim * wallet2, const unsigned_tx_set * unsigned_tx, size_t tx_idx = 0, hw::tx_aux_data * aux_data = nullptr);
+
+ std::shared_ptr<messages::monero::MoneroTransactionInitRequest> step_init();
+ void step_init_ack(std::shared_ptr<const messages::monero::MoneroTransactionInitAck> ack);
+
+ std::shared_ptr<messages::monero::MoneroTransactionSetInputRequest> step_set_input(size_t idx);
+ void step_set_input_ack(std::shared_ptr<const messages::monero::MoneroTransactionSetInputAck> ack);
+
+ void sort_ki();
+ std::shared_ptr<messages::monero::MoneroTransactionInputsPermutationRequest> step_permutation();
+ void step_permutation_ack(std::shared_ptr<const messages::monero::MoneroTransactionInputsPermutationAck> ack);
+
+ std::shared_ptr<messages::monero::MoneroTransactionInputViniRequest> step_set_vini_input(size_t idx);
+ void step_set_vini_input_ack(std::shared_ptr<const messages::monero::MoneroTransactionInputViniAck> ack);
+
+ std::shared_ptr<messages::monero::MoneroTransactionAllInputsSetRequest> step_all_inputs_set();
+ void step_all_inputs_set_ack(std::shared_ptr<const messages::monero::MoneroTransactionAllInputsSetAck> ack);
+
+ std::shared_ptr<messages::monero::MoneroTransactionSetOutputRequest> step_set_output(size_t idx);
+ void step_set_output_ack(std::shared_ptr<const messages::monero::MoneroTransactionSetOutputAck> ack);
+
+ std::shared_ptr<messages::monero::MoneroTransactionAllOutSetRequest> step_all_outs_set();
+ void step_all_outs_set_ack(std::shared_ptr<const messages::monero::MoneroTransactionAllOutSetAck> ack, hw::device &hwdev);
+
+ std::shared_ptr<messages::monero::MoneroTransactionSignInputRequest> step_sign_input(size_t idx);
+ void step_sign_input_ack(std::shared_ptr<const messages::monero::MoneroTransactionSignInputAck> ack);
+
+ std::shared_ptr<messages::monero::MoneroTransactionFinalRequest> step_final();
+ void step_final_ack(std::shared_ptr<const messages::monero::MoneroTransactionFinalAck> ack);
+
+ std::string store_tx_aux_info();
+
+ bool in_memory() const {
+ return m_ct.in_memory;
+ }
+
+ bool is_simple() const {
+ if (!m_ct.rv){
+ throw std::invalid_argument("RV not initialized");
+ }
+ auto tp = m_ct.rv->type;
+ return tp == rct::RCTTypeSimple;
+ }
+
+ bool is_req_bulletproof() const {
+ return m_ct.tx_data.use_bulletproofs;
+ }
+
+ bool is_bulletproof() const {
+ if (!m_ct.rv){
+ throw std::invalid_argument("RV not initialized");
+ }
+ auto tp = m_ct.rv->type;
+ return tp == rct::RCTTypeBulletproof;
+ }
+
+ bool is_offloading() const {
+ return m_ct.rsig_param && m_ct.rsig_param->offload_type() != 0;
+ }
+
+ size_t num_outputs() const {
+ return m_ct.tx_data.splitted_dsts.size();
+ }
+
+ size_t num_inputs() const {
+ return m_ct.tx_data.sources.size();
+ }
+
+ const TData & tdata() const {
+ return m_ct;
+ }
+ };
+
+}
+
+}
+}
+}
+
+
+#endif //MONERO_PROTOCOL_H
diff --git a/src/device_trezor/trezor/tools/README.md b/src/device_trezor/trezor/tools/README.md
new file mode 100644
index 000000000..91a8fb3f0
--- /dev/null
+++ b/src/device_trezor/trezor/tools/README.md
@@ -0,0 +1,36 @@
+# Trezor
+
+## Messages rebuild
+
+Install `protoc` for your distribution.
+
+- `protobuf-compiler`
+- `libprotobuf-dev`
+- `libprotoc-dev`
+- `python-protobuf`
+
+Python 3 is required. If you don't have python 3 quite an easy way is
+to use [pyenv].
+
+It is also advised to create own python virtual environment so dependencies
+are installed in this project-related virtual environment.
+
+```bash
+python -m venv /
+```
+
+Make sure your python has `protobuf` package installed
+
+```bash
+pip install protobuf
+```
+
+Regenerate messages:
+
+```
+./venv/bin/python3 src/device_trezor/trezor/tools/build_protob.py
+```
+
+The messages regeneration is done also automatically via cmake.
+
+[pyenv]: https://github.com/pyenv/pyenv
diff --git a/src/device_trezor/trezor/tools/build_protob.py b/src/device_trezor/trezor/tools/build_protob.py
new file mode 100644
index 000000000..2611f3296
--- /dev/null
+++ b/src/device_trezor/trezor/tools/build_protob.py
@@ -0,0 +1,38 @@
+#!/usr/bin/env python
+import os
+import subprocess
+import sys
+
+CWD = os.path.dirname(os.path.realpath(__file__))
+ROOT_DIR = os.path.abspath(os.path.join(CWD, "..", "..", "..", ".."))
+TREZOR_COMMON = os.path.join(ROOT_DIR, "external", "trezor-common")
+TREZOR_MESSAGES = os.path.join(CWD, "..", "messages")
+
+# check for existence of the submodule directory
+common_defs = os.path.join(TREZOR_COMMON, "defs")
+if not os.path.exists(common_defs):
+ raise ValueError(
+ "trezor-common submodule seems to be missing.\n"
+ + 'Use "git submodule update --init --recursive" to retrieve it.'
+ )
+
+# regenerate messages
+try:
+ selected = [
+ "messages.proto",
+ "messages-common.proto",
+ "messages-management.proto",
+ "messages-monero.proto",
+ ]
+ proto_srcs = [os.path.join(TREZOR_COMMON, "protob", x) for x in selected]
+ exec_args = [
+ sys.executable,
+ os.path.join(CWD, "pb2cpp.py"),
+ "-o",
+ TREZOR_MESSAGES,
+ ] + proto_srcs
+
+ subprocess.check_call(exec_args)
+
+except Exception as e:
+ raise
diff --git a/src/device_trezor/trezor/tools/pb2cpp.py b/src/device_trezor/trezor/tools/pb2cpp.py
new file mode 100644
index 000000000..eaa8a90ed
--- /dev/null
+++ b/src/device_trezor/trezor/tools/pb2cpp.py
@@ -0,0 +1,186 @@
+#!/usr/bin/env python3
+# Converts Google's protobuf python definitions of TREZOR wire messages
+# to plain-python objects as used in TREZOR Core and python-trezor
+
+import argparse
+import logging
+import os
+import re
+import shutil
+import subprocess
+import sys
+import glob
+import tempfile
+import hashlib
+
+
+AUTO_HEADER = "# Automatically generated by pb2cpp\n"
+
+# Fixing GCC7 compilation error
+UNDEF_STATEMENT = """
+#ifdef minor
+#undef minor
+#endif
+"""
+
+
+def which(pgm):
+ path = os.getenv('PATH')
+ for p in path.split(os.path.pathsep):
+ p = os.path.join(p, pgm)
+ if os.path.exists(p) and os.access(p, os.X_OK):
+ return p
+
+
+PROTOC = which("protoc")
+if not PROTOC:
+ print("protoc command not found")
+ sys.exit(1)
+
+PROTOC_PREFIX = os.path.dirname(os.path.dirname(PROTOC))
+PROTOC_INCLUDE = os.path.join(PROTOC_PREFIX, "include")
+
+
+def namespace_file(fpath, package):
+ """Adds / replaces package name. Simple regex parsing, may use https://github.com/ph4r05/plyprotobuf later"""
+ with open(fpath) as fh:
+ fdata = fh.read()
+
+ re_syntax = re.compile(r"^syntax\s*=")
+ re_package = re.compile(r"^package\s+([^;]+?)\s*;\s*$")
+ lines = fdata.split("\n")
+
+ line_syntax = None
+ line_package = None
+ for idx, line in enumerate(lines):
+ if line_syntax is None and re_syntax.match(line):
+ line_syntax = idx
+ if line_package is None and re_package.match(line):
+ line_package = idx
+
+ if package is None:
+ if line_package is None:
+ return
+ else:
+ lines.pop(line_package)
+
+ else:
+ new_package = "package %s;" % package
+ if line_package is None:
+ lines.insert(line_syntax + 1 if line_syntax is not None else 0, new_package)
+ else:
+ lines[line_package] = new_package
+
+ new_fdat = "\n".join(lines)
+ with open(fpath, "w+") as fh:
+ fh.write(new_fdat)
+ return new_fdat
+
+
+def protoc(files, out_dir, additional_includes=(), package=None, force=False):
+ """Compile code with protoc and return the data."""
+
+ include_dirs = set()
+ include_dirs.add(PROTOC_INCLUDE)
+ include_dirs.update(additional_includes)
+
+ with tempfile.TemporaryDirectory() as tmpdir_protob, tempfile.TemporaryDirectory() as tmpdir_out:
+ include_dirs.add(tmpdir_protob)
+
+ new_files = []
+ for file in files:
+ bname = os.path.basename(file)
+ tmp_file = os.path.join(tmpdir_protob, bname)
+
+ shutil.copy(file, tmp_file)
+ if package is not None:
+ namespace_file(tmp_file, package)
+ new_files.append(tmp_file)
+
+ protoc_includes = ["-I" + dir for dir in include_dirs if dir]
+
+ exec_args = (
+ [
+ PROTOC,
+ "--cpp_out",
+ tmpdir_out,
+ ]
+ + protoc_includes
+ + new_files
+ )
+
+ subprocess.check_call(exec_args)
+
+ # Fixing gcc compilation and clashes with "minor" field name
+ add_undef(tmpdir_out)
+
+ # Scan output dir, check file differences
+ update_message_files(tmpdir_out, out_dir, force)
+
+
+def update_message_files(tmpdir_out, out_dir, force=False):
+ files = glob.glob(os.path.join(tmpdir_out, '*.pb.*'))
+ for fname in files:
+ bname = os.path.basename(fname)
+ dest_file = os.path.join(out_dir, bname)
+ if not force and os.path.exists(dest_file):
+ data = open(fname, 'rb').read()
+ data_hash = hashlib.sha3_256(data).digest()
+ data_dest = open(dest_file, 'rb').read()
+ data_dest_hash = hashlib.sha3_256(data_dest).digest()
+ if data_hash == data_dest_hash:
+ continue
+
+ shutil.copy(fname, dest_file)
+
+
+def add_undef(out_dir):
+ files = glob.glob(os.path.join(out_dir, '*.pb.*'))
+ for fname in files:
+ with open(fname) as fh:
+ lines = fh.readlines()
+
+ idx_insertion = None
+ for idx in range(len(lines)):
+ if '@@protoc_insertion_point(includes)' in lines[idx]:
+ idx_insertion = idx
+ break
+
+ if idx_insertion is None:
+ pass
+
+ lines.insert(idx_insertion + 1, UNDEF_STATEMENT)
+ with open(fname, 'w') as fh:
+ fh.write("".join(lines))
+
+
+def strip_leader(s, prefix):
+ """Remove given prefix from underscored name."""
+ leader = prefix + "_"
+ if s.startswith(leader):
+ return s[len(leader) :]
+ else:
+ return s
+
+
+if __name__ == "__main__":
+ logging.basicConfig(level=logging.DEBUG)
+
+ parser = argparse.ArgumentParser()
+ # fmt: off
+ parser.add_argument("proto", nargs="+", help="Protobuf definition files")
+ parser.add_argument("-o", "--out-dir", help="Directory for generated source code")
+ parser.add_argument("-n", "--namespace", default=None, help="Message namespace")
+ parser.add_argument("-I", "--protoc-include", action="append", help="protoc include path")
+ parser.add_argument("-P", "--protobuf-module", default="protobuf", help="Name of protobuf module")
+ parser.add_argument("-f", "--force", default=False, help="Overwrite existing files")
+ # fmt: on
+ args = parser.parse_args()
+
+ protoc_includes = args.protoc_include or (os.environ.get("PROTOC_INCLUDE"),)
+
+ protoc(
+ args.proto, args.out_dir, protoc_includes, package=args.namespace, force=args.force
+ )
+
+
diff --git a/src/device_trezor/trezor/transport.cpp b/src/device_trezor/trezor/transport.cpp
new file mode 100644
index 000000000..fc86177e1
--- /dev/null
+++ b/src/device_trezor/trezor/transport.cpp
@@ -0,0 +1,651 @@
+// Copyright (c) 2017-2018, The Monero Project
+//
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without modification, are
+// permitted provided that the following conditions are met:
+//
+// 1. Redistributions of source code must retain the above copyright notice, this list of
+// conditions and the following disclaimer.
+//
+// 2. Redistributions in binary form must reproduce the above copyright notice, this list
+// of conditions and the following disclaimer in the documentation and/or other
+// materials provided with the distribution.
+//
+// 3. Neither the name of the copyright holder nor the names of its contributors may be
+// used to endorse or promote products derived from this software without specific
+// prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
+// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
+// THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
+// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+//
+
+#include <boost/endian/conversion.hpp>
+#include <boost/asio/io_service.hpp>
+#include <boost/asio/ip/udp.hpp>
+#include <boost/date_time/posix_time/posix_time_types.hpp>
+#include "transport.hpp"
+#include "messages/messages-common.pb.h"
+
+using namespace std;
+using json = rapidjson::Document;
+
+
+namespace hw{
+namespace trezor{
+
+ bool t_serialize(const std::string & in, std::string & out){
+ out = in;
+ return true;
+ }
+
+ bool t_serialize(const json_val & in, std::string & out){
+ rapidjson::StringBuffer sb;
+ rapidjson::Writer<rapidjson::StringBuffer> writer(sb);
+ in.Accept(writer);
+ out = sb.GetString();
+ return true;
+ }
+
+ std::string t_serialize(const json_val & in){
+ std::string ret;
+ t_serialize(in, ret);
+ return ret;
+ }
+
+ bool t_deserialize(const std::string & in, std::string & out){
+ out = in;
+ return true;
+ }
+
+ bool t_deserialize(const std::string & in, json & out){
+ if (out.Parse(in.c_str()).HasParseError()) {
+ throw exc::CommunicationException("JSON parse error");
+ }
+ return true;
+ }
+
+ static std::string json_get_string(const rapidjson::Value & in){
+ return std::string(in.GetString());
+ }
+
+ //
+ // Helpers
+ //
+
+#define PROTO_HEADER_SIZE 6
+
+ static size_t message_size(const google::protobuf::Message &req){
+ return static_cast<size_t>(req.ByteSize());
+ }
+
+ static size_t serialize_message_buffer_size(size_t msg_size) {
+ return PROTO_HEADER_SIZE + msg_size; // tag 2B + len 4B
+ }
+
+ static void serialize_message_header(void * buff, uint16_t tag, uint32_t len){
+ uint16_t wire_tag = boost::endian::native_to_big(static_cast<uint16_t>(tag));
+ uint32_t wire_len = boost::endian::native_to_big(static_cast<uint32_t>(len));
+ memcpy(buff, (void *) &wire_tag, 2);
+ memcpy((uint8_t*)buff + 2, (void *) &wire_len, 4);
+ }
+
+ static void deserialize_message_header(const void * buff, uint16_t & tag, uint32_t & len){
+ uint16_t wire_tag;
+ uint32_t wire_len;
+ memcpy(&wire_tag, buff, 2);
+ memcpy(&wire_len, (uint8_t*)buff + 2, 4);
+
+ tag = boost::endian::big_to_native(wire_tag);
+ len = boost::endian::big_to_native(wire_len);
+ }
+
+ static void serialize_message(const google::protobuf::Message &req, size_t msg_size, uint8_t * buff, size_t buff_size) {
+ auto msg_wire_num = MessageMapper::get_message_wire_number(req);
+ const auto req_buffer_size = serialize_message_buffer_size(msg_size);
+ if (req_buffer_size > buff_size){
+ throw std::invalid_argument("Buffer too small");
+ }
+
+ serialize_message_header(buff, msg_wire_num, msg_size);
+ if (!req.SerializeToArray(buff + 6, msg_size)){
+ throw exc::EncodingException("Message serialization error");
+ }
+ }
+
+ //
+ // Communication protocol
+ //
+
+#define REPLEN 64
+
+ void ProtocolV1::write(Transport & transport, const google::protobuf::Message & req){
+ const auto msg_size = message_size(req);
+ const auto buff_size = serialize_message_buffer_size(msg_size) + 2;
+
+ std::unique_ptr<uint8_t[]> req_buff(new uint8_t[buff_size]);
+ uint8_t * req_buff_raw = req_buff.get();
+ req_buff_raw[0] = '#';
+ req_buff_raw[1] = '#';
+
+ serialize_message(req, msg_size, req_buff_raw + 2, buff_size - 2);
+
+ size_t offset = 0;
+ uint8_t chunk_buff[REPLEN];
+
+ // Chunk by chunk upload
+ while(offset < buff_size){
+ auto to_copy = std::min((size_t)(buff_size - offset), (size_t)(REPLEN - 1));
+
+ chunk_buff[0] = '?';
+ memcpy(chunk_buff + 1, req_buff_raw + offset, to_copy);
+
+ // Pad with zeros
+ if (to_copy < REPLEN - 1){
+ memset(chunk_buff + 1 + to_copy, 0, REPLEN - 1 - to_copy);
+ }
+
+ transport.write_chunk(chunk_buff, REPLEN);
+ offset += REPLEN - 1;
+ }
+ }
+
+ void ProtocolV1::read(Transport & transport, std::shared_ptr<google::protobuf::Message> & msg, messages::MessageType * msg_type){
+ char chunk[REPLEN];
+
+ // Initial chunk read
+ size_t nread = transport.read_chunk(chunk, REPLEN);
+ if (nread != REPLEN){
+ throw exc::CommunicationException("Read chunk has invalid size");
+ }
+
+ if (strncmp(chunk, "?##", 3) != 0){
+ throw exc::CommunicationException("Malformed chunk");
+ }
+
+ uint16_t tag;
+ uint32_t len;
+ nread -= 3 + 6;
+ deserialize_message_header(chunk + 3, tag, len);
+
+ std::string data_acc(chunk + 3 + 6, nread);
+ data_acc.reserve(len);
+
+ while(nread < len){
+ const size_t cur = transport.read_chunk(chunk, REPLEN);
+ if (chunk[0] != '?'){
+ throw exc::CommunicationException("Chunk malformed");
+ }
+
+ data_acc.append(chunk + 1, cur - 1);
+ nread += cur - 1;
+ }
+
+ if (msg_type){
+ *msg_type = static_cast<messages::MessageType>(tag);
+ }
+
+ if (nread < len){
+ throw exc::CommunicationException("Response incomplete");
+ }
+
+ std::shared_ptr<google::protobuf::Message> msg_wrap(MessageMapper::get_message(tag));
+ if (!msg_wrap->ParseFromArray(data_acc.c_str(), len)){
+ throw exc::CommunicationException("Message could not be parsed");
+ }
+
+ msg = msg_wrap;
+ }
+
+ //
+ // Bridge transport
+ //
+
+ const char * BridgeTransport::PATH_PREFIX = "bridge:";
+
+ std::string BridgeTransport::get_path() const {
+ if (!m_device_path){
+ return "";
+ }
+
+ std::string path(PATH_PREFIX);
+ return path + m_device_path.get();
+ }
+
+ void BridgeTransport::enumerate(t_transport_vect & res) {
+ json bridge_res;
+ std::string req;
+
+ bool req_status = invoke_bridge_http("/enumerate", req, bridge_res, m_http_client);
+ if (!req_status){
+ throw exc::CommunicationException("Bridge enumeration failed");
+ }
+
+ for(rapidjson::Value::ConstValueIterator itr = bridge_res.Begin(); itr != bridge_res.End(); ++itr){
+ auto element = itr->GetObject();
+ auto t = std::make_shared<BridgeTransport>(boost::make_optional(json_get_string(element["path"])));
+ t->m_device_info.emplace();
+ t->m_device_info->CopyFrom(*itr, t->m_device_info->GetAllocator());
+ res.push_back(t);
+ }
+ }
+
+ void BridgeTransport::open() {
+ if (!m_device_path){
+ throw exc::CommunicationException("Coud not open, empty device path");
+ }
+
+ std::string uri = "/acquire/" + m_device_path.get() + "/null";
+ std::string req;
+ json bridge_res;
+ bool req_status = invoke_bridge_http(uri, req, bridge_res, m_http_client);
+ if (!req_status){
+ throw exc::CommunicationException("Failed to acquire device");
+ }
+
+ m_session = boost::make_optional(json_get_string(bridge_res["session"]));
+ }
+
+ void BridgeTransport::close() {
+ if (!m_device_path || !m_session){
+ throw exc::CommunicationException("Device not open");
+ }
+
+ std::string uri = "/release/" + m_session.get();
+ std::string req;
+ json bridge_res;
+ bool req_status = invoke_bridge_http(uri, req, bridge_res, m_http_client);
+ if (!req_status){
+ throw exc::CommunicationException("Failed to release device");
+ }
+
+ m_session = boost::none;
+ }
+
+ void BridgeTransport::write(const google::protobuf::Message &req) {
+ m_response = boost::none;
+
+ const auto msg_size = message_size(req);
+ const auto buff_size = serialize_message_buffer_size(msg_size);
+
+ std::unique_ptr<uint8_t[]> req_buff(new uint8_t[buff_size]);
+ uint8_t * req_buff_raw = req_buff.get();
+
+ serialize_message(req, msg_size, req_buff_raw, buff_size);
+
+ std::string uri = "/call/" + m_session.get();
+ std::string req_hex = epee::to_hex::string(epee::span<const std::uint8_t>(req_buff_raw, buff_size));
+ std::string res_hex;
+
+ bool req_status = invoke_bridge_http(uri, req_hex, res_hex, m_http_client);
+ if (!req_status){
+ throw exc::CommunicationException("Call method failed");
+ }
+
+ m_response = res_hex;
+ }
+
+ void BridgeTransport::read(std::shared_ptr<google::protobuf::Message> & msg, messages::MessageType * msg_type) {
+ if (!m_response){
+ throw exc::CommunicationException("Could not read, no response stored");
+ }
+
+ std::string bin_data;
+ if (!epee::string_tools::parse_hexstr_to_binbuff(m_response.get(), bin_data)){
+ throw exc::CommunicationException("Response is not well hexcoded");
+ }
+
+ uint16_t msg_tag;
+ uint32_t msg_len;
+ deserialize_message_header(bin_data.c_str(), msg_tag, msg_len);
+ if (bin_data.size() != msg_len + 6){
+ throw exc::CommunicationException("Response is not well hexcoded");
+ }
+
+ if (msg_type){
+ *msg_type = static_cast<messages::MessageType>(msg_tag);
+ }
+
+ std::shared_ptr<google::protobuf::Message> msg_wrap(MessageMapper::get_message(msg_tag));
+ if (!msg_wrap->ParseFromArray(bin_data.c_str() + 6, msg_len)){
+ throw exc::EncodingException("Response is not well hexcoded");
+ }
+ msg = msg_wrap;
+ }
+
+ const boost::optional<json> & BridgeTransport::device_info() const {
+ return m_device_info;
+ }
+
+ std::ostream& BridgeTransport::dump(std::ostream& o) const {
+ return o << "BridgeTransport<path=" << (m_device_path ? get_path() : "None")
+ << ", info=" << (m_device_info ? t_serialize(m_device_info.get()) : "None")
+ << ", session=" << (m_session ? m_session.get() : "None")
+ << ">";
+ }
+
+ //
+ // UdpTransport
+ //
+ const char * UdpTransport::PATH_PREFIX = "udp:";
+ const char * UdpTransport::DEFAULT_HOST = "127.0.0.1";
+ const int UdpTransport::DEFAULT_PORT = 21324;
+
+ UdpTransport::UdpTransport(boost::optional<std::string> device_path,
+ boost::optional<std::shared_ptr<Protocol>> proto) :
+ m_io_service(), m_deadline(m_io_service)
+ {
+ m_device_port = DEFAULT_PORT;
+ if (device_path) {
+ const std::string device_str = device_path.get();
+ auto delim = device_str.find(':');
+ if (delim == std::string::npos) {
+ m_device_host = device_str;
+ } else {
+ m_device_host = device_str.substr(0, delim);
+ m_device_port = std::stoi(device_str.substr(delim + 1));
+ }
+ } else {
+ m_device_host = DEFAULT_HOST;
+ }
+
+ if (m_device_port <= 1024 || m_device_port > 65535){
+ throw std::invalid_argument("Port number invalid");
+ }
+
+ if (m_device_host != "localhost" && m_device_host != DEFAULT_HOST){
+ throw std::invalid_argument("Local endpoint allowed only");
+ }
+
+ m_proto = proto ? proto.get() : std::make_shared<ProtocolV1>();
+ }
+
+ std::string UdpTransport::get_path() const {
+ std::string path(PATH_PREFIX);
+ return path + m_device_host + ":" + std::to_string(m_device_port);
+ }
+
+ void UdpTransport::require_socket(){
+ if (!m_socket){
+ throw exc::NotConnectedException("Socket not connected");
+ }
+ }
+
+ bool UdpTransport::ping(){
+ return ping_int();
+ }
+
+ bool UdpTransport::ping_int(boost::posix_time::time_duration timeout){
+ require_socket();
+ try {
+ std::string req = "PINGPING";
+ char res[8];
+
+ m_socket->send_to(boost::asio::buffer(req.c_str(), req.size()), m_endpoint);
+ receive(res, 8, nullptr, false, timeout);
+
+ return memcmp(res, "PONGPONG", 8) == 0;
+
+ } catch(...){
+ return false;
+ }
+ }
+
+ void UdpTransport::enumerate(t_transport_vect & res) {
+ std::shared_ptr<UdpTransport> t = std::make_shared<UdpTransport>();
+ bool t_works = false;
+
+ try{
+ t->open();
+ t_works = t->ping();
+ } catch(...) {
+
+ }
+ t->close();
+ if (t_works){
+ res.push_back(t);
+ }
+ }
+
+ void UdpTransport::open() {
+ udp::resolver resolver(m_io_service);
+ udp::resolver::query query(udp::v4(), m_device_host, std::to_string(m_device_port));
+ m_endpoint = *resolver.resolve(query);
+
+ m_socket.reset(new udp::socket(m_io_service));
+ m_socket->open(udp::v4());
+
+ m_deadline.expires_at(boost::posix_time::pos_infin);
+ check_deadline();
+
+ m_proto->session_begin(*this);
+ }
+
+ void UdpTransport::close() {
+ if (!m_socket){
+ throw exc::CommunicationException("Socket is already closed");
+ }
+
+ m_proto->session_end(*this);
+ m_socket->close();
+ m_socket = nullptr;
+ }
+
+ void UdpTransport::write_chunk(const void * buff, size_t size){
+ require_socket();
+
+ if (size != 64){
+ throw exc::CommunicationException("Invalid chunk size");
+ }
+
+ auto written = m_socket->send_to(boost::asio::buffer(buff, size), m_endpoint);
+ if (size != written){
+ throw exc::CommunicationException("Could not send the whole chunk");
+ }
+ }
+
+ size_t UdpTransport::read_chunk(void * buff, size_t size){
+ require_socket();
+ if (size < 64){
+ throw std::invalid_argument("Buffer too small");
+ }
+
+ ssize_t len;
+ while(true) {
+ try {
+ boost::system::error_code ec;
+ len = receive(buff, size, &ec, true);
+ if (ec == boost::asio::error::operation_aborted) {
+ continue;
+ } else if (ec) {
+ throw exc::CommunicationException(std::string("Comm error: ") + ec.message());
+ }
+
+ if (len != 64) {
+ throw exc::CommunicationException("Invalid chunk size");
+ }
+
+ break;
+
+ } catch(exc::CommunicationException const& e){
+ throw;
+ } catch(std::exception const& e){
+ MWARNING("Error reading chunk, reason: " << e.what());
+ throw exc::CommunicationException(std::string("Chunk read error: ") + std::string(e.what()));
+ }
+ }
+
+ return static_cast<size_t>(len);
+ }
+
+ ssize_t UdpTransport::receive(void * buff, size_t size, boost::system::error_code * error_code, bool no_throw, boost::posix_time::time_duration timeout){
+ boost::system::error_code ec;
+ boost::asio::mutable_buffer buffer = boost::asio::buffer(buff, size);
+
+ require_socket();
+
+ // Set a deadline for the asynchronous operation.
+ m_deadline.expires_from_now(timeout);
+
+ // Set up the variables that receive the result of the asynchronous
+ // operation. The error code is set to would_block to signal that the
+ // operation is incomplete. Asio guarantees that its asynchronous
+ // operations will never fail with would_block, so any other value in
+ // ec indicates completion.
+ ec = boost::asio::error::would_block;
+ std::size_t length = 0;
+
+ // Start the asynchronous operation itself. The handle_receive function
+ // used as a callback will update the ec and length variables.
+ m_socket->async_receive_from(boost::asio::buffer(buffer), m_endpoint,
+ boost::bind(&UdpTransport::handle_receive, _1, _2, &ec, &length));
+
+ // Block until the asynchronous operation has completed.
+ do {
+ m_io_service.run_one();
+ }
+ while (ec == boost::asio::error::would_block);
+
+ if (error_code){
+ *error_code = ec;
+ }
+
+ if (no_throw){
+ return length;
+ }
+
+ // Operation result
+ if (ec == boost::asio::error::operation_aborted){
+ throw exc::TimeoutException();
+
+ } else if (ec) {
+ MWARNING("Reading from UDP socket failed: " << ec.message());
+ throw exc::CommunicationException();
+
+ }
+
+ return length;
+ }
+
+ void UdpTransport::write(const google::protobuf::Message &req) {
+ m_proto->write(*this, req);
+ }
+
+ void UdpTransport::read(std::shared_ptr<google::protobuf::Message> & msg, messages::MessageType * msg_type) {
+ m_proto->read(*this, msg, msg_type);
+ }
+
+ void UdpTransport::check_deadline(){
+ if (!m_socket){
+ return; // no active socket.
+ }
+
+ // Check whether the deadline has passed. We compare the deadline against
+ // the current time since a new asynchronous operation may have moved the
+ // deadline before this actor had a chance to run.
+ if (m_deadline.expires_at() <= boost::asio::deadline_timer::traits_type::now())
+ {
+ // The deadline has passed. The outstanding asynchronous operation needs
+ // to be cancelled so that the blocked receive() function will return.
+ //
+ // Please note that cancel() has portability issues on some versions of
+ // Microsoft Windows, and it may be necessary to use close() instead.
+ // Consult the documentation for cancel() for further information.
+ m_socket->cancel();
+
+ // There is no longer an active deadline. The expiry is set to positive
+ // infinity so that the actor takes no action until a new deadline is set.
+ m_deadline.expires_at(boost::posix_time::pos_infin);
+ }
+
+ // Put the actor back to sleep.
+ m_deadline.async_wait(boost::bind(&UdpTransport::check_deadline, this));
+ }
+
+ void UdpTransport::handle_receive(const boost::system::error_code &ec, std::size_t length,
+ boost::system::error_code *out_ec, std::size_t *out_length) {
+ *out_ec = ec;
+ *out_length = length;
+ }
+
+ std::ostream& UdpTransport::dump(std::ostream& o) const {
+ return o << "UdpTransport<path=" << get_path()
+ << ", socket_alive=" << (m_socket ? "true" : "false")
+ << ">";
+ }
+
+ void enumerate(t_transport_vect & res){
+ BridgeTransport bt;
+ bt.enumerate(res);
+
+ hw::trezor::UdpTransport btu;
+ btu.enumerate(res);
+ }
+
+ std::shared_ptr<Transport> transport(const std::string & path){
+ if (boost::starts_with(path, BridgeTransport::PATH_PREFIX)){
+ return std::make_shared<BridgeTransport>(path.substr(strlen(BridgeTransport::PATH_PREFIX)));
+
+ } else if (boost::starts_with(path, UdpTransport::PATH_PREFIX)){
+ return std::make_shared<UdpTransport>(path.substr(strlen(UdpTransport::PATH_PREFIX)));
+
+ } else {
+ throw std::invalid_argument("Unknown Trezor device path: " + path);
+
+ }
+ }
+
+ void throw_failure_exception(const messages::common::Failure * failure) {
+ if (failure == nullptr){
+ throw std::invalid_argument("Failure message cannot be null");
+ }
+
+ boost::optional<std::string> message = failure->has_message() ? boost::make_optional(failure->message()) : boost::none;
+ boost::optional<uint32_t> code = failure->has_code() ? boost::make_optional(static_cast<uint32_t>(failure->code())) : boost::none;
+ if (!code){
+ throw exc::proto::FailureException(code, message);
+ }
+
+ auto ecode = failure->code();
+ if (ecode == messages::common::Failure_FailureType_Failure_UnexpectedMessage){
+ throw exc::proto::UnexpectedMessageException(code, message);
+ } else if (ecode == messages::common::Failure_FailureType_Failure_ActionCancelled){
+ throw exc::proto::CancelledException(code, message);
+ } else if (ecode == messages::common::Failure_FailureType_Failure_PinExpected){
+ throw exc::proto::PinExpectedException(code, message);
+ } else if (ecode == messages::common::Failure_FailureType_Failure_PinInvalid){
+ throw exc::proto::InvalidPinException(code, message);
+ } else if (ecode == messages::common::Failure_FailureType_Failure_NotEnoughFunds){
+ throw exc::proto::NotEnoughFundsException(code, message);
+ } else if (ecode == messages::common::Failure_FailureType_Failure_NotInitialized){
+ throw exc::proto::NotInitializedException(code, message);
+ } else if (ecode == messages::common::Failure_FailureType_Failure_FirmwareError){
+ throw exc::proto::FirmwareErrorException(code, message);
+ } else {
+ throw exc::proto::FailureException(code, message);
+ }
+ }
+
+ std::ostream& operator<<(std::ostream& o, hw::trezor::Transport const& t){
+ return t.dump(o);
+ }
+
+ std::ostream& operator<<(std::ostream& o, std::shared_ptr<hw::trezor::Transport> const& t){
+ if (!t){
+ return o << "None";
+ }
+
+ return t->dump(o);
+ }
+
+}
+}
+
+
diff --git a/src/device_trezor/trezor/transport.hpp b/src/device_trezor/trezor/transport.hpp
new file mode 100644
index 000000000..7b82fd06f
--- /dev/null
+++ b/src/device_trezor/trezor/transport.hpp
@@ -0,0 +1,331 @@
+// Copyright (c) 2017-2018, The Monero Project
+//
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without modification, are
+// permitted provided that the following conditions are met:
+//
+// 1. Redistributions of source code must retain the above copyright notice, this list of
+// conditions and the following disclaimer.
+//
+// 2. Redistributions in binary form must reproduce the above copyright notice, this list
+// of conditions and the following disclaimer in the documentation and/or other
+// materials provided with the distribution.
+//
+// 3. Neither the name of the copyright holder nor the names of its contributors may be
+// used to endorse or promote products derived from this software without specific
+// prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
+// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
+// THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
+// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+//
+
+#ifndef MONERO_TRANSPORT_H
+#define MONERO_TRANSPORT_H
+
+
+#include <boost/asio.hpp>
+#include <boost/asio/deadline_timer.hpp>
+#include <boost/array.hpp>
+#include <boost/utility/string_ref.hpp>
+
+#include <typeinfo>
+#include <type_traits>
+#include "net/http_client.h"
+
+#include "rapidjson/document.h"
+#include "rapidjson/writer.h"
+#include "rapidjson/stringbuffer.h"
+
+#include "exceptions.hpp"
+#include "trezor_defs.hpp"
+#include "messages_map.hpp"
+
+#include "messages/messages.pb.h"
+#include "messages/messages-common.pb.h"
+#include "messages/messages-management.pb.h"
+#include "messages/messages-monero.pb.h"
+
+namespace hw {
+namespace trezor {
+
+ using json = rapidjson::Document;
+ using json_val = rapidjson::Value;
+ namespace http = epee::net_utils::http;
+
+ const std::string DEFAULT_BRIDGE = "127.0.0.1:21325";
+
+ // Base HTTP comm serialization.
+ bool t_serialize(const std::string & in, std::string & out);
+ bool t_serialize(const json_val & in, std::string & out);
+ std::string t_serialize(const json_val & in);
+
+ bool t_deserialize(const std::string & in, std::string & out);
+ bool t_deserialize(const std::string & in, json & out);
+
+ // Flexible json serialization. HTTP client tailored for bridge API
+ template<class t_req, class t_res, class t_transport>
+ bool invoke_bridge_http(const boost::string_ref uri, const t_req & out_struct, t_res & result_struct, t_transport& transport, const boost::string_ref method = "POST", std::chrono::milliseconds timeout = std::chrono::seconds(180))
+ {
+ std::string req_param;
+ t_serialize(out_struct, req_param);
+
+ http::fields_list additional_params;
+ additional_params.push_back(std::make_pair("Origin","https://monero.trezor.io"));
+ additional_params.push_back(std::make_pair("Content-Type","application/json; charset=utf-8"));
+
+ const http::http_response_info* pri = nullptr;
+ if(!transport.invoke(uri, method, req_param, timeout, &pri, std::move(additional_params)))
+ {
+ MERROR("Failed to invoke http request to " << uri);
+ return false;
+ }
+
+ if(!pri)
+ {
+ MERROR("Failed to invoke http request to " << uri << ", internal error (null response ptr)");
+ return false;
+ }
+
+ if(pri->m_response_code != 200)
+ {
+ MERROR("Failed to invoke http request to " << uri << ", wrong response code: " << pri->m_response_code
+ << " Response Body: " << pri->m_body);
+ return false;
+ }
+
+ return t_deserialize(pri->m_body, result_struct);
+ }
+
+ // Forward decl
+ class Transport;
+ class Protocol;
+
+ // Communication protocol
+ class Protocol {
+ public:
+ Protocol() = default;
+ virtual ~Protocol() = default;
+ virtual void session_begin(Transport & transport){ };
+ virtual void session_end(Transport & transport){ };
+ virtual void write(Transport & transport, const google::protobuf::Message & req)= 0;
+ virtual void read(Transport & transport, std::shared_ptr<google::protobuf::Message> & msg, messages::MessageType * msg_type=nullptr)= 0;
+ };
+
+ class ProtocolV1 : public Protocol {
+ public:
+ ProtocolV1() = default;
+ virtual ~ProtocolV1() = default;
+
+ void write(Transport & transport, const google::protobuf::Message & req) override;
+ void read(Transport & transport, std::shared_ptr<google::protobuf::Message> & msg, messages::MessageType * msg_type=nullptr) override;
+ };
+
+
+ // Base transport
+ typedef std::vector<std::shared_ptr<Transport>> t_transport_vect;
+
+ class Transport {
+ public:
+ Transport() = default;
+ virtual ~Transport() = default;
+
+ virtual bool ping() { return false; };
+ virtual std::string get_path() const { return ""; };
+ virtual void enumerate(t_transport_vect & res){};
+ virtual void open(){};
+ virtual void close(){};
+ virtual void write(const google::protobuf::Message & req) =0;
+ virtual void read(std::shared_ptr<google::protobuf::Message> & msg, messages::MessageType * msg_type=nullptr) =0;
+
+ virtual void write_chunk(const void * buff, size_t size) { };
+ virtual size_t read_chunk(void * buff, size_t size) { return 0; };
+ virtual std::ostream& dump(std::ostream& o) const { return o << "Transport<>"; }
+ };
+
+ // Bridge transport
+ class BridgeTransport : public Transport {
+ public:
+ BridgeTransport(
+ boost::optional<std::string> device_path = boost::none,
+ boost::optional<std::string> bridge_host = boost::none):
+ m_device_path(device_path),
+ m_bridge_host(bridge_host ? bridge_host.get() : DEFAULT_BRIDGE),
+ m_response(boost::none),
+ m_session(boost::none),
+ m_device_info(boost::none)
+ {
+ m_http_client.set_server(m_bridge_host, boost::none, false);
+ }
+
+ virtual ~BridgeTransport() = default;
+
+ static const char * PATH_PREFIX;
+
+ std::string get_path() const override;
+ void enumerate(t_transport_vect & res) override;
+
+ void open() override;
+ void close() override;
+
+ void write(const google::protobuf::Message &req) override;
+ void read(std::shared_ptr<google::protobuf::Message> & msg, messages::MessageType * msg_type=nullptr) override;
+
+ const boost::optional<json> & device_info() const;
+ std::ostream& dump(std::ostream& o) const override;
+
+ private:
+ epee::net_utils::http::http_simple_client m_http_client;
+ std::string m_bridge_host;
+ boost::optional<std::string> m_device_path;
+ boost::optional<std::string> m_session;
+ boost::optional<std::string> m_response;
+ boost::optional<json> m_device_info;
+ };
+
+ // UdpTransport transport
+ using boost::asio::ip::udp;
+
+ class UdpTransport : public Transport {
+ public:
+
+ explicit UdpTransport(
+ boost::optional<std::string> device_path=boost::none,
+ boost::optional<std::shared_ptr<Protocol>> proto=boost::none);
+
+ virtual ~UdpTransport() = default;
+
+ static const char * PATH_PREFIX;
+ static const char * DEFAULT_HOST;
+ static const int DEFAULT_PORT;
+
+ bool ping() override;
+ std::string get_path() const override;
+ void enumerate(t_transport_vect & res) override;
+
+ void open() override;
+ void close() override;
+
+ void write(const google::protobuf::Message &req) override;
+ void read(std::shared_ptr<google::protobuf::Message> & msg, messages::MessageType * msg_type=nullptr) override;
+
+ void write_chunk(const void * buff, size_t size) override;
+ size_t read_chunk(void * buff, size_t size) override;
+
+ std::ostream& dump(std::ostream& o) const override;
+
+ private:
+ void require_socket();
+ ssize_t receive(void * buff, size_t size, boost::system::error_code * error_code=nullptr, bool no_throw=false, boost::posix_time::time_duration timeout=boost::posix_time::seconds(10));
+ void check_deadline();
+ static void handle_receive(const boost::system::error_code& ec, std::size_t length,
+ boost::system::error_code* out_ec, std::size_t* out_length);
+ bool ping_int(boost::posix_time::time_duration timeout=boost::posix_time::milliseconds(1500));
+
+ std::shared_ptr<Protocol> m_proto;
+ std::string m_device_host;
+ int m_device_port;
+
+ std::unique_ptr<udp::socket> m_socket;
+ boost::asio::io_service m_io_service;
+ boost::asio::deadline_timer m_deadline;
+ udp::endpoint m_endpoint;
+ };
+
+ //
+ // General helpers
+ //
+
+ /**
+ * Enumerates all transports
+ */
+ void enumerate(t_transport_vect & res);
+
+ /**
+ * Transforms path to the transport
+ */
+ std::shared_ptr<Transport> transport(const std::string & path);
+
+ /**
+ * Transforms path to the particular transport
+ */
+ template<class t_transport>
+ std::shared_ptr<t_transport> transport_typed(const std::string & path){
+ auto t = transport(path);
+ if (!t){
+ return nullptr;
+ }
+
+ return std::dynamic_pointer_cast<t_transport>(t);
+ }
+
+ // Exception carries unexpected message being received
+ namespace exc {
+ class UnexpectedMessageException: public ProtocolException {
+ protected:
+ hw::trezor::messages::MessageType recvType;
+ std::shared_ptr<google::protobuf::Message> recvMsg;
+
+ public:
+ using ProtocolException::ProtocolException;
+ UnexpectedMessageException(): ProtocolException("Trezor returned unexpected message") {};
+ UnexpectedMessageException(hw::trezor::messages::MessageType recvType,
+ const std::shared_ptr<google::protobuf::Message> & recvMsg)
+ : recvType(recvType), recvMsg(recvMsg) {
+ reason = std::string("Trezor returned unexpected message: ") + std::to_string(recvType);
+ }
+ };
+ }
+
+ /**
+ * Throws corresponding failure exception.
+ */
+ [[ noreturn ]] void throw_failure_exception(const messages::common::Failure * failure);
+
+ /**
+ * Simple wrapper for write-read message exchange with expected message response type.
+ *
+ * @throws UnexpectedMessageException if the response message type is different than expected.
+ * Exception contains message type and the message itself.
+ */
+ template<class t_message>
+ std::shared_ptr<t_message>
+ exchange_message(Transport & transport, const google::protobuf::Message & req,
+ boost::optional<messages::MessageType> resp_type = boost::none)
+ {
+ // Require strictly protocol buffers response in the template.
+ BOOST_STATIC_ASSERT(boost::is_base_of<google::protobuf::Message, t_message>::value);
+
+ // Write the request
+ transport.write(req);
+
+ // Read the response
+ std::shared_ptr<google::protobuf::Message> msg_resp;
+ hw::trezor::messages::MessageType msg_resp_type;
+ transport.read(msg_resp, &msg_resp_type);
+
+ // Determine type of expected message response
+ messages::MessageType required_type = resp_type ? resp_type.get() : MessageMapper::get_message_wire_number<t_message>();
+
+ if (msg_resp_type == required_type) {
+ return message_ptr_retype<t_message>(msg_resp);
+ } else if (msg_resp_type == messages::MessageType_Failure){
+ throw_failure_exception(dynamic_cast<messages::common::Failure*>(msg_resp.get()));
+ } else {
+ throw exc::UnexpectedMessageException(msg_resp_type, msg_resp);
+ }
+ }
+
+ std::ostream& operator<<(std::ostream& o, hw::trezor::Transport const& t);
+ std::ostream& operator<<(std::ostream& o, std::shared_ptr<hw::trezor::Transport> const& t);
+}}
+
+
+#endif //MONERO_TRANSPORT_H
diff --git a/src/device_trezor/trezor/trezor_defs.hpp b/src/device_trezor/trezor/trezor_defs.hpp
new file mode 100644
index 000000000..951a8f802
--- /dev/null
+++ b/src/device_trezor/trezor/trezor_defs.hpp
@@ -0,0 +1,48 @@
+// Copyright (c) 2017-2018, The Monero Project
+//
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without modification, are
+// permitted provided that the following conditions are met:
+//
+// 1. Redistributions of source code must retain the above copyright notice, this list of
+// conditions and the following disclaimer.
+//
+// 2. Redistributions in binary form must reproduce the above copyright notice, this list
+// of conditions and the following disclaimer in the documentation and/or other
+// materials provided with the distribution.
+//
+// 3. Neither the name of the copyright holder nor the names of its contributors may be
+// used to endorse or promote products derived from this software without specific
+// prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
+// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
+// THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
+// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+//
+
+#if defined(HAVE_PROTOBUF) && !defined(WITHOUT_TREZOR)
+ #define WITH_DEVICE_TREZOR 1
+#else
+ #define WITH_DEVICE_TREZOR 0
+#endif
+
+#ifndef WITH_DEVICE_TREZOR_LITE
+#define WITH_DEVICE_TREZOR_LITE 0
+#endif
+
+// Avoids protobuf undefined macro warning
+#ifndef PROTOBUF_INLINE_NOT_IN_HEADERS
+#define PROTOBUF_INLINE_NOT_IN_HEADERS 0
+#endif
+
+// Fixes gcc7 problem with minor macro defined clashing with minor() field.
+#ifdef minor
+#undef minor
+#endif
diff --git a/src/mnemonics/electrum-words.cpp b/src/mnemonics/electrum-words.cpp
index 3d6338856..496e26858 100644
--- a/src/mnemonics/electrum-words.cpp
+++ b/src/mnemonics/electrum-words.cpp
@@ -215,7 +215,7 @@ namespace
}
boost::crc_32_type result;
result.process_bytes(trimmed_words.data(), trimmed_words.length());
- return result.checksum() % crypto::ElectrumWords::seed_length;
+ return result.checksum() % word_list.size();
}
/*!
diff --git a/src/p2p/net_node.inl b/src/p2p/net_node.inl
index 9390626a8..a61b6107f 100644
--- a/src/p2p/net_node.inl
+++ b/src/p2p/net_node.inl
@@ -655,10 +655,14 @@ namespace nodetool
{
kill();
m_peerlist.deinit();
- m_net_server.deinit_server();
- // remove UPnP port mapping
- if(!m_no_igd)
- delete_upnp_port_mapping(m_listening_port);
+
+ if (!m_offline)
+ {
+ m_net_server.deinit_server();
+ // remove UPnP port mapping
+ if(!m_no_igd)
+ delete_upnp_port_mapping(m_listening_port);
+ }
return store_config();
}
//-----------------------------------------------------------------------------------
@@ -2042,7 +2046,7 @@ namespace nodetool
char lanAddress[64];
result = UPNP_GetValidIGD(deviceList, &urls, &igdData, lanAddress, sizeof lanAddress);
freeUPNPDevlist(deviceList);
- if (result != 0) {
+ if (result > 0) {
if (result == 1) {
std::ostringstream portString;
portString << port;
@@ -2088,7 +2092,7 @@ namespace nodetool
char lanAddress[64];
result = UPNP_GetValidIGD(deviceList, &urls, &igdData, lanAddress, sizeof lanAddress);
freeUPNPDevlist(deviceList);
- if (result != 0) {
+ if (result > 0) {
if (result == 1) {
std::ostringstream portString;
portString << port;
diff --git a/src/ringct/rctSigs.cpp b/src/ringct/rctSigs.cpp
index 0d1789a38..181e89c45 100644
--- a/src/ringct/rctSigs.cpp
+++ b/src/ringct/rctSigs.cpp
@@ -44,6 +44,19 @@ using namespace std;
#define CHECK_AND_ASSERT_MES_L1(expr, ret, message) {if(!(expr)) {MCERROR("verify", message); return ret;}}
+namespace
+{
+ rct::Bulletproof make_dummy_bulletproof(size_t n_outs)
+ {
+ const rct::key I = rct::identity();
+ size_t nrl = 0;
+ while ((1u << nrl) < n_outs)
+ ++nrl;
+ nrl += 6;
+ return rct::Bulletproof{rct::keyV(n_outs, I), I, I, I, I, I, I, rct::keyV(nrl, I), rct::keyV(nrl, I), I, I, I};
+ }
+}
+
namespace rct {
Bulletproof proveRangeBulletproof(key &C, key &mask, uint64_t amount)
{
@@ -762,10 +775,20 @@ namespace rct {
if (range_proof_type == RangeProofPaddedBulletproof)
{
rct::keyV C, masks;
- rv.p.bulletproofs.push_back(proveRangeBulletproof(C, masks, outamounts));
- #ifdef DBG
- CHECK_AND_ASSERT_THROW_MES(verBulletproof(rv.p.bulletproofs.back()), "verBulletproof failed on newly created proof");
- #endif
+ if (hwdev.get_mode() == hw::device::TRANSACTION_CREATE_FAKE)
+ {
+ // use a fake bulletproof for speed
+ rv.p.bulletproofs.push_back(make_dummy_bulletproof(outamounts.size()));
+ C = rct::keyV(outamounts.size(), I);
+ masks = rct::keyV(outamounts.size(), I);
+ }
+ else
+ {
+ rv.p.bulletproofs.push_back(proveRangeBulletproof(C, masks, outamounts));
+ #ifdef DBG
+ CHECK_AND_ASSERT_THROW_MES(verBulletproof(rv.p.bulletproofs.back()), "verBulletproof failed on newly created proof");
+ #endif
+ }
for (i = 0; i < outamounts.size(); ++i)
{
rv.outPk[i].mask = rct::scalarmult8(C[i]);
@@ -782,10 +805,20 @@ namespace rct {
std::vector<uint64_t> batch_amounts(batch_size);
for (i = 0; i < batch_size; ++i)
batch_amounts[i] = outamounts[i + amounts_proved];
- rv.p.bulletproofs.push_back(proveRangeBulletproof(C, masks, batch_amounts));
- #ifdef DBG
- CHECK_AND_ASSERT_THROW_MES(verBulletproof(rv.p.bulletproofs.back()), "verBulletproof failed on newly created proof");
- #endif
+ if (hwdev.get_mode() == hw::device::TRANSACTION_CREATE_FAKE)
+ {
+ // use a fake bulletproof for speed
+ rv.p.bulletproofs.push_back(make_dummy_bulletproof(batch_amounts.size()));
+ C = rct::keyV(batch_amounts.size(), I);
+ masks = rct::keyV(batch_amounts.size(), I);
+ }
+ else
+ {
+ rv.p.bulletproofs.push_back(proveRangeBulletproof(C, masks, batch_amounts));
+ #ifdef DBG
+ CHECK_AND_ASSERT_THROW_MES(verBulletproof(rv.p.bulletproofs.back()), "verBulletproof failed on newly created proof");
+ #endif
+ }
for (i = 0; i < batch_size; ++i)
{
rv.outPk[i + amounts_proved].mask = rct::scalarmult8(C[i]);
diff --git a/src/ringct/rctSigs.h b/src/ringct/rctSigs.h
index ae8bb91d7..b67a0b992 100644
--- a/src/ringct/rctSigs.h
+++ b/src/ringct/rctSigs.h
@@ -133,7 +133,7 @@ namespace rct {
xmr_amount decodeRct(const rctSig & rv, const key & sk, unsigned int i, hw::device &hwdev);
xmr_amount decodeRctSimple(const rctSig & rv, const key & sk, unsigned int i, key & mask, hw::device &hwdev);
xmr_amount decodeRctSimple(const rctSig & rv, const key & sk, unsigned int i, hw::device &hwdev);
-
+ key get_pre_mlsag_hash(const rctSig &rv, hw::device &hwdev);
bool signMultisig(rctSig &rv, const std::vector<unsigned int> &indices, const keyV &k, const multisig_out &msout, const key &secret_key);
}
#endif /* RCTSIGS_H */
diff --git a/src/rpc/core_rpc_server.cpp b/src/rpc/core_rpc_server.cpp
index dc3860d60..574f6c126 100644
--- a/src/rpc/core_rpc_server.cpp
+++ b/src/rpc/core_rpc_server.cpp
@@ -213,23 +213,15 @@ namespace cryptonote
return true;
}
//------------------------------------------------------------------------------------------------------------------------------
- static cryptonote::blobdata get_pruned_tx_blob(cryptonote::transaction &tx)
- {
- std::stringstream ss;
- binary_archive<true> ba(ss);
- bool r = tx.serialize_base(ba);
- CHECK_AND_ASSERT_MES(r, cryptonote::blobdata(), "Failed to serialize rct signatures base");
- return ss.str();
- }
- //------------------------------------------------------------------------------------------------------------------------------
- static cryptonote::blobdata get_pruned_tx_json(cryptonote::transaction &tx)
- {
- std::stringstream ss;
- json_archive<true> ar(ss);
- bool r = tx.serialize_base(ar);
- CHECK_AND_ASSERT_MES(r, cryptonote::blobdata(), "Failed to serialize rct signatures base");
- return ss.str();
- }
+ class pruned_transaction {
+ transaction& tx;
+ public:
+ pruned_transaction(transaction& tx) : tx(tx) {}
+ BEGIN_SERIALIZE_OBJECT()
+ bool r = tx.serialize_base(ar);
+ if (!r) return false;
+ END_SERIALIZE()
+ };
//------------------------------------------------------------------------------------------------------------------------------
bool core_rpc_server::on_get_blocks(const COMMAND_RPC_GET_BLOCKS_FAST::request& req, COMMAND_RPC_GET_BLOCKS_FAST::response& res)
{
@@ -565,10 +557,11 @@ namespace cryptonote
crypto::hash tx_hash = *vhi++;
e.tx_hash = *txhi++;
- blobdata blob = req.prune ? get_pruned_tx_blob(tx) : t_serializable_object_to_blob(tx);
+ pruned_transaction pruned_tx{tx};
+ blobdata blob = req.prune ? t_serializable_object_to_blob(pruned_tx) : t_serializable_object_to_blob(tx);
e.as_hex = string_tools::buff_to_hex_nodelimer(blob);
if (req.decode_as_json)
- e.as_json = req.prune ? get_pruned_tx_json(tx) : obj_to_json_str(tx);
+ e.as_json = req.prune ? obj_to_json_str(pruned_tx) : obj_to_json_str(tx);
e.in_pool = pool_tx_hashes.find(tx_hash) != pool_tx_hashes.end();
if (e.in_pool)
{
diff --git a/src/rpc/core_rpc_server_commands_defs.h b/src/rpc/core_rpc_server_commands_defs.h
index e8720cb14..8e8df7a52 100644
--- a/src/rpc/core_rpc_server_commands_defs.h
+++ b/src/rpc/core_rpc_server_commands_defs.h
@@ -783,9 +783,6 @@ namespace cryptonote
std::string tx_as_hex;
bool do_not_relay;
- request() {}
- explicit request(const transaction &);
-
BEGIN_KV_SERIALIZE_MAP()
KV_SERIALIZE(tx_as_hex)
KV_SERIALIZE_OPT(do_not_relay, false)
diff --git a/src/rpc/daemon_messages.h b/src/rpc/daemon_messages.h
index 70f4c50f1..d2014247c 100644
--- a/src/rpc/daemon_messages.h
+++ b/src/rpc/daemon_messages.h
@@ -68,7 +68,7 @@ class classname \
// NOTE: when using a type with multiple template parameters,
// replace any comma in the template specifier with the macro
// above, or the preprocessor will eat the comma in a bad way.
-#define RPC_MESSAGE_MEMBER(type, name) type name = type{}
+#define RPC_MESSAGE_MEMBER(type, name) type name = {}
namespace cryptonote
diff --git a/src/rpc/message.h b/src/rpc/message.h
index 16b8e92fc..56087b998 100644
--- a/src/rpc/message.h
+++ b/src/rpc/message.h
@@ -65,7 +65,7 @@ namespace rpc
static const char* STATUS_BAD_REQUEST;
static const char* STATUS_BAD_JSON;
- Message() : status(STATUS_OK) { }
+ Message() : status(STATUS_OK), rpc_version(0) { }
virtual ~Message() { }
diff --git a/src/simplewallet/simplewallet.cpp b/src/simplewallet/simplewallet.cpp
index 18b596662..58d4cdced 100644
--- a/src/simplewallet/simplewallet.cpp
+++ b/src/simplewallet/simplewallet.cpp
@@ -1801,6 +1801,27 @@ bool simple_wallet::version(const std::vector<std::string> &args)
return true;
}
+bool simple_wallet::cold_sign_tx(const std::vector<tools::wallet2::pending_tx>& ptx_vector, tools::wallet2::signed_tx_set &exported_txs, std::vector<cryptonote::address_parse_info> &dsts_info, std::function<bool(const tools::wallet2::signed_tx_set &)> accept_func)
+{
+ std::vector<std::string> tx_aux;
+
+ message_writer(console_color_white, false) << tr("Please confirm the transaction on the device");
+
+ m_wallet->cold_sign_tx(ptx_vector, exported_txs, dsts_info, tx_aux);
+
+ if (accept_func && !accept_func(exported_txs))
+ {
+ MERROR("Transactions rejected by callback");
+ return false;
+ }
+
+ // aux info
+ m_wallet->cold_tx_aux_import(exported_txs.ptx, tx_aux);
+
+ // import key images
+ return m_wallet->import_key_images(exported_txs.key_images);
+}
+
bool simple_wallet::set_always_confirm_transfers(const std::vector<std::string> &args/* = std::vector<std::string>()*/)
{
const auto pwd_container = get_and_verify_password();
@@ -2253,6 +2274,33 @@ bool simple_wallet::set_ignore_fractional_outputs(const std::vector<std::string>
return true;
}
+bool simple_wallet::set_device_name(const std::vector<std::string> &args/* = std::vector<std::string>()*/)
+{
+ const auto pwd_container = get_and_verify_password();
+ if (pwd_container)
+ {
+ if (args.size() == 0){
+ fail_msg_writer() << tr("Device name not specified");
+ return true;
+ }
+
+ m_wallet->device_name(args[0]);
+ bool r = false;
+ try {
+ r = m_wallet->reconnect_device();
+ if (!r){
+ fail_msg_writer() << tr("Device reconnect failed");
+ }
+
+ } catch(const std::exception & e){
+ MWARNING("Device reconnect failed: " << e.what());
+ fail_msg_writer() << tr("Device reconnect failed: ") << e.what();
+ }
+
+ }
+ return true;
+}
+
bool simple_wallet::help(const std::vector<std::string> &args/* = std::vector<std::string>()*/)
{
if(args.empty())
@@ -2484,7 +2532,15 @@ simple_wallet::simple_wallet()
m_cmd_binder.set_handler("show_transfers",
boost::bind(&simple_wallet::show_transfers, this, _1),
tr("show_transfers [in|out|pending|failed|pool|coinbase] [index=<N1>[,<N2>,...]] [<min_height> [<max_height>]]"),
- tr("Show the incoming/outgoing transfers within an optional height range."));
+ // Seemingly broken formatting to compensate for the backslash before the quotes.
+ tr("Show the incoming/outgoing transfers within an optional height range.\n\n"
+ "Output format:\n"
+ "In or Coinbase: Block Number, \"block\"|\"in\", Time, Amount, Transaction Hash, Payment ID, Subaddress Index, \"-\", Note\n"
+ "Out: Block Number, \"out\", Time, Amount*, Transaction Hash, Payment ID, Fee, Destinations, Input addresses**, \"-\", Note\n"
+ "Pool: \"pool\", \"in\", Time, Amount, Transaction Hash, Payment Id, Subaddress Index, \"-\", Note, Double Spend Note\n"
+ "Pending or Failed: \"failed\"|\"pending\", \"out\", Time, Amount*, Transaction Hash, Payment ID, Fee, Input addresses**, \"-\", Note\n\n"
+ "* Excluding change and fee.\n"
+ "** Set of address indices used as inputs in this transfer."));
m_cmd_binder.set_handler("unspent_outputs",
boost::bind(&simple_wallet::unspent_outputs, this, _1),
tr("unspent_outputs [index=<N1>[,<N2>,...]] [<min_amount> [<max_amount>]]"),
@@ -2529,6 +2585,10 @@ simple_wallet::simple_wallet()
boost::bind(&simple_wallet::import_key_images, this, _1),
tr("import_key_images <file>"),
tr("Import a signed key images list and verify their spent status."));
+ m_cmd_binder.set_handler("hw_key_images_sync",
+ boost::bind(&simple_wallet::hw_key_images_sync, this, _1),
+ tr("hw_key_images_sync"),
+ tr("Synchronizes key images with the hw wallet."));
m_cmd_binder.set_handler("hw_reconnect",
boost::bind(&simple_wallet::hw_reconnect, this, _1),
tr("hw_reconnect"),
@@ -2720,6 +2780,7 @@ bool simple_wallet::set_variable(const std::vector<std::string> &args)
CHECK_SIMPLE_VARIABLE("subaddress-lookahead", set_subaddress_lookahead, tr("<major>:<minor>"));
CHECK_SIMPLE_VARIABLE("segregation-height", set_segregation_height, tr("unsigned integer"));
CHECK_SIMPLE_VARIABLE("ignore-fractional-outputs", set_ignore_fractional_outputs, tr("0 or 1"));
+ CHECK_SIMPLE_VARIABLE("device-name", set_device_name, tr("<device_name[:device_spec]>"));
}
fail_msg_writer() << tr("set: unrecognized argument(s)");
return true;
@@ -4715,8 +4776,6 @@ bool simple_wallet::transfer_main(int transfer_type, const std::vector<std::stri
if (!try_connect_to_daemon())
return true;
- SCOPED_WALLET_UNLOCK();
-
std::vector<std::string> local_args = args_;
std::set<uint32_t> subaddr_indices;
@@ -4828,12 +4887,14 @@ bool simple_wallet::transfer_main(int transfer_type, const std::vector<std::stri
local_args.pop_back();
}
+ vector<cryptonote::address_parse_info> dsts_info;
vector<cryptonote::tx_destination_entry> dsts;
size_t num_subaddresses = 0;
for (size_t i = 0; i < local_args.size(); )
{
+ dsts_info.emplace_back();
+ cryptonote::address_parse_info & info = dsts_info.back();
cryptonote::tx_destination_entry de;
- cryptonote::address_parse_info info;
bool r = true;
// check for a URI
@@ -4936,6 +4997,8 @@ bool simple_wallet::transfer_main(int transfer_type, const std::vector<std::stri
}
}
+ SCOPED_WALLET_UNLOCK();
+
try
{
// figure out what tx will be necessary
@@ -5115,6 +5178,28 @@ bool simple_wallet::transfer_main(int transfer_type, const std::vector<std::stri
success_msg_writer(true) << tr("Unsigned transaction(s) successfully written to file: ") << "multisig_monero_tx";
}
}
+ else if (m_wallet->get_account().get_device().has_tx_cold_sign())
+ {
+ try
+ {
+ tools::wallet2::signed_tx_set signed_tx;
+ if (!cold_sign_tx(ptx_vector, signed_tx, dsts_info, [&](const tools::wallet2::signed_tx_set &tx){ return accept_loaded_tx(tx); })){
+ fail_msg_writer() << tr("Failed to cold sign transaction with HW wallet");
+ return true;
+ }
+
+ commit_or_save(signed_tx.ptx, m_do_not_relay);
+ }
+ catch (const std::exception& e)
+ {
+ handle_transfer_exception(std::current_exception(), m_wallet->is_trusted_daemon());
+ }
+ catch (...)
+ {
+ LOG_ERROR("Unknown error");
+ fail_msg_writer() << tr("unknown error");
+ }
+ }
else if (m_wallet->watch_only())
{
bool r = m_wallet->save_tx(ptx_vector, "unsigned_monero_tx");
@@ -5537,6 +5622,31 @@ bool simple_wallet::sweep_main(uint64_t below, bool locked, const std::vector<st
success_msg_writer(true) << tr("Unsigned transaction(s) successfully written to file: ") << "multisig_monero_tx";
}
}
+ else if (m_wallet->get_account().get_device().has_tx_cold_sign())
+ {
+ try
+ {
+ tools::wallet2::signed_tx_set signed_tx;
+ std::vector<cryptonote::address_parse_info> dsts_info;
+ dsts_info.push_back(info);
+
+ if (!cold_sign_tx(ptx_vector, signed_tx, dsts_info, [&](const tools::wallet2::signed_tx_set &tx){ return accept_loaded_tx(tx); })){
+ fail_msg_writer() << tr("Failed to cold sign transaction with HW wallet");
+ return true;
+ }
+
+ commit_or_save(signed_tx.ptx, m_do_not_relay);
+ }
+ catch (const std::exception& e)
+ {
+ handle_transfer_exception(std::current_exception(), m_wallet->is_trusted_daemon());
+ }
+ catch (...)
+ {
+ LOG_ERROR("Unknown error");
+ fail_msg_writer() << tr("unknown error");
+ }
+ }
else if (m_wallet->watch_only())
{
bool r = m_wallet->save_tx(ptx_vector, "unsigned_monero_tx");
@@ -7050,12 +7160,6 @@ bool simple_wallet::run()
void simple_wallet::stop()
{
m_cmd_binder.stop_handling();
-
- m_idle_run.store(false, std::memory_order_relaxed);
- m_wallet->stop();
- // make the background refresh thread quit
- boost::unique_lock<boost::mutex> lock(m_idle_mutex);
- m_idle_cond.notify_one();
}
//----------------------------------------------------------------------------------------------------
bool simple_wallet::account(const std::vector<std::string> &args/* = std::vector<std::string>()*/)
@@ -7792,6 +7896,48 @@ bool simple_wallet::import_key_images(const std::vector<std::string> &args)
return true;
}
//----------------------------------------------------------------------------------------------------
+bool simple_wallet::hw_key_images_sync(const std::vector<std::string> &args)
+{
+ if (!m_wallet->key_on_device())
+ {
+ fail_msg_writer() << tr("command only supported by HW wallet");
+ return true;
+ }
+ if (!m_wallet->get_account().get_device().has_ki_cold_sync())
+ {
+ fail_msg_writer() << tr("hw wallet does not support cold KI sync");
+ return true;
+ }
+ if (!m_wallet->is_trusted_daemon())
+ {
+ fail_msg_writer() << tr("this command requires a trusted daemon. Enable with --trusted-daemon");
+ return true;
+ }
+
+ LOCK_IDLE_SCOPE();
+ try
+ {
+ message_writer(console_color_white, false) << tr("Please confirm the key image sync on the device");
+
+ uint64_t spent = 0, unspent = 0;
+ uint64_t height = m_wallet->cold_key_image_sync(spent, unspent);
+ if (height > 0)
+ {
+ success_msg_writer() << tr("Signed key images imported to height ") << height << ", "
+ << print_money(spent) << tr(" spent, ") << print_money(unspent) << tr(" unspent");
+ } else {
+ fail_msg_writer() << tr("Failed to import key images");
+ }
+ }
+ catch (const std::exception &e)
+ {
+ fail_msg_writer() << tr("Failed to import key images: ") << e.what();
+ return true;
+ }
+
+ return true;
+}
+//----------------------------------------------------------------------------------------------------
bool simple_wallet::hw_reconnect(const std::vector<std::string> &args)
{
if (!m_wallet->key_on_device())
diff --git a/src/simplewallet/simplewallet.h b/src/simplewallet/simplewallet.h
index 39b715b73..7d813ceb0 100644
--- a/src/simplewallet/simplewallet.h
+++ b/src/simplewallet/simplewallet.h
@@ -139,6 +139,7 @@ namespace cryptonote
bool set_subaddress_lookahead(const std::vector<std::string> &args = std::vector<std::string>());
bool set_segregation_height(const std::vector<std::string> &args = std::vector<std::string>());
bool set_ignore_fractional_outputs(const std::vector<std::string> &args = std::vector<std::string>());
+ bool set_device_name(const std::vector<std::string> &args = std::vector<std::string>());
bool help(const std::vector<std::string> &args = std::vector<std::string>());
bool start_mining(const std::vector<std::string> &args);
bool stop_mining(const std::vector<std::string> &args);
@@ -200,6 +201,7 @@ namespace cryptonote
bool verify(const std::vector<std::string> &args);
bool export_key_images(const std::vector<std::string> &args);
bool import_key_images(const std::vector<std::string> &args);
+ bool hw_key_images_sync(const std::vector<std::string> &args);
bool hw_reconnect(const std::vector<std::string> &args);
bool export_outputs(const std::vector<std::string> &args);
bool import_outputs(const std::vector<std::string> &args);
@@ -224,6 +226,7 @@ namespace cryptonote
bool unblackball(const std::vector<std::string>& args);
bool blackballed(const std::vector<std::string>& args);
bool version(const std::vector<std::string>& args);
+ bool cold_sign_tx(const std::vector<tools::wallet2::pending_tx>& ptx_vector, tools::wallet2::signed_tx_set &exported_txs, std::vector<cryptonote::address_parse_info> &dsts_info, std::function<bool(const tools::wallet2::signed_tx_set &)> accept_func);
uint64_t get_daemon_blockchain_height(std::string& err);
bool try_connect_to_daemon(bool silent = false, uint32_t* version = nullptr);
diff --git a/src/wallet/CMakeLists.txt b/src/wallet/CMakeLists.txt
index be10b9f62..4932dd4b6 100644
--- a/src/wallet/CMakeLists.txt
+++ b/src/wallet/CMakeLists.txt
@@ -57,6 +57,7 @@ target_link_libraries(wallet
common
cryptonote_core
mnemonics
+ device_trezor
${LMDB_LIBRARY}
${Boost_CHRONO_LIBRARY}
${Boost_SERIALIZATION_LIBRARY}
diff --git a/src/wallet/api/wallet.cpp b/src/wallet/api/wallet.cpp
index 1b4370c36..ddf2d74ff 100644
--- a/src/wallet/api/wallet.cpp
+++ b/src/wallet/api/wallet.cpp
@@ -381,6 +381,7 @@ WalletImpl::WalletImpl(NetworkType nettype, uint64_t kdf_rounds)
, m_synchronized(false)
, m_rebuildWalletCache(false)
, m_is_connected(false)
+ , m_refreshShouldRescan(false)
{
m_wallet.reset(new tools::wallet2(static_cast<cryptonote::network_type>(nettype), kdf_rounds, true));
m_history.reset(new TransactionHistoryImpl(this));
@@ -1011,6 +1012,20 @@ void WalletImpl::refreshAsync()
m_refreshCV.notify_one();
}
+bool WalletImpl::rescanBlockchain()
+{
+ clearStatus();
+ m_refreshShouldRescan = true;
+ doRefresh();
+ return status() == Status_Ok;
+}
+
+void WalletImpl::rescanBlockchainAsync()
+{
+ m_refreshShouldRescan = true;
+ refreshAsync();
+}
+
void WalletImpl::setAutoRefreshInterval(int millis)
{
if (millis > MAX_REFRESH_INTERVAL_MILLIS) {
@@ -1984,6 +1999,7 @@ void WalletImpl::refreshThreadFunc()
LOG_PRINT_L3(__FUNCTION__ << ": refresh lock acquired...");
LOG_PRINT_L3(__FUNCTION__ << ": m_refreshEnabled: " << m_refreshEnabled);
LOG_PRINT_L3(__FUNCTION__ << ": m_status: " << status());
+ LOG_PRINT_L3(__FUNCTION__ << ": m_refreshShouldRescan: " << m_refreshShouldRescan);
if (m_refreshEnabled) {
LOG_PRINT_L3(__FUNCTION__ << ": refreshing...");
doRefresh();
@@ -1994,12 +2010,16 @@ void WalletImpl::refreshThreadFunc()
void WalletImpl::doRefresh()
{
+ bool rescan = m_refreshShouldRescan.exchange(false);
// synchronizing async and sync refresh calls
boost::lock_guard<boost::mutex> guarg(m_refreshMutex2);
- try {
+ do try {
+ LOG_PRINT_L3(__FUNCTION__ << ": doRefresh, rescan = "<<rescan);
// Syncing daemon and refreshing wallet simultaneously is very resource intensive.
// Disable refresh if wallet is disconnected or daemon isn't synced.
if (m_wallet->light_wallet() || daemonSynced()) {
+ if(rescan)
+ m_wallet->rescan_blockchain(false);
m_wallet->refresh(trustedDaemon());
if (!m_synchronized) {
m_synchronized = true;
@@ -2016,7 +2036,9 @@ void WalletImpl::doRefresh()
}
} catch (const std::exception &e) {
setStatusError(e.what());
- }
+ break;
+ }while(!rescan && (rescan=m_refreshShouldRescan.exchange(false))); // repeat if not rescanned and rescan was requested
+
if (m_wallet2Callback->getListener()) {
m_wallet2Callback->getListener()->refreshed();
}
diff --git a/src/wallet/api/wallet.h b/src/wallet/api/wallet.h
index 6d343888b..b4637b8e6 100644
--- a/src/wallet/api/wallet.h
+++ b/src/wallet/api/wallet.h
@@ -114,6 +114,8 @@ public:
bool synchronized() const override;
bool refresh() override;
void refreshAsync() override;
+ bool rescanBlockchain() override;
+ void rescanBlockchainAsync() override;
void setAutoRefreshInterval(int millis) override;
int autoRefreshInterval() const override;
void setRefreshFromBlockHeight(uint64_t refresh_from_block_height) override;
@@ -232,6 +234,7 @@ private:
std::atomic<bool> m_refreshEnabled;
std::atomic<bool> m_refreshThreadDone;
std::atomic<int> m_refreshIntervalMillis;
+ std::atomic<bool> m_refreshShouldRescan;
// synchronizing refresh loop;
boost::mutex m_refreshMutex;
diff --git a/src/wallet/api/wallet2_api.h b/src/wallet/api/wallet2_api.h
index ec1a84877..82627de29 100644
--- a/src/wallet/api/wallet2_api.h
+++ b/src/wallet/api/wallet2_api.h
@@ -644,6 +644,17 @@ struct Wallet
virtual void refreshAsync() = 0;
/**
+ * @brief rescanBlockchain - rescans the wallet, updating transactions from daemon
+ * @return - true if refreshed successfully;
+ */
+ virtual bool rescanBlockchain() = 0;
+
+ /**
+ * @brief rescanBlockchainAsync - rescans wallet asynchronously, starting from genesys
+ */
+ virtual void rescanBlockchainAsync() = 0;
+
+ /**
* @brief setAutoRefreshInterval - setup interval for automatic refresh.
* @param seconds - interval in millis. if zero or less than zero - automatic refresh disabled;
*/
diff --git a/src/wallet/wallet2.cpp b/src/wallet/wallet2.cpp
index 21793b58d..794d1d421 100644
--- a/src/wallet/wallet2.cpp
+++ b/src/wallet/wallet2.cpp
@@ -71,6 +71,8 @@ using namespace epee;
#include "common/notify.h"
#include "ringct/rctSigs.h"
#include "ringdb.h"
+#include "device/device_cold.hpp"
+#include "device_trezor/device_trezor.hpp"
extern "C"
{
@@ -123,6 +125,8 @@ using namespace cryptonote;
#define FIRST_REFRESH_GRANULARITY 1024
+#define GAMMA_PICK_HALF_WINDOW 5
+
static const std::string MULTISIG_SIGNATURE_MAGIC = "SigMultisigPkV1";
static const std::string MULTISIG_EXTRA_INFO_MAGIC = "MultisigxV1";
@@ -766,6 +770,11 @@ uint32_t get_subaddress_clamped_sum(uint32_t idx, uint32_t extra)
return idx + extra;
}
+static void setup_shim(hw::wallet_shim * shim, tools::wallet2 * wallet)
+{
+ shim->get_tx_pub_key_from_received_outs = boost::bind(&tools::wallet2::get_tx_pub_key_from_received_outs, wallet, _1);
+}
+
//-----------------------------------------------------------------
} //namespace
@@ -811,6 +820,7 @@ wallet_keys_unlocker::~wallet_keys_unlocker()
wallet2::wallet2(network_type nettype, uint64_t kdf_rounds, bool unattended):
m_multisig_rescan_info(NULL),
m_multisig_rescan_k(NULL),
+ m_upper_transaction_weight_limit(0),
m_run(true),
m_callback(0),
m_trusted_daemon(false),
@@ -843,6 +853,9 @@ wallet2::wallet2(network_type nettype, uint64_t kdf_rounds, bool unattended):
m_is_initialized(false),
m_kdf_rounds(kdf_rounds),
is_old_file_format(false),
+ m_watch_only(false),
+ m_multisig(false),
+ m_multisig_threshold(0),
m_node_rpc_proxy(m_http_client, m_daemon_rpc_mutex),
m_subaddress_lookahead_major(SUBADDRESS_LOOKAHEAD_MAJOR),
m_subaddress_lookahead_minor(SUBADDRESS_LOOKAHEAD_MINOR),
@@ -1054,8 +1067,9 @@ bool wallet2::get_multisig_seed(epee::wipeable_string& seed, const epee::wipeabl
bool wallet2::reconnect_device()
{
bool r = true;
- hw::device &hwdev = hw::get_device(m_device_name);
+ hw::device &hwdev = lookup_device(m_device_name);
hwdev.set_name(m_device_name);
+ hwdev.set_network_type(m_nettype);
r = hwdev.init();
if (!r){
LOG_PRINT_L2("Could not init device");
@@ -2938,6 +2952,7 @@ bool wallet2::deinit()
{
m_is_initialized=false;
unlock_keys_file();
+ m_account.deinit();
return true;
}
//----------------------------------------------------------------------------------------------------
@@ -3407,13 +3422,20 @@ bool wallet2::load_keys(const std::string& keys_file_name, const epee::wipeable_
r = epee::serialization::load_t_from_binary(m_account, account_data);
THROW_WALLET_EXCEPTION_IF(!r, error::invalid_password);
- if (m_key_device_type == hw::device::device_type::LEDGER) {
+ if (m_key_device_type == hw::device::device_type::LEDGER || m_key_device_type == hw::device::device_type::TREZOR) {
LOG_PRINT_L0("Account on device. Initing device...");
- hw::device &hwdev = hw::get_device(m_device_name);
- hwdev.set_name(m_device_name);
- hwdev.init();
- hwdev.connect();
+ hw::device &hwdev = lookup_device(m_device_name);
+ THROW_WALLET_EXCEPTION_IF(!hwdev.set_name(m_device_name), error::wallet_internal_error, "Could not set device name " + m_device_name);
+ hwdev.set_network_type(m_nettype);
+ THROW_WALLET_EXCEPTION_IF(!hwdev.init(), error::wallet_internal_error, "Could not initialize the device " + m_device_name);
+ THROW_WALLET_EXCEPTION_IF(!hwdev.connect(), error::wallet_internal_error, "Could not connect to the device " + m_device_name);
m_account.set_device(hwdev);
+
+ account_public_address device_account_public_address;
+ THROW_WALLET_EXCEPTION_IF(!hwdev.get_public_address(device_account_public_address), error::wallet_internal_error, "Cannot get a device address");
+ THROW_WALLET_EXCEPTION_IF(device_account_public_address != m_account.get_keys().m_account_address, error::wallet_internal_error, "Device wallet does not match wallet address. "
+ "Device address: " + cryptonote::get_account_address_as_str(m_nettype, false, device_account_public_address) +
+ ", wallet address: " + m_account.get_public_address_str(m_nettype));
LOG_PRINT_L0("Device inited...");
} else if (key_on_device()) {
THROW_WALLET_EXCEPTION(error::wallet_internal_error, "hardware device not supported");
@@ -3444,7 +3466,7 @@ bool wallet2::load_keys(const std::string& keys_file_name, const epee::wipeable_
const cryptonote::account_keys& keys = m_account.get_keys();
hw::device &hwdev = m_account.get_device();
r = r && hwdev.verify_keys(keys.m_view_secret_key, keys.m_account_address.m_view_public_key);
- if(!m_watch_only && !m_multisig)
+ if(!m_watch_only && !m_multisig && hwdev.device_protocol() != hw::device::PROTOCOL_COLD)
r = r && hwdev.verify_keys(keys.m_spend_secret_key, keys.m_account_address.m_spend_public_key);
THROW_WALLET_EXCEPTION_IF(!r, error::invalid_password);
@@ -3468,7 +3490,7 @@ bool wallet2::verify_password(const epee::wipeable_string& password)
{
// this temporary unlocking is necessary for Windows (otherwise the file couldn't be loaded).
unlock_keys_file();
- bool r = verify_password(m_keys_file, password, m_watch_only || m_multisig, m_account.get_device(), m_kdf_rounds);
+ bool r = verify_password(m_keys_file, password, m_account.get_device().device_protocol() == hw::device::PROTOCOL_COLD || m_watch_only || m_multisig, m_account.get_device(), m_kdf_rounds);
lock_keys_file();
return r;
}
@@ -3908,8 +3930,9 @@ void wallet2::restore(const std::string& wallet_, const epee::wipeable_string& p
THROW_WALLET_EXCEPTION_IF(boost::filesystem::exists(m_keys_file, ignored_ec), error::file_exists, m_keys_file);
}
- auto &hwdev = hw::get_device(device_name);
+ auto &hwdev = lookup_device(device_name);
hwdev.set_name(device_name);
+ hwdev.set_network_type(m_nettype);
m_account.create_from_device(hwdev);
m_key_device_type = m_account.get_device().get_type();
@@ -5610,6 +5633,10 @@ bool wallet2::sign_tx(unsigned_tx_set &exported_txs, std::vector<wallet2::pendin
ptx.construction_data = sd;
txs.push_back(ptx);
+
+ // add tx keys only to ptx
+ txs.back().tx_key = tx_key;
+ txs.back().additional_tx_keys = additional_tx_keys;
}
// add key images
@@ -5771,22 +5798,8 @@ bool wallet2::parse_tx_from_str(const std::string &signed_tx_st, std::vector<too
}
// import key images
- if (signed_txs.key_images.size() > m_transfers.size())
- {
- LOG_PRINT_L1("More key images returned that we know outputs for");
- return false;
- }
- for (size_t i = 0; i < signed_txs.key_images.size(); ++i)
- {
- transfer_details &td = m_transfers[i];
- if (td.m_key_image_known && !td.m_key_image_partial && td.m_key_image != signed_txs.key_images[i])
- LOG_PRINT_L0("WARNING: imported key image differs from previously known key image at index " << i << ": trusting imported one");
- td.m_key_image = signed_txs.key_images[i];
- m_key_images[m_transfers[i].m_key_image] = i;
- td.m_key_image_known = true;
- td.m_key_image_partial = false;
- m_pub_keys[m_transfers[i].get_public_key()] = i;
- }
+ bool r = import_key_images(signed_txs.key_images);
+ if (!r) return false;
ptx = signed_txs.ptx;
@@ -6336,6 +6349,19 @@ crypto::chacha_key wallet2::get_ringdb_key()
return *m_ringdb_key;
}
+void wallet2::register_devices(){
+ hw::trezor::register_all();
+}
+
+hw::device& wallet2::lookup_device(const std::string & device_descriptor){
+ if (!m_devices_registered){
+ m_devices_registered = true;
+ register_devices();
+ }
+
+ return hw::get_device(device_descriptor);
+}
+
bool wallet2::add_rings(const crypto::chacha_key &key, const cryptonote::transaction_prefix &tx)
{
if (!m_ringdb)
@@ -6795,10 +6821,29 @@ void wallet2::get_outs(std::vector<std::vector<tools::wallet2::get_outs_entry>>
error::get_output_distribution, "Decreasing offsets in rct distribution: " +
std::to_string(block_offset) + ": " + std::to_string(rct_offsets[block_offset]) + ", " +
std::to_string(block_offset + 1) + ": " + std::to_string(rct_offsets[block_offset + 1]));
- uint64_t n_rct = rct_offsets[block_offset + 1] - rct_offsets[block_offset];
+ uint64_t first_block_offset = block_offset, last_block_offset = block_offset;
+ for (size_t half_window = 0; half_window < GAMMA_PICK_HALF_WINDOW; ++half_window)
+ {
+ // end when we have a non empty block
+ uint64_t cum0 = first_block_offset > 0 ? rct_offsets[first_block_offset] - rct_offsets[first_block_offset - 1] : rct_offsets[0];
+ if (cum0 > 1)
+ break;
+ uint64_t cum1 = last_block_offset > 0 ? rct_offsets[last_block_offset] - rct_offsets[last_block_offset - 1] : rct_offsets[0];
+ if (cum1 > 1)
+ break;
+ if (first_block_offset == 0 && last_block_offset >= rct_offsets.size() - 2)
+ break;
+ // expand up to bounds
+ if (first_block_offset > 0)
+ --first_block_offset;
+ if (last_block_offset < rct_offsets.size() - 1)
+ ++last_block_offset;
+ }
+ const uint64_t n_rct = rct_offsets[last_block_offset] - (first_block_offset == 0 ? 0 : rct_offsets[first_block_offset - 1]);
if (n_rct == 0)
return rct_offsets[block_offset] ? rct_offsets[block_offset] - 1 : 0;
- return rct_offsets[block_offset] + crypto::rand<uint64_t>() % n_rct;
+ MDEBUG("Picking 1/" << n_rct << " in " << (last_block_offset - first_block_offset + 1) << " blocks centered around " << block_offset);
+ return rct_offsets[first_block_offset] + crypto::rand<uint64_t>() % n_rct;
};
size_t num_selected_transfers = 0;
@@ -8404,12 +8449,8 @@ std::vector<wallet2::pending_tx> wallet2::create_transactions_2(std::vector<cryp
}
}
- // shuffle & sort output indices
+ // sort output indices
{
- std::random_device rd;
- std::mt19937 g(rd());
- std::shuffle(unused_transfers_indices_per_subaddr.begin(), unused_transfers_indices_per_subaddr.end(), g);
- std::shuffle(unused_dust_indices_per_subaddr.begin(), unused_dust_indices_per_subaddr.end(), g);
auto sort_predicate = [&unlocked_balance_per_subaddr] (const std::pair<uint32_t, std::vector<size_t>>& x, const std::pair<uint32_t, std::vector<size_t>>& y)
{
return unlocked_balance_per_subaddr[x.first] > unlocked_balance_per_subaddr[y.first];
@@ -8601,7 +8642,7 @@ std::vector<wallet2::pending_tx> wallet2::create_transactions_2(std::vector<cryp
cryptonote::transaction test_tx;
pending_tx test_ptx;
- needed_fee = estimate_fee(use_per_byte_fee, use_rct ,tx.selected_transfers.size(), fake_outs_count, tx.dsts.size()+1, extra.size(), bulletproof, base_fee, fee_multiplier, fee_multiplier);
+ needed_fee = estimate_fee(use_per_byte_fee, use_rct ,tx.selected_transfers.size(), fake_outs_count, tx.dsts.size()+1, extra.size(), bulletproof, base_fee, fee_multiplier, fee_quantization_mask);
uint64_t inputs = 0, outputs = needed_fee;
for (size_t idx: tx.selected_transfers) inputs += m_transfers[idx].amount();
@@ -9055,6 +9096,62 @@ std::vector<wallet2::pending_tx> wallet2::create_transactions_from(const crypton
return ptx_vector;
}
//----------------------------------------------------------------------------------------------------
+void wallet2::cold_tx_aux_import(const std::vector<pending_tx> & ptx, const std::vector<std::string> & tx_device_aux)
+{
+ CHECK_AND_ASSERT_THROW_MES(ptx.size() == tx_device_aux.size(), "TX aux has invalid size");
+ for (size_t i = 0; i < ptx.size(); ++i){
+ crypto::hash txid;
+ txid = get_transaction_hash(ptx[i].tx);
+ set_tx_device_aux(txid, tx_device_aux[i]);
+ }
+}
+//----------------------------------------------------------------------------------------------------
+void wallet2::cold_sign_tx(const std::vector<pending_tx>& ptx_vector, signed_tx_set &exported_txs, std::vector<cryptonote::address_parse_info> &dsts_info, std::vector<std::string> & tx_device_aux)
+{
+ auto & hwdev = get_account().get_device();
+ if (!hwdev.has_tx_cold_sign()){
+ throw std::invalid_argument("Device does not support cold sign protocol");
+ }
+
+ unsigned_tx_set txs;
+ for (auto &tx: ptx_vector)
+ {
+ txs.txes.push_back(get_construction_data_with_decrypted_short_payment_id(tx, m_account.get_device()));
+ }
+ txs.transfers = m_transfers;
+
+ auto dev_cold = dynamic_cast<::hw::device_cold*>(&hwdev);
+ CHECK_AND_ASSERT_THROW_MES(dev_cold, "Device does not implement cold signing interface");
+
+ hw::tx_aux_data aux_data;
+ hw::wallet_shim wallet_shim;
+ setup_shim(&wallet_shim, this);
+ aux_data.tx_recipients = dsts_info;
+ dev_cold->tx_sign(&wallet_shim, txs, exported_txs, aux_data);
+ tx_device_aux = aux_data.tx_device_aux;
+
+ MDEBUG("Signed tx data from hw: " << exported_txs.ptx.size() << " transactions");
+ for (auto &c_ptx: exported_txs.ptx) LOG_PRINT_L0(cryptonote::obj_to_json_str(c_ptx.tx));
+}
+//----------------------------------------------------------------------------------------------------
+uint64_t wallet2::cold_key_image_sync(uint64_t &spent, uint64_t &unspent) {
+ auto & hwdev = get_account().get_device();
+ if (!hwdev.has_ki_cold_sync()){
+ throw std::invalid_argument("Device does not support cold ki sync protocol");
+ }
+
+ auto dev_cold = dynamic_cast<::hw::device_cold*>(&hwdev);
+ CHECK_AND_ASSERT_THROW_MES(dev_cold, "Device does not implement cold signing interface");
+
+ std::vector<std::pair<crypto::key_image, crypto::signature>> ski;
+ hw::wallet_shim wallet_shim;
+ setup_shim(&wallet_shim, this);
+
+ dev_cold->ki_sync(&wallet_shim, m_transfers, ski);
+
+ return import_key_images(ski, spent, unspent);
+}
+//----------------------------------------------------------------------------------------------------
void wallet2::get_hard_fork_info(uint8_t version, uint64_t &earliest_height) const
{
boost::optional<std::string> result = m_node_rpc_proxy.get_earliest_height(version, earliest_height);
@@ -10211,6 +10308,19 @@ std::string wallet2::get_tx_note(const crypto::hash &txid) const
return i->second;
}
+void wallet2::set_tx_device_aux(const crypto::hash &txid, const std::string &aux)
+{
+ m_tx_device[txid] = aux;
+}
+
+std::string wallet2::get_tx_device_aux(const crypto::hash &txid) const
+{
+ std::unordered_map<crypto::hash, std::string>::const_iterator i = m_tx_device.find(txid);
+ if (i == m_tx_device.end())
+ return std::string();
+ return i->second;
+}
+
void wallet2::set_attribute(const std::string &key, const std::string &value)
{
m_attributes[key] = value;
@@ -10558,7 +10668,7 @@ uint64_t wallet2::import_key_images(const std::vector<std::pair<crypto::key_imag
+ boost::lexical_cast<std::string>(signed_key_images.size()) + ", key image " + epee::string_tools::pod_to_hex(key_image));
THROW_WALLET_EXCEPTION_IF(!crypto::check_ring_signature((const crypto::hash&)key_image, key_image, pkeys, &signature),
- error::wallet_internal_error, "Signature check failed: input " + boost::lexical_cast<std::string>(n) + "/"
+ error::signature_check_failed, boost::lexical_cast<std::string>(n) + "/"
+ boost::lexical_cast<std::string>(signed_key_images.size()) + ", key image " + epee::string_tools::pod_to_hex(key_image)
+ ", signature " + epee::string_tools::pod_to_hex(signature) + ", pubkey " + epee::string_tools::pod_to_hex(*pkeys[0]));
@@ -10756,6 +10866,29 @@ uint64_t wallet2::import_key_images(const std::vector<std::pair<crypto::key_imag
return m_transfers[signed_key_images.size() - 1].m_block_height;
}
+
+bool wallet2::import_key_images(std::vector<crypto::key_image> key_images)
+{
+ if (key_images.size() > m_transfers.size())
+ {
+ LOG_PRINT_L1("More key images returned that we know outputs for");
+ return false;
+ }
+ for (size_t i = 0; i < key_images.size(); ++i)
+ {
+ transfer_details &td = m_transfers[i];
+ if (td.m_key_image_known && !td.m_key_image_partial && td.m_key_image != key_images[i])
+ LOG_PRINT_L0("WARNING: imported key image differs from previously known key image at index " << i << ": trusting imported one");
+ td.m_key_image = key_images[i];
+ m_key_images[m_transfers[i].m_key_image] = i;
+ td.m_key_image_known = true;
+ td.m_key_image_partial = false;
+ m_pub_keys[m_transfers[i].get_public_key()] = i;
+ }
+
+ return true;
+}
+
wallet2::payment_container wallet2::export_payments() const
{
payment_container payments;
diff --git a/src/wallet/wallet2.h b/src/wallet/wallet2.h
index 680196f01..7d78a3fee 100644
--- a/src/wallet/wallet2.h
+++ b/src/wallet/wallet2.h
@@ -54,6 +54,7 @@
#include "ringct/rctTypes.h"
#include "ringct/rctOps.h"
#include "checkpoints/checkpoints.h"
+#include "serialization/pair.h"
#include "wallet_errors.h"
#include "common/password.h"
@@ -764,6 +765,9 @@ namespace tools
std::vector<wallet2::pending_tx> create_transactions_all(uint64_t below, const cryptonote::account_public_address &address, bool is_subaddress, const size_t outputs, const size_t fake_outs_count, const uint64_t unlock_time, uint32_t priority, const std::vector<uint8_t>& extra, uint32_t subaddr_account, std::set<uint32_t> subaddr_indices);
std::vector<wallet2::pending_tx> create_transactions_single(const crypto::key_image &ki, const cryptonote::account_public_address &address, bool is_subaddress, const size_t outputs, const size_t fake_outs_count, const uint64_t unlock_time, uint32_t priority, const std::vector<uint8_t>& extra);
std::vector<wallet2::pending_tx> create_transactions_from(const cryptonote::account_public_address &address, bool is_subaddress, const size_t outputs, std::vector<size_t> unused_transfers_indices, std::vector<size_t> unused_dust_indices, const size_t fake_outs_count, const uint64_t unlock_time, uint32_t priority, const std::vector<uint8_t>& extra);
+ void cold_tx_aux_import(const std::vector<pending_tx>& ptx, const std::vector<std::string>& tx_device_aux);
+ void cold_sign_tx(const std::vector<pending_tx>& ptx_vector, signed_tx_set &exported_txs, std::vector<cryptonote::address_parse_info> &dsts_info, std::vector<std::string> & tx_device_aux);
+ uint64_t cold_key_image_sync(uint64_t &spent, uint64_t &unspent);
bool load_multisig_tx(cryptonote::blobdata blob, multisig_tx_set &exported_txs, std::function<bool(const multisig_tx_set&)> accept_func = NULL);
bool load_multisig_tx_from_file(const std::string &filename, multisig_tx_set &exported_txs, std::function<bool(const multisig_tx_set&)> accept_func = NULL);
bool sign_multisig_tx_from_file(const std::string &filename, std::vector<crypto::hash> &txids, std::function<bool(const multisig_tx_set&)> accept_func);
@@ -892,6 +896,9 @@ namespace tools
if(ver < 25)
return;
a & m_last_block_reward;
+ if(ver < 26)
+ return;
+ a & m_tx_device;
}
/*!
@@ -1020,6 +1027,9 @@ namespace tools
void set_tx_note(const crypto::hash &txid, const std::string &note);
std::string get_tx_note(const crypto::hash &txid) const;
+ void set_tx_device_aux(const crypto::hash &txid, const std::string &aux);
+ std::string get_tx_device_aux(const crypto::hash &txid) const;
+
void set_description(const std::string &description);
std::string get_description() const;
@@ -1074,6 +1084,8 @@ namespace tools
std::vector<std::pair<crypto::key_image, crypto::signature>> export_key_images() const;
uint64_t import_key_images(const std::vector<std::pair<crypto::key_image, crypto::signature>> &signed_key_images, uint64_t &spent, uint64_t &unspent, bool check_spent = true);
uint64_t import_key_images(const std::string &filename, uint64_t &spent, uint64_t &unspent);
+ bool import_key_images(std::vector<crypto::key_image> key_images);
+ crypto::public_key get_tx_pub_key_from_received_outs(const tools::wallet2::transfer_details &td) const;
void update_pool_state(bool refreshed = false);
void remove_obsolete_pool_txs(const std::vector<crypto::hash> &tx_hashes);
@@ -1236,7 +1248,6 @@ namespace tools
void set_unspent(size_t idx);
void get_outs(std::vector<std::vector<get_outs_entry>> &outs, const std::vector<size_t> &selected_transfers, size_t fake_outputs_count);
bool tx_add_fake_output(std::vector<std::vector<tools::wallet2::get_outs_entry>> &outs, uint64_t global_index, const crypto::public_key& tx_public_key, const rct::key& mask, uint64_t real_index, bool unlocked) const;
- crypto::public_key get_tx_pub_key_from_received_outs(const tools::wallet2::transfer_details &td) const;
bool should_pick_a_second_output(bool use_rct, size_t n_transfers, const std::vector<size_t> &unused_transfers_indices, const std::vector<size_t> &unused_dust_indices) const;
std::vector<size_t> get_only_rct(const std::vector<size_t> &unused_dust_indices, const std::vector<size_t> &unused_transfers_indices) const;
void scan_output(const cryptonote::transaction &tx, const crypto::public_key &tx_pub_key, size_t i, tx_scan_info_t &tx_scan_info, int &num_vouts_received, std::unordered_map<cryptonote::subaddress_index, uint64_t> &tx_money_got_in_outs, std::vector<size_t> &outs);
@@ -1253,6 +1264,9 @@ namespace tools
crypto::chacha_key get_ringdb_key();
void setup_keys(const epee::wipeable_string &password);
+ void register_devices();
+ hw::device& lookup_device(const std::string & device_descriptor);
+
bool get_rct_distribution(uint64_t &start_height, std::vector<uint64_t> &distribution);
uint64_t get_segregation_fork_height() const;
@@ -1347,6 +1361,9 @@ namespace tools
size_t m_subaddress_lookahead_major, m_subaddress_lookahead_minor;
std::string m_device_name;
+ // Aux transaction data from device
+ std::unordered_map<crypto::hash, std::string> m_tx_device;
+
// Light wallet
bool m_light_wallet; /* sends view key to daemon for scanning */
uint64_t m_light_wallet_scanned_block_height;
@@ -1373,11 +1390,12 @@ namespace tools
boost::optional<epee::wipeable_string> m_encrypt_keys_after_refresh;
bool m_unattended;
+ bool m_devices_registered;
std::shared_ptr<tools::Notify> m_tx_notify;
};
}
-BOOST_CLASS_VERSION(tools::wallet2, 25)
+BOOST_CLASS_VERSION(tools::wallet2, 26)
BOOST_CLASS_VERSION(tools::wallet2::transfer_details, 9)
BOOST_CLASS_VERSION(tools::wallet2::multisig_info, 1)
BOOST_CLASS_VERSION(tools::wallet2::multisig_info::LR, 0)
diff --git a/src/wallet/wallet_errors.h b/src/wallet/wallet_errors.h
index bc518d04a..b3141985d 100644
--- a/src/wallet/wallet_errors.h
+++ b/src/wallet/wallet_errors.h
@@ -72,6 +72,7 @@ namespace tools
// tx_parse_error
// get_tx_pool_error
// out_of_hashchain_bounds_error
+ // signature_check_failed
// transfer_error *
// get_outs_general_error
// not_enough_unlocked_money
@@ -418,6 +419,14 @@ namespace tools
std::string to_string() const { return refresh_error::to_string(); }
};
//----------------------------------------------------------------------------------------------------
+ struct signature_check_failed : public wallet_logic_error
+ {
+ explicit signature_check_failed(std::string&& loc, const std::string& message)
+ : wallet_logic_error(std::move(loc), "Signature check failed " + message)
+ {
+ }
+ };
+ //----------------------------------------------------------------------------------------------------
struct transfer_error : public wallet_logic_error
{
protected:
diff --git a/src/wallet/wallet_rpc_server.cpp b/src/wallet/wallet_rpc_server.cpp
index 1b63d65b6..5e6100dfd 100644
--- a/src/wallet/wallet_rpc_server.cpp
+++ b/src/wallet/wallet_rpc_server.cpp
@@ -981,6 +981,8 @@ namespace tools
for (auto &ptx: ptxs)
{
res.tx_hash_list.push_back(epee::string_tools::pod_to_hex(cryptonote::get_transaction_hash(ptx.tx)));
+ if (req.get_tx_keys)
+ res.tx_key_list.push_back(epee::string_tools::pod_to_hex(ptx.tx_key));
}
if (req.export_raw)
@@ -994,6 +996,171 @@ namespace tools
return true;
}
//------------------------------------------------------------------------------------------------------------------------------
+ bool wallet_rpc_server::on_describe_transfer(const wallet_rpc::COMMAND_RPC_DESCRIBE_TRANSFER::request& req, wallet_rpc::COMMAND_RPC_DESCRIBE_TRANSFER::response& res, epee::json_rpc::error& er)
+ {
+ if (!m_wallet) return not_open(er);
+ if (m_restricted)
+ {
+ er.code = WALLET_RPC_ERROR_CODE_DENIED;
+ er.message = "Command unavailable in restricted mode.";
+ return false;
+ }
+ if (m_wallet->key_on_device())
+ {
+ er.code = WALLET_RPC_ERROR_CODE_UNKNOWN_ERROR;
+ er.message = "command not supported by HW wallet";
+ return false;
+ }
+ if(m_wallet->watch_only())
+ {
+ er.code = WALLET_RPC_ERROR_CODE_WATCH_ONLY;
+ er.message = "command not supported by watch-only wallet";
+ return false;
+ }
+
+ tools::wallet2::unsigned_tx_set exported_txs;
+ try
+ {
+ cryptonote::blobdata blob;
+ if (!epee::string_tools::parse_hexstr_to_binbuff(req.unsigned_txset, blob))
+ {
+ er.code = WALLET_RPC_ERROR_CODE_BAD_HEX;
+ er.message = "Failed to parse hex.";
+ return false;
+ }
+ if(!m_wallet->parse_unsigned_tx_from_str(blob, exported_txs))
+ {
+ er.code = WALLET_RPC_ERROR_CODE_BAD_UNSIGNED_TX_DATA;
+ er.message = "cannot load unsigned_txset";
+ return false;
+ }
+ }
+ catch (const std::exception &e)
+ {
+ er.code = WALLET_RPC_ERROR_CODE_BAD_UNSIGNED_TX_DATA;
+ er.message = "failed to parse unsigned transfers: " + std::string(e.what());
+ return false;
+ }
+
+ std::vector<tools::wallet2::pending_tx> ptx;
+ try
+ {
+ // gather info to ask the user
+ std::unordered_map<cryptonote::account_public_address, std::pair<std::string, uint64_t>> dests;
+ int first_known_non_zero_change_index = -1;
+ for (size_t n = 0; n < exported_txs.txes.size(); ++n)
+ {
+ const tools::wallet2::tx_construction_data &cd = exported_txs.txes[n];
+ res.desc.push_back({0, 0, std::numeric_limits<uint32_t>::max(), 0, {}, "", 0, "", 0, 0, ""});
+ wallet_rpc::COMMAND_RPC_DESCRIBE_TRANSFER::transfer_description &desc = res.desc.back();
+
+ std::vector<cryptonote::tx_extra_field> tx_extra_fields;
+ bool has_encrypted_payment_id = false;
+ crypto::hash8 payment_id8 = crypto::null_hash8;
+ if (cryptonote::parse_tx_extra(cd.extra, tx_extra_fields))
+ {
+ cryptonote::tx_extra_nonce extra_nonce;
+ if (find_tx_extra_field_by_type(tx_extra_fields, extra_nonce))
+ {
+ crypto::hash payment_id;
+ if(cryptonote::get_encrypted_payment_id_from_tx_extra_nonce(extra_nonce.nonce, payment_id8))
+ {
+ desc.payment_id = epee::string_tools::pod_to_hex(payment_id8);
+ has_encrypted_payment_id = true;
+ }
+ else if (cryptonote::get_payment_id_from_tx_extra_nonce(extra_nonce.nonce, payment_id))
+ {
+ desc.payment_id = epee::string_tools::pod_to_hex(payment_id);
+ }
+ }
+ }
+
+ for (size_t s = 0; s < cd.sources.size(); ++s)
+ {
+ desc.amount_in += cd.sources[s].amount;
+ size_t ring_size = cd.sources[s].outputs.size();
+ if (ring_size < desc.ring_size)
+ desc.ring_size = ring_size;
+ }
+ for (size_t d = 0; d < cd.splitted_dsts.size(); ++d)
+ {
+ const cryptonote::tx_destination_entry &entry = cd.splitted_dsts[d];
+ std::string address = cryptonote::get_account_address_as_str(m_wallet->nettype(), entry.is_subaddress, entry.addr);
+ if (has_encrypted_payment_id && !entry.is_subaddress)
+ address = cryptonote::get_account_integrated_address_as_str(m_wallet->nettype(), entry.addr, payment_id8);
+ auto i = dests.find(entry.addr);
+ if (i == dests.end())
+ dests.insert(std::make_pair(entry.addr, std::make_pair(address, entry.amount)));
+ else
+ i->second.second += entry.amount;
+ desc.amount_out += entry.amount;
+ }
+ if (cd.change_dts.amount > 0)
+ {
+ auto it = dests.find(cd.change_dts.addr);
+ if (it == dests.end())
+ {
+ er.code = WALLET_RPC_ERROR_CODE_BAD_UNSIGNED_TX_DATA;
+ er.message = "Claimed change does not go to a paid address";
+ return false;
+ }
+ if (it->second.second < cd.change_dts.amount)
+ {
+ er.code = WALLET_RPC_ERROR_CODE_BAD_UNSIGNED_TX_DATA;
+ er.message = "Claimed change is larger than payment to the change address";
+ return false;
+ }
+ if (cd.change_dts.amount > 0)
+ {
+ if (first_known_non_zero_change_index == -1)
+ first_known_non_zero_change_index = n;
+ const tools::wallet2::tx_construction_data &cdn = exported_txs.txes[first_known_non_zero_change_index];
+ if (memcmp(&cd.change_dts.addr, &cdn.change_dts.addr, sizeof(cd.change_dts.addr)))
+ {
+ er.code = WALLET_RPC_ERROR_CODE_BAD_UNSIGNED_TX_DATA;
+ er.message = "Change goes to more than one address";
+ return false;
+ }
+ }
+ desc.change_amount += cd.change_dts.amount;
+ it->second.second -= cd.change_dts.amount;
+ if (it->second.second == 0)
+ dests.erase(cd.change_dts.addr);
+ }
+
+ size_t n_dummy_outputs = 0;
+ for (auto i = dests.begin(); i != dests.end(); )
+ {
+ if (i->second.second > 0)
+ {
+ desc.recipients.push_back({i->second.first, i->second.second});
+ }
+ else
+ ++desc.dummy_outputs;
+ ++i;
+ }
+
+ if (desc.change_amount > 0)
+ {
+ const tools::wallet2::tx_construction_data &cd0 = exported_txs.txes[0];
+ desc.change_address = get_account_address_as_str(m_wallet->nettype(), cd0.subaddr_account > 0, cd0.change_dts.addr);
+ }
+
+ desc.fee = desc.amount_in - desc.amount_out;
+ desc.unlock_time = cd.unlock_time;
+ desc.extra = epee::to_hex::string({cd.extra.data(), cd.extra.size()});
+ }
+ }
+ catch (const std::exception &e)
+ {
+ er.code = WALLET_RPC_ERROR_CODE_BAD_UNSIGNED_TX_DATA;
+ er.message = "failed to parse unsigned transfers";
+ return false;
+ }
+
+ return true;
+ }
+ //------------------------------------------------------------------------------------------------------------------------------
bool wallet_rpc_server::on_submit_transfer(const wallet_rpc::COMMAND_RPC_SUBMIT_TRANSFER::request& req, wallet_rpc::COMMAND_RPC_SUBMIT_TRANSFER::response& res, epee::json_rpc::error& er)
{
if (!m_wallet) return not_open(er);
@@ -1577,6 +1744,7 @@ namespace tools
epee::wipeable_string seed;
if (!m_wallet->get_seed(seed))
{
+ er.code = WALLET_RPC_ERROR_CODE_NON_DETERMINISTIC;
er.message = "The wallet is non-deterministic. Cannot display seed.";
return false;
}
@@ -2905,6 +3073,11 @@ namespace tools
er.code = WALLET_RPC_ERROR_CODE_ADDRESS_INDEX_OUT_OF_BOUNDS;
er.message = e.what();
}
+ catch (const error::signature_check_failed& e)
+ {
+ er.code = WALLET_RPC_ERROR_CODE_WRONG_SIGNATURE;
+ er.message = e.what();
+ }
catch (const std::exception& e)
{
er.code = default_error_code;
diff --git a/src/wallet/wallet_rpc_server.h b/src/wallet/wallet_rpc_server.h
index 35ea11902..887723ed5 100644
--- a/src/wallet/wallet_rpc_server.h
+++ b/src/wallet/wallet_rpc_server.h
@@ -87,6 +87,7 @@ namespace tools
MAP_JON_RPC_WE("transfer", on_transfer, wallet_rpc::COMMAND_RPC_TRANSFER)
MAP_JON_RPC_WE("transfer_split", on_transfer_split, wallet_rpc::COMMAND_RPC_TRANSFER_SPLIT)
MAP_JON_RPC_WE("sign_transfer", on_sign_transfer, wallet_rpc::COMMAND_RPC_SIGN_TRANSFER)
+ MAP_JON_RPC_WE("describe_transfer", on_describe_transfer, wallet_rpc::COMMAND_RPC_DESCRIBE_TRANSFER)
MAP_JON_RPC_WE("submit_transfer", on_submit_transfer, wallet_rpc::COMMAND_RPC_SUBMIT_TRANSFER)
MAP_JON_RPC_WE("sweep_dust", on_sweep_dust, wallet_rpc::COMMAND_RPC_SWEEP_DUST)
MAP_JON_RPC_WE("sweep_unmixable", on_sweep_dust, wallet_rpc::COMMAND_RPC_SWEEP_DUST)
@@ -167,6 +168,7 @@ namespace tools
bool on_transfer(const wallet_rpc::COMMAND_RPC_TRANSFER::request& req, wallet_rpc::COMMAND_RPC_TRANSFER::response& res, epee::json_rpc::error& er);
bool on_transfer_split(const wallet_rpc::COMMAND_RPC_TRANSFER_SPLIT::request& req, wallet_rpc::COMMAND_RPC_TRANSFER_SPLIT::response& res, epee::json_rpc::error& er);
bool on_sign_transfer(const wallet_rpc::COMMAND_RPC_SIGN_TRANSFER::request& req, wallet_rpc::COMMAND_RPC_SIGN_TRANSFER::response& res, epee::json_rpc::error& er);
+ bool on_describe_transfer(const wallet_rpc::COMMAND_RPC_DESCRIBE_TRANSFER::request& req, wallet_rpc::COMMAND_RPC_DESCRIBE_TRANSFER::response& res, epee::json_rpc::error& er);
bool on_submit_transfer(const wallet_rpc::COMMAND_RPC_SUBMIT_TRANSFER::request& req, wallet_rpc::COMMAND_RPC_SUBMIT_TRANSFER::response& res, epee::json_rpc::error& er);
bool on_sweep_dust(const wallet_rpc::COMMAND_RPC_SWEEP_DUST::request& req, wallet_rpc::COMMAND_RPC_SWEEP_DUST::response& res, epee::json_rpc::error& er);
bool on_sweep_all(const wallet_rpc::COMMAND_RPC_SWEEP_ALL::request& req, wallet_rpc::COMMAND_RPC_SWEEP_ALL::response& res, epee::json_rpc::error& er);
diff --git a/src/wallet/wallet_rpc_server_commands_defs.h b/src/wallet/wallet_rpc_server_commands_defs.h
index 2377b69e3..924f3a0f1 100644
--- a/src/wallet/wallet_rpc_server_commands_defs.h
+++ b/src/wallet/wallet_rpc_server_commands_defs.h
@@ -47,7 +47,7 @@
// advance which version they will stop working with
// Don't go over 32767 for any of these
#define WALLET_RPC_VERSION_MAJOR 1
-#define WALLET_RPC_VERSION_MINOR 4
+#define WALLET_RPC_VERSION_MINOR 5
#define MAKE_WALLET_RPC_VERSION(major,minor) (((major)<<16)|(minor))
#define WALLET_RPC_VERSION MAKE_WALLET_RPC_VERSION(WALLET_RPC_VERSION_MAJOR, WALLET_RPC_VERSION_MINOR)
namespace tools
@@ -531,16 +531,79 @@ namespace wallet_rpc
};
};
+ struct COMMAND_RPC_DESCRIBE_TRANSFER
+ {
+ struct recipient
+ {
+ std::string address;
+ uint64_t amount;
+
+ BEGIN_KV_SERIALIZE_MAP()
+ KV_SERIALIZE(address)
+ KV_SERIALIZE(amount)
+ END_KV_SERIALIZE_MAP()
+ };
+
+ struct transfer_description
+ {
+ uint64_t amount_in;
+ uint64_t amount_out;
+ uint32_t ring_size;
+ uint64_t unlock_time;
+ std::list<recipient> recipients;
+ std::string payment_id;
+ uint64_t change_amount;
+ std::string change_address;
+ uint64_t fee;
+ uint32_t dummy_outputs;
+ std::string extra;
+
+ BEGIN_KV_SERIALIZE_MAP()
+ KV_SERIALIZE(amount_in)
+ KV_SERIALIZE(amount_out)
+ KV_SERIALIZE(ring_size)
+ KV_SERIALIZE(unlock_time)
+ KV_SERIALIZE(recipients)
+ KV_SERIALIZE(payment_id)
+ KV_SERIALIZE(change_amount)
+ KV_SERIALIZE(change_address)
+ KV_SERIALIZE(fee)
+ KV_SERIALIZE(dummy_outputs)
+ KV_SERIALIZE(extra)
+ END_KV_SERIALIZE_MAP()
+ };
+
+ struct request
+ {
+ std::string unsigned_txset;
+
+ BEGIN_KV_SERIALIZE_MAP()
+ KV_SERIALIZE(unsigned_txset)
+ END_KV_SERIALIZE_MAP()
+ };
+
+ struct response
+ {
+ std::list<transfer_description> desc;
+
+ BEGIN_KV_SERIALIZE_MAP()
+ KV_SERIALIZE(desc)
+ END_KV_SERIALIZE_MAP()
+ };
+ };
+
struct COMMAND_RPC_SIGN_TRANSFER
{
struct request
{
std::string unsigned_txset;
bool export_raw;
+ bool get_tx_keys;
BEGIN_KV_SERIALIZE_MAP()
KV_SERIALIZE(unsigned_txset)
KV_SERIALIZE_OPT(export_raw, false)
+ KV_SERIALIZE_OPT(get_tx_keys, false)
END_KV_SERIALIZE_MAP()
};
@@ -549,11 +612,13 @@ namespace wallet_rpc
std::string signed_txset;
std::list<std::string> tx_hash_list;
std::list<std::string> tx_raw_list;
+ std::list<std::string> tx_key_list;
BEGIN_KV_SERIALIZE_MAP()
KV_SERIALIZE(signed_txset)
KV_SERIALIZE(tx_hash_list)
KV_SERIALIZE(tx_raw_list)
+ KV_SERIALIZE(tx_key_list)
END_KV_SERIALIZE_MAP()
};
};
diff --git a/src/wallet/wallet_rpc_server_error_codes.h b/src/wallet/wallet_rpc_server_error_codes.h
index f127ae240..9b3a2847d 100644
--- a/src/wallet/wallet_rpc_server_error_codes.h
+++ b/src/wallet/wallet_rpc_server_error_codes.h
@@ -73,3 +73,4 @@
#define WALLET_RPC_ERROR_CODE_BAD_SIGNED_TX_DATA -40
#define WALLET_RPC_ERROR_CODE_SIGNED_SUBMISSION -41
#define WALLET_RPC_ERROR_CODE_SIGN_UNSIGNED -42
+#define WALLET_RPC_ERROR_CODE_NON_DETERMINISTIC -43
diff --git a/tests/unit_tests/CMakeLists.txt b/tests/unit_tests/CMakeLists.txt
index e248ed965..7687e3c52 100644
--- a/tests/unit_tests/CMakeLists.txt
+++ b/tests/unit_tests/CMakeLists.txt
@@ -123,7 +123,7 @@ SET_PROPERTY(SOURCE memwipe.cpp PROPERTY COMPILE_FLAGS -Ofast)
add_test(
NAME unit_tests
- COMMAND unit_tests --data-dir "${TEST_DATA_DIR} --binary-dir ${CMAKE_BINARY_DIR}")
+ COMMAND unit_tests --data-dir "${TEST_DATA_DIR}")
add_executable(test_notifier test_notifier.cpp)
target_link_libraries(test_notifier ${EXTRA_LIBRARIES})
diff --git a/tests/unit_tests/notify.cpp b/tests/unit_tests/notify.cpp
index d6811c6bd..4daeeddee 100644
--- a/tests/unit_tests/notify.cpp
+++ b/tests/unit_tests/notify.cpp
@@ -26,6 +26,10 @@
// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+#ifdef __GLIBC__
+#include <sys/stat.h>
+#endif
+
#include "gtest/gtest.h"
#include <boost/filesystem.hpp>
@@ -37,12 +41,29 @@
TEST(notify, works)
{
- char name_template[] = "/tmp/monero-notify-unit-test-XXXXXX";
+#ifdef __GLIBC__
+ mode_t prevmode = umask(077);
+#endif
+ const char *tmp = getenv("TEMP");
+ if (!tmp)
+ tmp = "/tmp";
+ static const char *filename = "monero-notify-unit-test-XXXXXX";
+ const size_t len = strlen(tmp) + 1 + strlen(filename);
+ char *name_template = (char*)malloc(len + 1);
+ ASSERT_TRUE(name_template != NULL);
+ snprintf(name_template, len + 1, "%s/%s", tmp, filename);
int fd = mkstemp(name_template);
+#ifdef __GLIBC__
+ umask(prevmode);
+#endif
ASSERT_TRUE(fd >= 0);
close(fd);
- const std::string spec = epee::string_tools::get_current_module_folder() + "/test_notifier " + name_template + " %s";
+ const std::string spec = epee::string_tools::get_current_module_folder() + "/test_notifier"
+#ifdef _WIN32
+ + ".exe"
+#endif
+ + " " + name_template + " %s";
tools::Notify notify(spec.c_str());
notify.notify("1111111111111111111111111111111111111111111111111111111111111111");
@@ -54,4 +75,5 @@ TEST(notify, works)
ASSERT_TRUE(s == "1111111111111111111111111111111111111111111111111111111111111111");
boost::filesystem::remove(name_template);
+ free(name_template);
}
diff --git a/tests/unit_tests/test_tx_utils.cpp b/tests/unit_tests/test_tx_utils.cpp
index 8a9f983e6..55c76c3b6 100644
--- a/tests/unit_tests/test_tx_utils.cpp
+++ b/tests/unit_tests/test_tx_utils.cpp
@@ -33,6 +33,8 @@
#include <vector>
#include "common/util.h"
+#include "cryptonote_basic/cryptonote_basic.h"
+#include "cryptonote_basic/tx_extra.h"
#include "cryptonote_core/cryptonote_tx_utils.h"
namespace
@@ -203,3 +205,85 @@ TEST(validate_parse_amount_case, validate_parse_amount)
r = cryptonote::parse_amount(res, "1 00.00 00");
ASSERT_FALSE(r);
}
+
+TEST(sort_tx_extra, empty)
+{
+ std::vector<uint8_t> extra, sorted;
+ ASSERT_TRUE(cryptonote::sort_tx_extra(extra, sorted));
+ ASSERT_EQ(extra, sorted);
+}
+
+TEST(sort_tx_extra, pubkey)
+{
+ std::vector<uint8_t> sorted;
+ const uint8_t extra_arr[] = {1, 30, 208, 98, 162, 133, 64, 85, 83, 112, 91, 188, 89, 211, 24, 131, 39, 154, 22, 228,
+ 80, 63, 198, 141, 173, 111, 244, 183, 4, 149, 186, 140, 230};
+ std::vector<uint8_t> extra(&extra_arr[0], &extra_arr[0] + sizeof(extra_arr));
+ ASSERT_TRUE(cryptonote::sort_tx_extra(extra, sorted));
+ ASSERT_EQ(extra, sorted);
+}
+
+TEST(sort_tx_extra, two_pubkeys)
+{
+ std::vector<uint8_t> sorted;
+ const uint8_t extra_arr[] = {1, 30, 208, 98, 162, 133, 64, 85, 83, 112, 91, 188, 89, 211, 24, 131, 39, 154, 22, 228,
+ 80, 63, 198, 141, 173, 111, 244, 183, 4, 149, 186, 140, 230,
+ 1, 30, 208, 98, 162, 133, 64, 85, 83, 112, 91, 188, 89, 211, 24, 131, 39, 154, 22, 228,
+ 80, 63, 198, 141, 173, 111, 244, 183, 4, 149, 186, 140, 230};
+ std::vector<uint8_t> extra(&extra_arr[0], &extra_arr[0] + sizeof(extra_arr));
+ ASSERT_TRUE(cryptonote::sort_tx_extra(extra, sorted));
+ ASSERT_EQ(extra, sorted);
+}
+
+TEST(sort_tx_extra, keep_order)
+{
+ std::vector<uint8_t> sorted;
+ const uint8_t extra_arr[] = {1, 30, 208, 98, 162, 133, 64, 85, 83, 112, 91, 188, 89, 211, 24, 131, 39, 154, 22, 228,
+ 80, 63, 198, 141, 173, 111, 244, 183, 4, 149, 186, 140, 230,
+ 2, 9, 1, 0, 0, 0, 0, 0, 0, 0, 0};
+ std::vector<uint8_t> extra(&extra_arr[0], &extra_arr[0] + sizeof(extra_arr));
+ ASSERT_TRUE(cryptonote::sort_tx_extra(extra, sorted));
+ ASSERT_EQ(extra, sorted);
+}
+
+TEST(sort_tx_extra, switch_order)
+{
+ std::vector<uint8_t> sorted;
+ const uint8_t extra_arr[] = {2, 9, 1, 0, 0, 0, 0, 0, 0, 0, 0,
+ 1, 30, 208, 98, 162, 133, 64, 85, 83, 112, 91, 188, 89, 211, 24, 131, 39, 154, 22, 228,
+ 80, 63, 198, 141, 173, 111, 244, 183, 4, 149, 186, 140, 230};
+ const uint8_t expected_arr[] = {1, 30, 208, 98, 162, 133, 64, 85, 83, 112, 91, 188, 89, 211, 24, 131, 39, 154, 22, 228,
+ 80, 63, 198, 141, 173, 111, 244, 183, 4, 149, 186, 140, 230,
+ 2, 9, 1, 0, 0, 0, 0, 0, 0, 0, 0};
+ std::vector<uint8_t> extra(&extra_arr[0], &extra_arr[0] + sizeof(extra_arr));
+ ASSERT_TRUE(cryptonote::sort_tx_extra(extra, sorted));
+ std::vector<uint8_t> expected(&expected_arr[0], &expected_arr[0] + sizeof(expected_arr));
+ ASSERT_EQ(expected, sorted);
+}
+
+TEST(sort_tx_extra, invalid)
+{
+ std::vector<uint8_t> sorted;
+ const uint8_t extra_arr[] = {1};
+ std::vector<uint8_t> extra(&extra_arr[0], &extra_arr[0] + sizeof(extra_arr));
+ ASSERT_FALSE(cryptonote::sort_tx_extra(extra, sorted));
+}
+
+TEST(sort_tx_extra, invalid_suffix_strict)
+{
+ std::vector<uint8_t> sorted;
+ const uint8_t extra_arr[] = {2, 9, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1};
+ std::vector<uint8_t> extra(&extra_arr[0], &extra_arr[0] + sizeof(extra_arr));
+ ASSERT_FALSE(cryptonote::sort_tx_extra(extra, sorted));
+}
+
+TEST(sort_tx_extra, invalid_suffix_partial)
+{
+ std::vector<uint8_t> sorted;
+ const uint8_t extra_arr[] = {2, 9, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1};
+ const uint8_t expected_arr[] = {2, 9, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1};
+ std::vector<uint8_t> extra(&extra_arr[0], &extra_arr[0] + sizeof(extra_arr));
+ ASSERT_TRUE(cryptonote::sort_tx_extra(extra, sorted, true));
+ std::vector<uint8_t> expected(&expected_arr[0], &expected_arr[0] + sizeof(expected_arr));
+ ASSERT_EQ(sorted, expected);
+}
diff --git a/translations/monero.ts b/translations/monero.ts
index 8d4ee7ab8..e28a2a058 100644
--- a/translations/monero.ts
+++ b/translations/monero.ts
@@ -1727,7 +1727,16 @@ Otherwise, you prove the reserve of the smallest possible amount above &lt;amoun
</message>
<message>
<location filename="../src/simplewallet/simplewallet.cpp" line="2432"/>
- <source>Show the incoming/outgoing transfers within an optional height range.</source>
+ <source>Show the incoming/outgoing transfers within an optional height range.
+
+Output format:
+In or Coinbase: Block Number, &quot;block&quot;|&quot;in&quot;, Time, Amount, Transaction Hash, Payment ID, Subaddress Index, &quot;-&quot;, Note\
+Out: Block Number, &quot;out&quot;, Time, Amount*, Transaction Hash, Payment ID, Fee, Destinations, Input addresses**, &quot;-&quot;, Note
+Pool: &quot;pool&quot;, &quot;in&quot;, Time, Amount, Transaction Hash, Payment Id, Subaddress Index, &quot;-&quot;, Note, Double Spend Note\
+Pending or Failed: &quot;failed&quot;|&quot;pending&quot;, &quot;out&quot;, Time, Amount*, Transaction Hash, Payment ID, Fee, Input addresses**, &quot;-&quot;, Note
+
+* Excluding change and fee.
+** Set of address indices used as inputs in this transfer.</source>
<translation type="unfinished"></translation>
</message>
<message>
diff --git a/translations/monero_fr.ts b/translations/monero_fr.ts
index bac742c56..27c38ee10 100644
--- a/translations/monero_fr.ts
+++ b/translations/monero_fr.ts
@@ -1789,7 +1789,16 @@ Sinon, vous prouvez le plus petit solde supérieur à &lt;montant&gt; dans votre
</message>
<message>
<location filename="../src/simplewallet/simplewallet.cpp" line="2432"/>
- <source>Show the incoming/outgoing transfers within an optional height range.</source>
+ <source>Show the incoming/outgoing transfers within an optional height range.
+
+Output format:
+In or Coinbase: Block Number, &quot;block&quot;|&quot;in&quot;, Time, Amount, Transaction Hash, Payment ID, Subaddress Index, &quot;-&quot;, Note\
+Out: Block Number, &quot;out&quot;, Time, Amount*, Transaction Hash, Payment ID, Fee, Destinations, Input addresses**, &quot;-&quot;, Note
+Pool: &quot;pool&quot;, &quot;in&quot;, Time, Amount, Transaction Hash, Payment Id, Subaddress Index, &quot;-&quot;, Note, Double Spend Note\
+Pending or Failed: &quot;failed&quot;|&quot;pending&quot;, &quot;out&quot;, Time, Amount*, Transaction Hash, Payment ID, Fee, Input addresses**, &quot;-&quot;, Note
+
+* Excluding change and fee.
+** Set of address indices used as inputs in this transfer.</source>
<translation>Afficher les transferts entrants/sortants dans un interval de hauteurs facultatif.</translation>
</message>
<message>
diff --git a/translations/monero_it.ts b/translations/monero_it.ts
index d4b1695f3..5ab96d7dc 100644
--- a/translations/monero_it.ts
+++ b/translations/monero_it.ts
@@ -1702,7 +1702,16 @@ Otherwise, you prove the reserve of the smallest possible amount above &lt;amoun
</message>
<message>
<location filename="../src/simplewallet/simplewallet.cpp" line="1758"/>
- <source>Show the incoming/outgoing transfers within an optional height range.</source>
+ <source>Show the incoming/outgoing transfers within an optional height range.
+
+Output format:
+In or Coinbase: Block Number, &quot;block&quot;|&quot;in&quot;, Time, Amount, Transaction Hash, Payment ID, Subaddress Index, &quot;-&quot;, Note\
+Out: Block Number, &quot;out&quot;, Time, Amount*, Transaction Hash, Payment ID, Fee, Destinations, Input addresses**, &quot;-&quot;, Note
+Pool: &quot;pool&quot;, &quot;in&quot;, Time, Amount, Transaction Hash, Payment Id, Subaddress Index, &quot;-&quot;, Note, Double Spend Note\
+Pending or Failed: &quot;failed&quot;|&quot;pending&quot;, &quot;out&quot;, Time, Amount*, Transaction Hash, Payment ID, Fee, Input addresses**, &quot;-&quot;, Note
+
+* Excluding change and fee.
+** Set of address indices used as inputs in this transfer.</source>
<translation type="unfinished"></translation>
</message>
<message>
diff --git a/translations/monero_ja.ts b/translations/monero_ja.ts
new file mode 100644
index 000000000..17815d982
--- /dev/null
+++ b/translations/monero_ja.ts
@@ -0,0 +1,4072 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!DOCTYPE TS>
+<TS version="2.1" language="ja" sourcelanguage="en">
+<context>
+ <name>Monero::AddressBookImpl</name>
+ <message>
+ <location filename="../src/wallet/api/address_book.cpp" line="53"/>
+ <source>Invalid destination address</source>
+ <translation>不正な宛先アドレス</translation>
+ </message>
+ <message>
+ <location filename="../src/wallet/api/address_book.cpp" line="63"/>
+ <source>Invalid payment ID. Short payment ID should only be used in an integrated address</source>
+ <translation>不正なペイメントIDありっます。インテグレーテットアドレスにだけ短いペイメントIDを使えます</translation>
+ </message>
+ <message>
+ <location filename="../src/wallet/api/address_book.cpp" line="70"/>
+ <source>Invalid payment ID</source>
+ <translation>不正なペイメントID</translation>
+ </message>
+ <message>
+ <location filename="../src/wallet/api/address_book.cpp" line="77"/>
+ <source>Integrated address and long payment ID can&apos;t be used at the same time</source>
+ <translation>同じ時にインテグレーテットアドレスと長いペイメントIDを使えません</translation>
+ </message>
+</context>
+<context>
+ <name>Monero::PendingTransactionImpl</name>
+ <message>
+ <location filename="../src/wallet/api/pending_transaction.cpp" line="90"/>
+ <source>Attempting to save transaction to file, but specified file(s) exist. Exiting to not risk overwriting. File:</source>
+ <translation>ファイルは既に存在するのでファイルに取引を書き出せなかった。上書きしないにエグジットしてます。ファイル:</translation>
+ </message>
+ <message>
+ <location filename="../src/wallet/api/pending_transaction.cpp" line="97"/>
+ <source>Failed to write transaction(s) to file</source>
+ <translation>取引をファイルに書き込めませんでした</translation>
+ </message>
+ <message>
+ <location filename="../src/wallet/api/pending_transaction.cpp" line="115"/>
+ <source>daemon is busy. Please try again later.</source>
+ <translation>デーモンは忙しいです。後でもう一度試してください。</translation>
+ </message>
+ <message>
+ <location filename="../src/wallet/api/pending_transaction.cpp" line="118"/>
+ <source>no connection to daemon. Please make sure daemon is running.</source>
+ <translation>デーモンの接続が確立ありません。デーモンが実行中になっていることを確認してください。</translation>
+ </message>
+ <message>
+ <location filename="../src/wallet/api/pending_transaction.cpp" line="122"/>
+ <source>transaction %s was rejected by daemon with status: </source>
+ <translation>取引 %s がデーモンによって拒否しました。ステータス: </translation>
+ </message>
+ <message>
+ <location filename="../src/wallet/api/pending_transaction.cpp" line="127"/>
+ <source>. Reason: </source>
+ <translation>。 理由: </translation>
+ </message>
+ <message>
+ <location filename="../src/wallet/api/pending_transaction.cpp" line="129"/>
+ <source>Unknown exception: </source>
+ <translation>未知の例外: </translation>
+ </message>
+ <message>
+ <location filename="../src/wallet/api/pending_transaction.cpp" line="132"/>
+ <source>Unhandled exception</source>
+ <translation>未処理の例外</translation>
+ </message>
+</context>
+<context>
+ <name>Monero::UnsignedTransactionImpl</name>
+ <message>
+ <location filename="../src/wallet/api/unsigned_transaction.cpp" line="75"/>
+ <source>This is a watch only wallet</source>
+ <translation>これは閲覧専用ウォレットです</translation>
+ </message>
+ <message>
+ <location filename="../src/wallet/api/unsigned_transaction.cpp" line="85"/>
+ <location filename="../src/wallet/api/unsigned_transaction.cpp" line="92"/>
+ <source>Failed to sign transaction</source>
+ <translation>取引を署名できませんでした</translation>
+ </message>
+ <message>
+ <location filename="../src/wallet/api/unsigned_transaction.cpp" line="168"/>
+ <source>Claimed change does not go to a paid address</source>
+ <translation>請求したお釣りはもうお金に送ったアドレス送りません</translation>
+ </message>
+ <message>
+ <location filename="../src/wallet/api/unsigned_transaction.cpp" line="174"/>
+ <source>Claimed change is larger than payment to the change address</source>
+ <translation>請求したお釣りはお釣りのアドレスに送ったペイメントより大きいです</translation>
+ </message>
+ <message>
+ <location filename="../src/wallet/api/unsigned_transaction.cpp" line="184"/>
+ <source>Change goes to more than one address</source>
+ <translation>お釣りは複数のアドレスに送ります</translation>
+ </message>
+ <message>
+ <location filename="../src/wallet/api/unsigned_transaction.cpp" line="197"/>
+ <source>sending %s to %s</source>
+ <translation>%s を %s に送ってます</translation>
+ </message>
+ <message>
+ <location filename="../src/wallet/api/unsigned_transaction.cpp" line="203"/>
+ <source>with no destinations</source>
+ <translation>目的地なし</translation>
+ </message>
+ <message>
+ <location filename="../src/wallet/api/unsigned_transaction.cpp" line="209"/>
+ <source>%s change to %s</source>
+ <translation>%s のお釣り %s に</translation>
+ </message>
+ <message>
+ <location filename="../src/wallet/api/unsigned_transaction.cpp" line="212"/>
+ <source>no change</source>
+ <translation>お釣りありません</translation>
+ </message>
+ <message>
+ <location filename="../src/wallet/api/unsigned_transaction.cpp" line="214"/>
+ <source>Loaded %lu transactions, for %s, fee %s, %s, %s, with min ring size %lu. %s</source>
+ <translation>取引は %lu ロードした、 %s に、%s のの手数料、 %s 、 %s 、最小リングサイズ %lu 。%s</translation>
+ </message>
+</context>
+<context>
+ <name>Monero::WalletImpl</name>
+ <message>
+ <location filename="../src/wallet/api/wallet.cpp" line="1111"/>
+ <source>payment id has invalid format, expected 16 or 64 character hex string: </source>
+ <translation>ペイメントIDのフォーマットは不正です。16文字または64文字の16進数の文字列が必要で: </translation>
+ </message>
+ <message>
+ <location filename="../src/wallet/api/wallet.cpp" line="1121"/>
+ <source>Failed to add short payment id: </source>
+ <translation>短いペイメントIDの追加に失敗しました: </translation>
+ </message>
+ <message>
+ <location filename="../src/wallet/api/wallet.cpp" line="1154"/>
+ <location filename="../src/wallet/api/wallet.cpp" line="1258"/>
+ <source>daemon is busy. Please try again later.</source>
+ <translation>デーモンは忙しいです。後でもう一度試してください。</translation>
+ </message>
+ <message>
+ <location filename="../src/wallet/api/wallet.cpp" line="1157"/>
+ <location filename="../src/wallet/api/wallet.cpp" line="1261"/>
+ <source>no connection to daemon. Please make sure daemon is running.</source>
+ <translation>デーモンの接続が確立ありません。デーモンが実行中になっていることを確認してください。</translation>
+ </message>
+ <message>
+ <location filename="../src/wallet/api/wallet.cpp" line="1160"/>
+ <location filename="../src/wallet/api/wallet.cpp" line="1264"/>
+ <source>RPC error: </source>
+ <translation>RPCエラー: </translation>
+ </message>
+ <message>
+ <location filename="../src/wallet/api/wallet.cpp" line="1197"/>
+ <location filename="../src/wallet/api/wallet.cpp" line="1301"/>
+ <source>not enough outputs for specified ring size</source>
+ <translation>指定したリングサイズのアウトプットが不十分です</translation>
+ </message>
+ <message>
+ <location filename="../src/wallet/api/wallet.cpp" line="1199"/>
+ <location filename="../src/wallet/api/wallet.cpp" line="1303"/>
+ <source>found outputs to use</source>
+ <translation>使うためにアウトプットを見つかれました</translation>
+ </message>
+ <message>
+ <location filename="../src/wallet/api/wallet.cpp" line="1201"/>
+ <source>Please sweep unmixable outputs.</source>
+ <translation>ミックス不能なアウトプットをスイープしてください。</translation>
+ </message>
+ <message>
+ <location filename="../src/wallet/api/wallet.cpp" line="1267"/>
+ <source>failed to get random outputs to mix</source>
+ <translation>ランダムなアウトプットをミックスすることに失敗しました</translation>
+ </message>
+ <message>
+ <location filename="../src/wallet/api/wallet.cpp" line="1170"/>
+ <location filename="../src/wallet/api/wallet.cpp" line="1274"/>
+ <source>not enough money to transfer, available only %s, sent amount %s</source>
+ <translation>振替でMoneroを受け取ることできません。利用可能な金額: %s, 取引の金額: %s</translation>
+ </message>
+ <message>
+ <location filename="../src/wallet/api/wallet.cpp" line="474"/>
+ <source>failed to parse address</source>
+ <translation>アドレスの解析に失敗しました</translation>
+ </message>
+ <message>
+ <location filename="../src/wallet/api/wallet.cpp" line="486"/>
+ <source>failed to parse secret spend key</source>
+ <translation>秘密なスペンドキーの解析に失敗しました</translation>
+ </message>
+ <message>
+ <location filename="../src/wallet/api/wallet.cpp" line="496"/>
+ <source>No view key supplied, cancelled</source>
+ <translation>ビューキーをもらいませんでしたのでキャンセルしました</translation>
+ </message>
+ <message>
+ <location filename="../src/wallet/api/wallet.cpp" line="503"/>
+ <source>failed to parse secret view key</source>
+ <translation>秘密なビューキーの解析に失敗しました</translation>
+ </message>
+ <message>
+ <location filename="../src/wallet/api/wallet.cpp" line="513"/>
+ <source>failed to verify secret spend key</source>
+ <translation>秘密なスペンドキーの検証に失敗しました</translation>
+ </message>
+ <message>
+ <location filename="../src/wallet/api/wallet.cpp" line="518"/>
+ <source>spend key does not match address</source>
+ <translation>スペンドキーがアドレスと一致しませんでした</translation>
+ </message>
+ <message>
+ <location filename="../src/wallet/api/wallet.cpp" line="524"/>
+ <source>failed to verify secret view key</source>
+ <translation>秘密なビューキーの検証に失敗しました</translation>
+ </message>
+ <message>
+ <location filename="../src/wallet/api/wallet.cpp" line="529"/>
+ <source>view key does not match address</source>
+ <translation>ビューキーがアドレスと一致しませんでした</translation>
+ </message>
+ <message>
+ <location filename="../src/wallet/api/wallet.cpp" line="548"/>
+ <source>failed to generate new wallet: </source>
+ <translation>新しいウォレットの生成に失敗しました: </translation>
+ </message>
+ <message>
+ <location filename="../src/wallet/api/wallet.cpp" line="773"/>
+ <source>Failed to send import wallet request</source>
+ <translation>インポートウォレットリクエストの送信に失敗しました</translation>
+ </message>
+ <message>
+ <location filename="../src/wallet/api/wallet.cpp" line="919"/>
+ <source>Failed to load unsigned transactions</source>
+ <translation>未署名の取引を読み込めませんでした</translation>
+ </message>
+ <message>
+ <location filename="../src/wallet/api/wallet.cpp" line="940"/>
+ <source>Failed to load transaction from file</source>
+ <translation>ファイルからの取引のロードに失敗しました</translation>
+ </message>
+ <message>
+ <location filename="../src/wallet/api/wallet.cpp" line="958"/>
+ <source>Wallet is view only</source>
+ <translation>閲覧専用ウォレットです</translation>
+ </message>
+ <message>
+ <location filename="../src/wallet/api/wallet.cpp" line="967"/>
+ <source>failed to save file </source>
+ <translation>ファイルを保存できませんでした </translation>
+ </message>
+ <message>
+ <location filename="../src/wallet/api/wallet.cpp" line="986"/>
+ <source>Key images can only be imported with a trusted daemon</source>
+ <translation>信頼できるデーモンしかでキーイメージをインポートしません</translation>
+ </message>
+ <message>
+ <location filename="../src/wallet/api/wallet.cpp" line="999"/>
+ <source>Failed to import key images: </source>
+ <translation>キーイメージをインポートできませんでした: </translation>
+ </message>
+ <message>
+ <location filename="../src/wallet/api/wallet.cpp" line="1032"/>
+ <source>Failed to get subaddress label: </source>
+ <translation>サブアドレスラベルを取得できませんでした: </translation>
+ </message>
+ <message>
+ <location filename="../src/wallet/api/wallet.cpp" line="1046"/>
+ <source>Failed to set subaddress label: </source>
+ <translation>サブアドレスラベルをセットできませんでした: </translation>
+ </message>
+ <message>
+ <location filename="../src/wallet/api/wallet.cpp" line="1163"/>
+ <source>failed to get random outputs to mix: %s</source>
+ <translation>ランダムなアウトプットをミックスすることに失敗しました: %s</translation>
+ </message>
+ <message>
+ <location filename="../src/wallet/api/wallet.cpp" line="1179"/>
+ <location filename="../src/wallet/api/wallet.cpp" line="1283"/>
+ <source>not enough money to transfer, overall balance only %s, sent amount %s</source>
+ <translation>振替でMoneroを受け取ることできません。利用可能な金額: %s, 取引の金額: %s</translation>
+ </message>
+ <message>
+ <location filename="../src/wallet/api/wallet.cpp" line="1188"/>
+ <location filename="../src/wallet/api/wallet.cpp" line="1292"/>
+ <source>not enough money to transfer, available only %s, transaction amount %s = %s + %s (fee)</source>
+ <translation>取引は無理です。利用可能な金額 %s、 取引の金額 %s = %s + %s (手数料)</translation>
+ </message>
+ <message>
+ <location filename="../src/wallet/api/wallet.cpp" line="1199"/>
+ <location filename="../src/wallet/api/wallet.cpp" line="1303"/>
+ <source>output amount</source>
+ <translation>アウトプットの金額</translation>
+ </message>
+ <message>
+ <location filename="../src/wallet/api/wallet.cpp" line="1205"/>
+ <location filename="../src/wallet/api/wallet.cpp" line="1308"/>
+ <source>transaction was not constructed</source>
+ <translation>取引を作りませんでした</translation>
+ </message>
+ <message>
+ <location filename="../src/wallet/api/wallet.cpp" line="1209"/>
+ <location filename="../src/wallet/api/wallet.cpp" line="1312"/>
+ <source>transaction %s was rejected by daemon with status: </source>
+ <translation>取引 %s がデーモンによって拒否しました。ステータス: </translation>
+ </message>
+ <message>
+ <location filename="../src/wallet/api/wallet.cpp" line="1216"/>
+ <location filename="../src/wallet/api/wallet.cpp" line="1319"/>
+ <source>one of destinations is zero</source>
+ <translation>宛先の1つはゼロです</translation>
+ </message>
+ <message>
+ <location filename="../src/wallet/api/wallet.cpp" line="1219"/>
+ <location filename="../src/wallet/api/wallet.cpp" line="1322"/>
+ <source>failed to find a suitable way to split transactions</source>
+ <translation>取引を分割する適切な方法を見つけることができませんでした</translation>
+ </message>
+ <message>
+ <location filename="../src/wallet/api/wallet.cpp" line="1222"/>
+ <location filename="../src/wallet/api/wallet.cpp" line="1325"/>
+ <source>unknown transfer error: </source>
+ <translation>不明な転送エラー: </translation>
+ </message>
+ <message>
+ <location filename="../src/wallet/api/wallet.cpp" line="1225"/>
+ <location filename="../src/wallet/api/wallet.cpp" line="1328"/>
+ <source>internal error: </source>
+ <translation>内部エラー: </translation>
+ </message>
+ <message>
+ <location filename="../src/wallet/api/wallet.cpp" line="1228"/>
+ <location filename="../src/wallet/api/wallet.cpp" line="1331"/>
+ <source>unexpected error: </source>
+ <translation>予期せぬエラー: </translation>
+ </message>
+ <message>
+ <location filename="../src/wallet/api/wallet.cpp" line="1231"/>
+ <location filename="../src/wallet/api/wallet.cpp" line="1334"/>
+ <source>unknown error</source>
+ <translation>不明なエラー</translation>
+ </message>
+ <message>
+ <location filename="../src/wallet/api/wallet.cpp" line="1412"/>
+ <location filename="../src/wallet/api/wallet.cpp" line="1441"/>
+ <location filename="../src/wallet/api/wallet.cpp" line="1494"/>
+ <location filename="../src/wallet/api/wallet.cpp" line="1525"/>
+ <location filename="../src/wallet/api/wallet.cpp" line="1556"/>
+ <location filename="../src/wallet/api/wallet.cpp" line="1579"/>
+ <source>Failed to parse txid</source>
+ <translation>txidの解析に失敗しました</translation>
+ </message>
+ <message>
+ <location filename="../src/wallet/api/wallet.cpp" line="1430"/>
+ <source>no tx keys found for this txid</source>
+ <translation>このtxidのためにtxキーを見つかれませんでした</translation>
+ </message>
+ <message>
+ <location filename="../src/wallet/api/wallet.cpp" line="1450"/>
+ <location filename="../src/wallet/api/wallet.cpp" line="1460"/>
+ <source>Failed to parse tx key</source>
+ <translation>txキーの解析に失敗しました</translation>
+ </message>
+ <message>
+ <location filename="../src/wallet/api/wallet.cpp" line="1470"/>
+ <location filename="../src/wallet/api/wallet.cpp" line="1502"/>
+ <location filename="../src/wallet/api/wallet.cpp" line="1533"/>
+ <location filename="../src/wallet/api/wallet.cpp" line="1621"/>
+ <source>Failed to parse address</source>
+ <translation>アドレスの解析に失敗しました</translation>
+ </message>
+ <message>
+ <location filename="../src/wallet/api/wallet.cpp" line="1627"/>
+ <source>Address must not be a subaddress</source>
+ <translation>アドレスはサブアドレスであってはならないです</translation>
+ </message>
+ <message>
+ <location filename="../src/wallet/api/wallet.cpp" line="1849"/>
+ <source>Rescan spent can only be used with a trusted daemon</source>
+ <translation>信頼できるデーモンしかで再スキャンしません</translation>
+ </message>
+</context>
+<context>
+ <name>Wallet</name>
+ <message>
+ <location filename="../src/wallet/api/wallet.cpp" line="246"/>
+ <source>Failed to parse address</source>
+ <translation>アドレスの解析に失敗しました</translation>
+ </message>
+ <message>
+ <location filename="../src/wallet/api/wallet.cpp" line="253"/>
+ <source>Failed to parse key</source>
+ <translation>キーの解析に失敗しました</translation>
+ </message>
+ <message>
+ <location filename="../src/wallet/api/wallet.cpp" line="261"/>
+ <source>failed to verify key</source>
+ <translation>キーの検証に失敗しました</translation>
+ </message>
+ <message>
+ <location filename="../src/wallet/api/wallet.cpp" line="271"/>
+ <source>key does not match address</source>
+ <translation>キーがアドレスと一致しませんでした</translation>
+ </message>
+</context>
+<context>
+ <name>command_line</name>
+ <message>
+ <location filename="../src/common/command_line.cpp" line="57"/>
+ <source>yes</source>
+ <translation>はい</translation>
+ </message>
+ <message>
+ <location filename="../src/common/command_line.cpp" line="71"/>
+ <source>no</source>
+ <translation>いいえ</translation>
+ </message>
+</context>
+<context>
+ <name>cryptonote::rpc_args</name>
+ <message>
+ <location filename="../src/rpc/rpc_args.cpp" line="40"/>
+ <source>Specify IP to bind RPC server</source>
+ <translation>RPCサーバに接続するIPを指定してください</translation>
+ </message>
+ <message>
+ <location filename="../src/rpc/rpc_args.cpp" line="41"/>
+ <source>Specify username[:password] required for RPC server</source>
+ <translation>RPCサーバを使うためにユーザー名[:パスワード]を指定してください</translation>
+ </message>
+ <message>
+ <location filename="../src/rpc/rpc_args.cpp" line="42"/>
+ <source>Confirm rpc-bind-ip value is NOT a loopback (local) IP</source>
+ <translation>rpc-bind-ipの価はループバック(ローカル)IPじゃないことを確認してください</translation>
+ </message>
+ <message>
+ <location filename="../src/rpc/rpc_args.cpp" line="43"/>
+ <source>Specify a comma separated list of origins to allow cross origin resource sharing</source>
+ <translation>クロスオリジンリソース共同をできるためにコンマ区切りリストを指定してください</translation>
+ </message>
+ <message>
+ <location filename="../src/rpc/rpc_args.cpp" line="70"/>
+ <source>Invalid IP address given for --</source>
+ <translation>このRPCサーバーのIPはだめです --</translation>
+ </message>
+ <message>
+ <location filename="../src/rpc/rpc_args.cpp" line="78"/>
+ <source> permits inbound unencrypted external connections. Consider SSH tunnel or SSL proxy instead. Override with --</source>
+ <translation> は暗号化されていない外部接続をできますがSSHトンネルやSSLプロキシの方がいいです。これでオーバーライド --</translation>
+ </message>
+ <message>
+ <location filename="../src/rpc/rpc_args.cpp" line="95"/>
+ <source>Username specified with --</source>
+ <translation>このRPCサーバのユーザー名につて --</translation>
+ </message>
+ <message>
+ <location filename="../src/rpc/rpc_args.cpp" line="95"/>
+ <location filename="../src/rpc/rpc_args.cpp" line="105"/>
+ <source> cannot be empty</source>
+ <translation> 入力する必要があります</translation>
+ </message>
+ <message>
+ <location filename="../src/rpc/rpc_args.cpp" line="105"/>
+ <source> requires RPC server password --</source>
+ <translation> のRPCサーバのパスワードありません --</translation>
+ </message>
+</context>
+<context>
+ <name>cryptonote::simple_wallet</name>
+ <message>
+ <location filename="../src/simplewallet/simplewallet.cpp" line="479"/>
+ <source>Commands: </source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../src/simplewallet/simplewallet.cpp" line="3008"/>
+ <source>failed to read wallet password</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../src/simplewallet/simplewallet.cpp" line="2699"/>
+ <source>invalid password</source>
+ <translation>不正なパスワード</translation>
+ </message>
+ <message>
+ <location filename="../src/simplewallet/simplewallet.cpp" line="1905"/>
+ <source>set seed: needs an argument. available options: language</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../src/simplewallet/simplewallet.cpp" line="1933"/>
+ <source>set: unrecognized argument(s)</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../src/simplewallet/simplewallet.cpp" line="2869"/>
+ <source>wallet file path not valid: </source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../src/simplewallet/simplewallet.cpp" line="1987"/>
+ <source>Attempting to generate or restore wallet, but specified file(s) exist. Exiting to not risk overwriting.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../src/simplewallet/simplewallet.cpp" line="662"/>
+ <source>usage: payment_id</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../src/simplewallet/simplewallet.cpp" line="1891"/>
+ <source>needs an argument</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../src/simplewallet/simplewallet.cpp" line="1914"/>
+ <location filename="../src/simplewallet/simplewallet.cpp" line="1915"/>
+ <location filename="../src/simplewallet/simplewallet.cpp" line="1916"/>
+ <location filename="../src/simplewallet/simplewallet.cpp" line="1918"/>
+ <location filename="../src/simplewallet/simplewallet.cpp" line="1921"/>
+ <location filename="../src/simplewallet/simplewallet.cpp" line="1922"/>
+ <location filename="../src/simplewallet/simplewallet.cpp" line="1926"/>
+ <location filename="../src/simplewallet/simplewallet.cpp" line="1927"/>
+ <location filename="../src/simplewallet/simplewallet.cpp" line="1929"/>
+ <location filename="../src/simplewallet/simplewallet.cpp" line="1931"/>
+ <source>0 or 1</source>
+ <translation>0や1</translation>
+ </message>
+ <message>
+ <location filename="../src/simplewallet/simplewallet.cpp" line="1920"/>
+ <source>0, 1, 2, 3, or 4</source>
+ <translation>0、1、2、3、や 4</translation>
+ </message>
+ <message>
+ <location filename="../src/simplewallet/simplewallet.cpp" line="1924"/>
+ <location filename="../src/simplewallet/simplewallet.cpp" line="1928"/>
+ <source>unsigned integer</source>
+ <translation>符号無しの整数</translation>
+ </message>
+ <message>
+ <location filename="../src/simplewallet/simplewallet.cpp" line="2041"/>
+ <source>NOTE: the following 25 words can be used to recover access to your wallet. Write them down and store them somewhere safe and secure. Please do not store them in your email or on file storage services outside of your immediate control.
+</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../src/simplewallet/simplewallet.cpp" line="2092"/>
+ <source>--restore-deterministic-wallet uses --generate-new-wallet, not --wallet-file</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../src/simplewallet/simplewallet.cpp" line="2121"/>
+ <source>specify a recovery parameter with the --electrum-seed=&quot;words list here&quot;</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../src/simplewallet/simplewallet.cpp" line="2471"/>
+ <source>specify a wallet path with --generate-new-wallet (not --wallet-file)</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../src/simplewallet/simplewallet.cpp" line="2635"/>
+ <source>wallet failed to connect to daemon: </source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../src/simplewallet/simplewallet.cpp" line="2643"/>
+ <source>Daemon uses a different RPC major version (%u) than the wallet (%u): %s. Either update one of them, or use --allow-mismatched-daemon-version.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../src/simplewallet/simplewallet.cpp" line="2662"/>
+ <source>List of available languages for your wallet&apos;s seed:</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../src/simplewallet/simplewallet.cpp" line="2671"/>
+ <source>Enter the number corresponding to the language of your choice: </source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../src/simplewallet/simplewallet.cpp" line="2737"/>
+ <source>You had been using a deprecated version of the wallet. Please use the new seed that we provide.
+</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../src/simplewallet/simplewallet.cpp" line="2751"/>
+ <location filename="../src/simplewallet/simplewallet.cpp" line="2809"/>
+ <source>Generated new wallet: </source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../src/simplewallet/simplewallet.cpp" line="2757"/>
+ <location filename="../src/simplewallet/simplewallet.cpp" line="2814"/>
+ <location filename="../src/simplewallet/simplewallet.cpp" line="2858"/>
+ <source>failed to generate new wallet: </source>
+ <translation>新しいウォレットの生成に失敗しました: </translation>
+ </message>
+ <message>
+ <location filename="../src/simplewallet/simplewallet.cpp" line="2887"/>
+ <source>Opened watch-only wallet</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../src/simplewallet/simplewallet.cpp" line="2891"/>
+ <source>Opened wallet</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../src/simplewallet/simplewallet.cpp" line="2901"/>
+ <source>You had been using a deprecated version of the wallet. Please proceed to upgrade your wallet.
+</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../src/simplewallet/simplewallet.cpp" line="2916"/>
+ <source>You had been using a deprecated version of the wallet. Your wallet file format is being upgraded now.
+</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../src/simplewallet/simplewallet.cpp" line="2924"/>
+ <source>failed to load wallet: </source>
+ <translation>ウォレットをロードできませんでした: </translation>
+ </message>
+ <message>
+ <location filename="../src/simplewallet/simplewallet.cpp" line="2941"/>
+ <source>Use the &quot;help&quot; command to see the list of available commands.
+</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../src/simplewallet/simplewallet.cpp" line="2986"/>
+ <source>Wallet data saved</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../src/simplewallet/simplewallet.cpp" line="3072"/>
+ <source>Mining started in daemon</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../src/simplewallet/simplewallet.cpp" line="3074"/>
+ <source>mining has NOT been started: </source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../src/simplewallet/simplewallet.cpp" line="3093"/>
+ <source>Mining stopped in daemon</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../src/simplewallet/simplewallet.cpp" line="3095"/>
+ <source>mining has NOT been stopped: </source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../src/simplewallet/simplewallet.cpp" line="3150"/>
+ <source>Blockchain saved</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../src/simplewallet/simplewallet.cpp" line="3165"/>
+ <location filename="../src/simplewallet/simplewallet.cpp" line="3183"/>
+ <location filename="../src/simplewallet/simplewallet.cpp" line="3196"/>
+ <source>Height </source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../src/simplewallet/simplewallet.cpp" line="3197"/>
+ <source>transaction </source>
+ <translation>取引 </translation>
+ </message>
+ <message>
+ <location filename="../src/simplewallet/simplewallet.cpp" line="3185"/>
+ <source>spent </source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../src/simplewallet/simplewallet.cpp" line="3198"/>
+ <source>unsupported transaction format</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../src/simplewallet/simplewallet.cpp" line="3219"/>
+ <source>Starting refresh...</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../src/simplewallet/simplewallet.cpp" line="3232"/>
+ <source>Refresh done, blocks received: </source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../src/simplewallet/simplewallet.cpp" line="3758"/>
+ <location filename="../src/simplewallet/simplewallet.cpp" line="4230"/>
+ <source>payment id has invalid format, expected 16 or 64 character hex string: </source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../src/simplewallet/simplewallet.cpp" line="3773"/>
+ <source>bad locked_blocks parameter:</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../src/simplewallet/simplewallet.cpp" line="3801"/>
+ <location filename="../src/simplewallet/simplewallet.cpp" line="4248"/>
+ <location filename="../src/simplewallet/simplewallet.cpp" line="4462"/>
+ <source>a single transaction cannot use more than one payment id: </source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../src/simplewallet/simplewallet.cpp" line="3810"/>
+ <location filename="../src/simplewallet/simplewallet.cpp" line="4257"/>
+ <location filename="../src/simplewallet/simplewallet.cpp" line="4430"/>
+ <location filename="../src/simplewallet/simplewallet.cpp" line="4470"/>
+ <source>failed to set up payment id, though it was decoded correctly</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../src/simplewallet/simplewallet.cpp" line="3835"/>
+ <location filename="../src/simplewallet/simplewallet.cpp" line="3916"/>
+ <location filename="../src/simplewallet/simplewallet.cpp" line="3987"/>
+ <location filename="../src/simplewallet/simplewallet.cpp" line="4096"/>
+ <location filename="../src/simplewallet/simplewallet.cpp" line="4271"/>
+ <location filename="../src/simplewallet/simplewallet.cpp" line="4329"/>
+ <location filename="../src/simplewallet/simplewallet.cpp" line="4484"/>
+ <location filename="../src/simplewallet/simplewallet.cpp" line="4527"/>
+ <source>transaction cancelled.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../src/simplewallet/simplewallet.cpp" line="3895"/>
+ <location filename="../src/simplewallet/simplewallet.cpp" line="3905"/>
+ <source>Is this okay anyway? (Y/Yes/N/No): </source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../src/simplewallet/simplewallet.cpp" line="3900"/>
+ <source>There is currently a %u block backlog at that fee level. Is this okay? (Y/Yes/N/No): </source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../src/simplewallet/simplewallet.cpp" line="3905"/>
+ <source>Failed to check for backlog: </source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../src/simplewallet/simplewallet.cpp" line="3946"/>
+ <location filename="../src/simplewallet/simplewallet.cpp" line="4302"/>
+ <source>
+Transaction </source>
+ <translation>
+取引 </translation>
+ </message>
+ <message>
+ <location filename="../src/simplewallet/simplewallet.cpp" line="3951"/>
+ <location filename="../src/simplewallet/simplewallet.cpp" line="4307"/>
+ <source>Spending from address index %d
+</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../src/simplewallet/simplewallet.cpp" line="3953"/>
+ <location filename="../src/simplewallet/simplewallet.cpp" line="4309"/>
+ <source>WARNING: Outputs of multiple addresses are being used together, which might potentially compromise your privacy.
+</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../src/simplewallet/simplewallet.cpp" line="3955"/>
+ <source>Sending %s. </source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../src/simplewallet/simplewallet.cpp" line="3958"/>
+ <source>Your transaction needs to be split into %llu transactions. This will result in a transaction fee being applied to each transaction, for a total fee of %s</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../src/simplewallet/simplewallet.cpp" line="3964"/>
+ <source>The transaction fee is %s</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../src/simplewallet/simplewallet.cpp" line="3967"/>
+ <source>, of which %s is dust from change</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../src/simplewallet/simplewallet.cpp" line="3968"/>
+ <source>.</source>
+ <translation>。</translation>
+ </message>
+ <message>
+ <location filename="../src/simplewallet/simplewallet.cpp" line="3968"/>
+ <source>A total of %s from dust change will be sent to dust address</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../src/simplewallet/simplewallet.cpp" line="3973"/>
+ <source>.
+This transaction will unlock on block %llu, in approximately %s days (assuming 2 minutes per block)</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../src/simplewallet/simplewallet.cpp" line="3999"/>
+ <location filename="../src/simplewallet/simplewallet.cpp" line="4011"/>
+ <location filename="../src/simplewallet/simplewallet.cpp" line="4107"/>
+ <location filename="../src/simplewallet/simplewallet.cpp" line="4119"/>
+ <location filename="../src/simplewallet/simplewallet.cpp" line="4340"/>
+ <location filename="../src/simplewallet/simplewallet.cpp" line="4352"/>
+ <location filename="../src/simplewallet/simplewallet.cpp" line="4537"/>
+ <location filename="../src/simplewallet/simplewallet.cpp" line="4549"/>
+ <source>Failed to write transaction(s) to file</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../src/simplewallet/simplewallet.cpp" line="4003"/>
+ <location filename="../src/simplewallet/simplewallet.cpp" line="4015"/>
+ <location filename="../src/simplewallet/simplewallet.cpp" line="4111"/>
+ <location filename="../src/simplewallet/simplewallet.cpp" line="4123"/>
+ <location filename="../src/simplewallet/simplewallet.cpp" line="4344"/>
+ <location filename="../src/simplewallet/simplewallet.cpp" line="4356"/>
+ <location filename="../src/simplewallet/simplewallet.cpp" line="4541"/>
+ <location filename="../src/simplewallet/simplewallet.cpp" line="4553"/>
+ <source>Unsigned transaction(s) successfully written to file: </source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../src/simplewallet/simplewallet.cpp" line="4066"/>
+ <source>No unmixable outputs found</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../src/simplewallet/simplewallet.cpp" line="4149"/>
+ <source>No address given</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../src/simplewallet/simplewallet.cpp" line="4424"/>
+ <source>failed to parse Payment ID</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../src/simplewallet/simplewallet.cpp" line="4440"/>
+ <source>usage: sweep_single [&lt;priority&gt;] [&lt;ring_size&gt;] &lt;key_image&gt; &lt;address&gt; [&lt;payment_id&gt;]</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../src/simplewallet/simplewallet.cpp" line="4447"/>
+ <source>failed to parse key image</source>
+ <translation>キーイメージの解析に失敗しました</translation>
+ </message>
+ <message>
+ <location filename="../src/simplewallet/simplewallet.cpp" line="4499"/>
+ <source>No outputs found</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../src/simplewallet/simplewallet.cpp" line="4504"/>
+ <source>Multiple transactions are created, which is not supposed to happen</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../src/simplewallet/simplewallet.cpp" line="4509"/>
+ <source>The transaction uses multiple or no inputs, which is not supposed to happen</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../src/simplewallet/simplewallet.cpp" line="4586"/>
+ <source>missing threshold amount</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../src/simplewallet/simplewallet.cpp" line="4591"/>
+ <source>invalid amount threshold</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../src/simplewallet/simplewallet.cpp" line="4601"/>
+ <source>donations are not enabled on the testnet</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../src/simplewallet/simplewallet.cpp" line="4608"/>
+ <source>usage: donate [index=&lt;N1&gt;[,&lt;N2&gt;,...]] [&lt;priority&gt;] [&lt;ring_size&gt;] &lt;amount&gt; [&lt;payment_id&gt;]</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../src/simplewallet/simplewallet.cpp" line="4702"/>
+ <source>Claimed change does not go to a paid address</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../src/simplewallet/simplewallet.cpp" line="4707"/>
+ <source>Claimed change is larger than payment to the change address</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../src/simplewallet/simplewallet.cpp" line="4738"/>
+ <source>sending %s to %s</source>
+ <translation>%s を %s に送ってます</translation>
+ </message>
+ <message>
+ <location filename="../src/simplewallet/simplewallet.cpp" line="4748"/>
+ <source> dummy output(s)</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../src/simplewallet/simplewallet.cpp" line="4751"/>
+ <source>with no destinations</source>
+ <translation>目的地なし</translation>
+ </message>
+ <message>
+ <location filename="../src/simplewallet/simplewallet.cpp" line="4763"/>
+ <source>Loaded %lu transactions, for %s, fee %s, %s, %s, with min ring size %lu, %s. %sIs this okay? (Y/Yes/N/No): </source>
+ <translation>取引は %lu ロードした、 %s に、%s のの手数料、 %s 、 %s 、最小リングサイズ %lu 、%s。これは大丈夫ですか?&#x3000;はい&#x3000;(Y)&#x3000;いいえ (N): </translation>
+ </message>
+ <message>
+ <location filename="../src/simplewallet/simplewallet.cpp" line="4787"/>
+ <source>This is a multisig wallet, it can only sign with sign_multisig</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../src/simplewallet/simplewallet.cpp" line="4797"/>
+ <source>usage: sign_transfer [export]</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../src/simplewallet/simplewallet.cpp" line="4809"/>
+ <source>Failed to sign transaction</source>
+ <translation>取引を署名できませんでした</translation>
+ </message>
+ <message>
+ <location filename="../src/simplewallet/simplewallet.cpp" line="4815"/>
+ <source>Failed to sign transaction: </source>
+ <translation>取引を署名できませんでした: </translation>
+ </message>
+ <message>
+ <location filename="../src/simplewallet/simplewallet.cpp" line="4836"/>
+ <source>Transaction raw hex data exported to </source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../src/simplewallet/simplewallet.cpp" line="4852"/>
+ <source>Failed to load transaction from file</source>
+ <translation>ファイルからの取引のロードに失敗しました</translation>
+ </message>
+ <message>
+ <location filename="../src/simplewallet/simplewallet.cpp" line="3248"/>
+ <location filename="../src/simplewallet/simplewallet.cpp" line="3551"/>
+ <source>RPC error: </source>
+ <translation>RPCエラー: </translation>
+ </message>
+ <message>
+ <location filename="../src/simplewallet/simplewallet.cpp" line="522"/>
+ <source>wallet is watch-only and has no spend key</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../src/simplewallet/simplewallet.cpp" line="636"/>
+ <location filename="../src/simplewallet/simplewallet.cpp" line="780"/>
+ <location filename="../src/simplewallet/simplewallet.cpp" line="848"/>
+ <source>Your original password was incorrect.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../src/simplewallet/simplewallet.cpp" line="650"/>
+ <source>Error with wallet rewrite: </source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../src/simplewallet/simplewallet.cpp" line="1289"/>
+ <source>priority must be 0, 1, 2, 3, or 4 </source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../src/simplewallet/simplewallet.cpp" line="1301"/>
+ <location filename="../src/simplewallet/simplewallet.cpp" line="1316"/>
+ <source>priority must be 0, 1, 2, 3, or 4</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../src/simplewallet/simplewallet.cpp" line="1404"/>
+ <source>invalid unit</source>
+ <translation>不正なユニット</translation>
+ </message>
+ <message>
+ <location filename="../src/simplewallet/simplewallet.cpp" line="1422"/>
+ <location filename="../src/simplewallet/simplewallet.cpp" line="1484"/>
+ <source>invalid count: must be an unsigned integer</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../src/simplewallet/simplewallet.cpp" line="1440"/>
+ <source>invalid value</source>
+ <translation>不正な金額</translation>
+ </message>
+ <message>
+ <location filename="../src/simplewallet/simplewallet.cpp" line="1942"/>
+ <source>usage: set_log &lt;log_level_number_0-4&gt; | &lt;categories&gt;</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../src/simplewallet/simplewallet.cpp" line="2013"/>
+ <source>(Y/Yes/N/No): </source>
+ <translation>(はい (Y)&#x3000;いいえ (N)): </translation>
+ </message>
+ <message>
+ <location filename="../src/simplewallet/simplewallet.cpp" line="2509"/>
+ <location filename="../src/simplewallet/simplewallet.cpp" line="2536"/>
+ <source>bad m_restore_height parameter: </source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../src/simplewallet/simplewallet.cpp" line="2514"/>
+ <source>date format must be YYYY-MM-DD</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../src/simplewallet/simplewallet.cpp" line="2527"/>
+ <source>Restore height is: </source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../src/simplewallet/simplewallet.cpp" line="2528"/>
+ <location filename="../src/simplewallet/simplewallet.cpp" line="3980"/>
+ <source>Is this okay? (Y/Yes/N/No): </source>
+ <translation>これは大丈夫ですか? (はい (Y)&#x3000;いいえ (N)): </translation>
+ </message>
+ <message>
+ <location filename="../src/simplewallet/simplewallet.cpp" line="2575"/>
+ <source>Daemon is local, assuming trusted</source>
+ <translation>デーモンはローカルです。信頼できるデーモン予期してます</translation>
+ </message>
+ <message>
+ <location filename="../src/simplewallet/simplewallet.cpp" line="3004"/>
+ <source>Password for new watch-only wallet</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../src/simplewallet/simplewallet.cpp" line="3063"/>
+ <source>invalid arguments. Please use start_mining [&lt;number_of_threads&gt;] [do_bg_mining] [ignore_battery], &lt;number_of_threads&gt; should be from 1 to </source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../src/simplewallet/simplewallet.cpp" line="3258"/>
+ <source>internal error: </source>
+ <translation>内部エラー: </translation>
+ </message>
+ <message>
+ <location filename="../src/simplewallet/simplewallet.cpp" line="1185"/>
+ <location filename="../src/simplewallet/simplewallet.cpp" line="3263"/>
+ <location filename="../src/simplewallet/simplewallet.cpp" line="3556"/>
+ <source>unexpected error: </source>
+ <translation>予期せぬエラー: </translation>
+ </message>
+ <message>
+ <location filename="../src/simplewallet/simplewallet.cpp" line="1119"/>
+ <location filename="../src/simplewallet/simplewallet.cpp" line="1190"/>
+ <location filename="../src/simplewallet/simplewallet.cpp" line="3268"/>
+ <location filename="../src/simplewallet/simplewallet.cpp" line="3561"/>
+ <location filename="../src/simplewallet/simplewallet.cpp" line="4030"/>
+ <location filename="../src/simplewallet/simplewallet.cpp" line="4138"/>
+ <location filename="../src/simplewallet/simplewallet.cpp" line="4371"/>
+ <location filename="../src/simplewallet/simplewallet.cpp" line="4570"/>
+ <location filename="../src/simplewallet/simplewallet.cpp" line="4865"/>
+ <source>unknown error</source>
+ <translation>不明なエラー</translation>
+ </message>
+ <message>
+ <location filename="../src/simplewallet/simplewallet.cpp" line="3273"/>
+ <source>refresh failed: </source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../src/simplewallet/simplewallet.cpp" line="3273"/>
+ <source>Blocks received: </source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../src/simplewallet/simplewallet.cpp" line="3304"/>
+ <source>unlocked balance: </source>
+ <translation>ロック解除された残高: </translation>
+ </message>
+ <message>
+ <location filename="../src/simplewallet/simplewallet.cpp" line="1925"/>
+ <location filename="../src/simplewallet/simplewallet.cpp" line="3400"/>
+ <location filename="../src/simplewallet/simplewallet.cpp" line="3451"/>
+ <source>amount</source>
+ <translation>金額</translation>
+ </message>
+ <message>
+ <location filename="../src/simplewallet/simplewallet.cpp" line="219"/>
+ <source>false</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../src/simplewallet/simplewallet.cpp" line="493"/>
+ <source>Unknown command: </source>
+ <translation>未知のコマンド: </translation>
+ </message>
+ <message>
+ <location filename="../src/simplewallet/simplewallet.cpp" line="500"/>
+ <source>Command usage: </source>
+ <translation>コマンドの使用: </translation>
+ </message>
+ <message>
+ <location filename="../src/simplewallet/simplewallet.cpp" line="503"/>
+ <source>Command description: </source>
+ <translation>コマンドの記述: </translation>
+ </message>
+ <message>
+ <location filename="../src/simplewallet/simplewallet.cpp" line="551"/>
+ <source>wallet is multisig but not yet finalized</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../src/simplewallet/simplewallet.cpp" line="567"/>
+ <source>Enter optional seed encryption passphrase, empty to see raw seed</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../src/simplewallet/simplewallet.cpp" line="584"/>
+ <source>Failed to retrieve seed</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../src/simplewallet/simplewallet.cpp" line="603"/>
+ <source>wallet is multisig and has no seed</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../src/simplewallet/simplewallet.cpp" line="674"/>
+ <source>Cannot connect to daemon</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../src/simplewallet/simplewallet.cpp" line="679"/>
+ <source>Current fee is %s monero per kB</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../src/simplewallet/simplewallet.cpp" line="695"/>
+ <source>Error: failed to estimate backlog array size: </source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../src/simplewallet/simplewallet.cpp" line="700"/>
+ <source>Error: bad estimated backlog array size</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../src/simplewallet/simplewallet.cpp" line="712"/>
+ <source> (current)</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../src/simplewallet/simplewallet.cpp" line="715"/>
+ <source>%u block (%u minutes) backlog at priority %u%s</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../src/simplewallet/simplewallet.cpp" line="717"/>
+ <source>%u to %u block (%u to %u minutes) backlog at priority %u</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../src/simplewallet/simplewallet.cpp" line="720"/>
+ <source>No backlog at priority </source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../src/simplewallet/simplewallet.cpp" line="729"/>
+ <location filename="../src/simplewallet/simplewallet.cpp" line="762"/>
+ <source>This wallet is already multisig</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../src/simplewallet/simplewallet.cpp" line="734"/>
+ <location filename="../src/simplewallet/simplewallet.cpp" line="767"/>
+ <source>wallet is watch-only and cannot be made multisig</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../src/simplewallet/simplewallet.cpp" line="740"/>
+ <location filename="../src/simplewallet/simplewallet.cpp" line="773"/>
+ <source>This wallet has been used before, please use a new wallet to create a multisig wallet</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../src/simplewallet/simplewallet.cpp" line="747"/>
+ <source>Your password is incorrect.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../src/simplewallet/simplewallet.cpp" line="753"/>
+ <source>Send this multisig info to all other participants, then use make_multisig &lt;threshold&gt; &lt;info1&gt; [&lt;info2&gt;...] with others&apos; multisig info</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../src/simplewallet/simplewallet.cpp" line="754"/>
+ <source>This includes the PRIVATE view key, so needs to be disclosed only to that multisig wallet&apos;s participants </source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../src/simplewallet/simplewallet.cpp" line="786"/>
+ <source>usage: make_multisig &lt;threshold&gt; &lt;multisiginfo1&gt; [&lt;multisiginfo2&gt;...]</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../src/simplewallet/simplewallet.cpp" line="794"/>
+ <source>Invalid threshold</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../src/simplewallet/simplewallet.cpp" line="807"/>
+ <source>Another step is needed</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../src/simplewallet/simplewallet.cpp" line="809"/>
+ <source>Send this multisig info to all other participants, then use finalize_multisig &lt;info1&gt; [&lt;info2&gt;...] with others&apos; multisig info</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../src/simplewallet/simplewallet.cpp" line="815"/>
+ <source>Error creating multisig: </source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../src/simplewallet/simplewallet.cpp" line="822"/>
+ <source>Error creating multisig: new wallet is not multisig</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../src/simplewallet/simplewallet.cpp" line="825"/>
+ <source> multisig address: </source>
+ <translation> マルチサインアドレス: </translation>
+ </message>
+ <message>
+ <location filename="../src/simplewallet/simplewallet.cpp" line="836"/>
+ <location filename="../src/simplewallet/simplewallet.cpp" line="880"/>
+ <location filename="../src/simplewallet/simplewallet.cpp" line="927"/>
+ <source>This wallet is not multisig</source>
+ <translation>これはマルチシッグウォレットではありません</translation>
+ </message>
+ <message>
+ <location filename="../src/simplewallet/simplewallet.cpp" line="841"/>
+ <source>This wallet is already finalized</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../src/simplewallet/simplewallet.cpp" line="854"/>
+ <source>usage: finalize_multisig &lt;multisiginfo1&gt; [&lt;multisiginfo2&gt;...]</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../src/simplewallet/simplewallet.cpp" line="862"/>
+ <source>Failed to finalize multisig</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../src/simplewallet/simplewallet.cpp" line="868"/>
+ <source>Failed to finalize multisig: </source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../src/simplewallet/simplewallet.cpp" line="885"/>
+ <location filename="../src/simplewallet/simplewallet.cpp" line="932"/>
+ <location filename="../src/simplewallet/simplewallet.cpp" line="1006"/>
+ <location filename="../src/simplewallet/simplewallet.cpp" line="1074"/>
+ <location filename="../src/simplewallet/simplewallet.cpp" line="1136"/>
+ <source>This multisig wallet is not yet finalized</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../src/simplewallet/simplewallet.cpp" line="890"/>
+ <source>usage: export_multisig_info &lt;filename&gt;</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../src/simplewallet/simplewallet.cpp" line="913"/>
+ <source>Error exporting multisig info: </source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../src/simplewallet/simplewallet.cpp" line="917"/>
+ <source>Multisig info exported to </source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../src/simplewallet/simplewallet.cpp" line="937"/>
+ <source>usage: import_multisig_info &lt;filename1&gt; [&lt;filename2&gt;...] - one for each other participant</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../src/simplewallet/simplewallet.cpp" line="965"/>
+ <source>Multisig info imported</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../src/simplewallet/simplewallet.cpp" line="969"/>
+ <source>Failed to import multisig info: </source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../src/simplewallet/simplewallet.cpp" line="980"/>
+ <source>Failed to update spent status after importing multisig info: </source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../src/simplewallet/simplewallet.cpp" line="985"/>
+ <source>Untrusted daemon, spent status may be incorrect. Use a trusted daemon and run &quot;rescan_spent&quot;</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../src/simplewallet/simplewallet.cpp" line="1001"/>
+ <location filename="../src/simplewallet/simplewallet.cpp" line="1069"/>
+ <location filename="../src/simplewallet/simplewallet.cpp" line="1131"/>
+ <source>This is not a multisig wallet</source>
+ <translation>これはマルチシッグウォレットではありません</translation>
+ </message>
+ <message>
+ <location filename="../src/simplewallet/simplewallet.cpp" line="1011"/>
+ <source>usage: sign_multisig &lt;filename&gt;</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../src/simplewallet/simplewallet.cpp" line="1024"/>
+ <source>Failed to sign multisig transaction</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../src/simplewallet/simplewallet.cpp" line="1030"/>
+ <source>Multisig error: </source>
+ <translation>マルチサインエラー: </translation>
+ </message>
+ <message>
+ <location filename="../src/simplewallet/simplewallet.cpp" line="1035"/>
+ <source>Failed to sign multisig transaction: </source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../src/simplewallet/simplewallet.cpp" line="1058"/>
+ <source>It may be relayed to the network with submit_multisig</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../src/simplewallet/simplewallet.cpp" line="1079"/>
+ <source>usage: submit_multisig &lt;filename&gt;</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../src/simplewallet/simplewallet.cpp" line="1094"/>
+ <location filename="../src/simplewallet/simplewallet.cpp" line="1155"/>
+ <source>Failed to load multisig transaction from file</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../src/simplewallet/simplewallet.cpp" line="1099"/>
+ <location filename="../src/simplewallet/simplewallet.cpp" line="1160"/>
+ <source>Multisig transaction signed by only %u signers, needs %u more signatures</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../src/simplewallet/simplewallet.cpp" line="1108"/>
+ <location filename="../src/simplewallet/simplewallet.cpp" line="6750"/>
+ <source>Transaction successfully submitted, transaction </source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../src/simplewallet/simplewallet.cpp" line="1109"/>
+ <location filename="../src/simplewallet/simplewallet.cpp" line="6751"/>
+ <source>You can check its status by using the `show_transfers` command.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../src/simplewallet/simplewallet.cpp" line="1141"/>
+ <source>usage: export_raw_multisig &lt;filename&gt;</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../src/simplewallet/simplewallet.cpp" line="1176"/>
+ <source>Failed to export multisig transaction to file </source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../src/simplewallet/simplewallet.cpp" line="1180"/>
+ <source>Saved exported multisig transaction file(s): </source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../src/simplewallet/simplewallet.cpp" line="1252"/>
+ <location filename="../src/simplewallet/simplewallet.cpp" line="1258"/>
+ <location filename="../src/simplewallet/simplewallet.cpp" line="1272"/>
+ <source>ring size must be an integer &gt;= </source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../src/simplewallet/simplewallet.cpp" line="1277"/>
+ <source>could not change default ring size</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../src/simplewallet/simplewallet.cpp" line="1518"/>
+ <source>Invalid height</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../src/simplewallet/simplewallet.cpp" line="1564"/>
+ <source>start_mining [&lt;number_of_threads&gt;] [bg_mining] [ignore_battery]</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../src/simplewallet/simplewallet.cpp" line="1565"/>
+ <source>Start mining in the daemon (bg_mining and ignore_battery are optional booleans).</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../src/simplewallet/simplewallet.cpp" line="1568"/>
+ <source>Stop mining in the daemon.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../src/simplewallet/simplewallet.cpp" line="1571"/>
+ <source>set_daemon &lt;host&gt;[:&lt;port&gt;]</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../src/simplewallet/simplewallet.cpp" line="1572"/>
+ <source>Set another daemon to connect to.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../src/simplewallet/simplewallet.cpp" line="1575"/>
+ <source>Save the current blockchain data.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../src/simplewallet/simplewallet.cpp" line="1578"/>
+ <source>Synchronize the transactions and balance.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../src/simplewallet/simplewallet.cpp" line="1581"/>
+ <source>balance [detail]</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../src/simplewallet/simplewallet.cpp" line="1582"/>
+ <source>Show the wallet&apos;s balance of the currently selected account.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../src/simplewallet/simplewallet.cpp" line="1585"/>
+ <source>incoming_transfers [available|unavailable] [verbose] [index=&lt;N1&gt;[,&lt;N2&gt;[,...]]]</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../src/simplewallet/simplewallet.cpp" line="1586"/>
+ <source>Show the incoming transfers, all or filtered by availability and address index.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../src/simplewallet/simplewallet.cpp" line="1589"/>
+ <source>payments &lt;PID_1&gt; [&lt;PID_2&gt; ... &lt;PID_N&gt;]</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../src/simplewallet/simplewallet.cpp" line="1590"/>
+ <source>Show the payments for the given payment IDs.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../src/simplewallet/simplewallet.cpp" line="1593"/>
+ <source>Show the blockchain height.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../src/simplewallet/simplewallet.cpp" line="1596"/>
+ <source>transfer_original [index=&lt;N1&gt;[,&lt;N2&gt;,...]] [&lt;priority&gt;] [&lt;ring_size&gt;] &lt;address&gt; &lt;amount&gt; [&lt;payment_id&gt;]</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../src/simplewallet/simplewallet.cpp" line="1597"/>
+ <source>Transfer &lt;amount&gt; to &lt;address&gt; using an older transaction building algorithm. If the parameter &quot;index=&lt;N1&gt;[,&lt;N2&gt;,...]&quot; is specified, the wallet uses outputs received by addresses of those indices. If omitted, the wallet randomly chooses address indices to be used. In any case, it tries its best not to combine outputs across multiple addresses. &lt;priority&gt; is the priority of the transaction. The higher the priority, the higher the transaction fee. Valid values in priority order (from lowest to highest) are: unimportant, normal, elevated, priority. If omitted, the default value (see the command &quot;set priority&quot;) is used. &lt;ring_size&gt; is the number of inputs to include for untraceability. Multiple payments can be made at once by adding &lt;address_2&gt; &lt;amount_2&gt; etcetera (before the payment ID, if it&apos;s included)</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../src/simplewallet/simplewallet.cpp" line="1599"/>
+ <source>transfer [index=&lt;N1&gt;[,&lt;N2&gt;,...]] [&lt;priority&gt;] [&lt;ring_size&gt;] &lt;address&gt; &lt;amount&gt; [&lt;payment_id&gt;]</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../src/simplewallet/simplewallet.cpp" line="1600"/>
+ <source>Transfer &lt;amount&gt; to &lt;address&gt;. If the parameter &quot;index=&lt;N1&gt;[,&lt;N2&gt;,...]&quot; is specified, the wallet uses outputs received by addresses of those indices. If omitted, the wallet randomly chooses address indices to be used. In any case, it tries its best not to combine outputs across multiple addresses. &lt;priority&gt; is the priority of the transaction. The higher the priority, the higher the transaction fee. Valid values in priority order (from lowest to highest) are: unimportant, normal, elevated, priority. If omitted, the default value (see the command &quot;set priority&quot;) is used. &lt;ring_size&gt; is the number of inputs to include for untraceability. Multiple payments can be made at once by adding &lt;address_2&gt; &lt;amount_2&gt; etcetera (before the payment ID, if it&apos;s included)</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../src/simplewallet/simplewallet.cpp" line="1603"/>
+ <source>locked_transfer [index=&lt;N1&gt;[,&lt;N2&gt;,...]] [&lt;priority&gt;] [&lt;ring_size&gt;] &lt;addr&gt; &lt;amount&gt; &lt;lockblocks&gt; [&lt;payment_id&gt;]</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../src/simplewallet/simplewallet.cpp" line="1604"/>
+ <source>Transfer &lt;amount&gt; to &lt;address&gt; and lock it for &lt;lockblocks&gt; (max. 1000000). If the parameter &quot;index=&lt;N1&gt;[,&lt;N2&gt;,...]&quot; is specified, the wallet uses outputs received by addresses of those indices. If omitted, the wallet randomly chooses address indices to be used. In any case, it tries its best not to combine outputs across multiple addresses. &lt;priority&gt; is the priority of the transaction. The higher the priority, the higher the transaction fee. Valid values in priority order (from lowest to highest) are: unimportant, normal, elevated, priority. If omitted, the default value (see the command &quot;set priority&quot;) is used. &lt;ring_size&gt; is the number of inputs to include for untraceability. Multiple payments can be made at once by adding &lt;address_2&gt; &lt;amount_2&gt; etcetera (before the payment ID, if it&apos;s included)</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../src/simplewallet/simplewallet.cpp" line="1607"/>
+ <source>Send all unmixable outputs to yourself with ring_size 1</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../src/simplewallet/simplewallet.cpp" line="1609"/>
+ <source>sweep_all [index=&lt;N1&gt;[,&lt;N2&gt;,...]] [&lt;priority&gt;] [&lt;ring_size&gt;] &lt;address&gt; [&lt;payment_id&gt;]</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../src/simplewallet/simplewallet.cpp" line="1610"/>
+ <source>Send all unlocked balance to an address. If the parameter &quot;index&lt;N1&gt;[,&lt;N2&gt;,...]&quot; is specified, the wallet sweeps outputs received by those address indices. If omitted, the wallet randomly chooses an address index to be used.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../src/simplewallet/simplewallet.cpp" line="1613"/>
+ <source>sweep_below &lt;amount_threshold&gt; [index=&lt;N1&gt;[,&lt;N2&gt;,...]] [&lt;priority&gt;] [&lt;ring_size&gt;] &lt;address&gt; [&lt;payment_id&gt;]</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../src/simplewallet/simplewallet.cpp" line="1614"/>
+ <source>Send all unlocked outputs below the threshold to an address.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../src/simplewallet/simplewallet.cpp" line="1617"/>
+ <source>sweep_single [&lt;priority&gt;] [&lt;ring_size&gt;] &lt;key_image&gt; &lt;address&gt; [&lt;payment_id&gt;]</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../src/simplewallet/simplewallet.cpp" line="1618"/>
+ <source>Send a single output of the given key image to an address without change.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../src/simplewallet/simplewallet.cpp" line="1621"/>
+ <source>donate [index=&lt;N1&gt;[,&lt;N2&gt;,...]] [&lt;priority&gt;] [&lt;ring_size&gt;] &lt;amount&gt; [&lt;payment_id&gt;]</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../src/simplewallet/simplewallet.cpp" line="1622"/>
+ <source>Donate &lt;amount&gt; to the development team (donate.getmonero.org).</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../src/simplewallet/simplewallet.cpp" line="1625"/>
+ <source>sign_transfer &lt;file&gt;</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../src/simplewallet/simplewallet.cpp" line="1626"/>
+ <source>Sign a transaction from a &lt;file&gt;.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../src/simplewallet/simplewallet.cpp" line="1629"/>
+ <source>Submit a signed transaction from a file.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../src/simplewallet/simplewallet.cpp" line="1632"/>
+ <source>set_log &lt;level&gt;|{+,-,}&lt;categories&gt;</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../src/simplewallet/simplewallet.cpp" line="1633"/>
+ <source>Change the current log detail (level must be &lt;0-4&gt;).</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../src/simplewallet/simplewallet.cpp" line="1636"/>
+ <source>account
+ account new &lt;label text with white spaces allowed&gt;
+ account switch &lt;index&gt;
+ account label &lt;index&gt; &lt;label text with white spaces allowed&gt;
+ account tag &lt;tag_name&gt; &lt;account_index_1&gt; [&lt;account_index_2&gt; ...]
+ account untag &lt;account_index_1&gt; [&lt;account_index_2&gt; ...]
+ account tag_description &lt;tag_name&gt; &lt;description&gt;</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../src/simplewallet/simplewallet.cpp" line="1643"/>
+ <source>If no arguments are specified, the wallet shows all the existing accounts along with their balances.
+If the &quot;new&quot; argument is specified, the wallet creates a new account with its label initialized by the provided label text (which can be empty).
+If the &quot;switch&quot; argument is specified, the wallet switches to the account specified by &lt;index&gt;.
+If the &quot;label&quot; argument is specified, the wallet sets the label of the account specified by &lt;index&gt; to the provided label text.
+If the &quot;tag&quot; argument is specified, a tag &lt;tag_name&gt; is assigned to the specified accounts &lt;account_index_1&gt;, &lt;account_index_2&gt;, ....
+If the &quot;untag&quot; argument is specified, the tags assigned to the specified accounts &lt;account_index_1&gt;, &lt;account_index_2&gt; ..., are removed.
+If the &quot;tag_description&quot; argument is specified, the tag &lt;tag_name&gt; is assigned an arbitrary text &lt;description&gt;.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../src/simplewallet/simplewallet.cpp" line="1652"/>
+ <source>address [ new &lt;label text with white spaces allowed&gt; | all | &lt;index_min&gt; [&lt;index_max&gt;] | label &lt;index&gt; &lt;label text with white spaces allowed&gt;]</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../src/simplewallet/simplewallet.cpp" line="1653"/>
+ <source>If no arguments are specified or &lt;index&gt; is specified, the wallet shows the default or specified address. If &quot;all&quot; is specified, the wallet shows all the existing addresses in the currently selected account. If &quot;new &quot; is specified, the wallet creates a new address with the provided label text (which can be empty). If &quot;label&quot; is specified, the wallet sets the label of the address specified by &lt;index&gt; to the provided label text.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../src/simplewallet/simplewallet.cpp" line="1656"/>
+ <source>integrated_address [&lt;payment_id&gt; | &lt;address&gt;]</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../src/simplewallet/simplewallet.cpp" line="1657"/>
+ <source>Encode a payment ID into an integrated address for the current wallet public address (no argument uses a random payment ID), or decode an integrated address to standard address and payment ID</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../src/simplewallet/simplewallet.cpp" line="1660"/>
+ <source>address_book [(add ((&lt;address&gt; [pid &lt;id&gt;])|&lt;integrated address&gt;) [&lt;description possibly with whitespaces&gt;])|(delete &lt;index&gt;)]</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../src/simplewallet/simplewallet.cpp" line="1661"/>
+ <source>Print all entries in the address book, optionally adding/deleting an entry to/from it.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../src/simplewallet/simplewallet.cpp" line="1664"/>
+ <source>Save the wallet data.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../src/simplewallet/simplewallet.cpp" line="1667"/>
+ <source>Save a watch-only keys file.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../src/simplewallet/simplewallet.cpp" line="1670"/>
+ <source>Display the private view key.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../src/simplewallet/simplewallet.cpp" line="1673"/>
+ <source>Display the private spend key.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../src/simplewallet/simplewallet.cpp" line="1676"/>
+ <source>Display the Electrum-style mnemonic seed</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../src/simplewallet/simplewallet.cpp" line="1679"/>
+ <source>set &lt;option&gt; [&lt;value&gt;]</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../src/simplewallet/simplewallet.cpp" line="1680"/>
+ <source>Available options:
+ seed language
+ Set the wallet&apos;s seed language.
+ always-confirm-transfers &lt;1|0&gt;
+ Whether to confirm unsplit txes.
+ print-ring-members &lt;1|0&gt;
+ Whether to print detailed information about ring members during confirmation.
+ store-tx-info &lt;1|0&gt;
+ Whether to store outgoing tx info (destination address, payment ID, tx secret key) for future reference.
+ default-ring-size &lt;n&gt;
+ Set the default ring size (default and minimum is 5).
+ auto-refresh &lt;1|0&gt;
+ Whether to automatically synchronize new blocks from the daemon.
+ refresh-type &lt;full|optimize-coinbase|no-coinbase|default&gt;
+ Set the wallet&apos;s refresh behaviour.
+ priority [0|1|2|3|4]
+ Set the fee to default/unimportant/normal/elevated/priority.
+ confirm-missing-payment-id &lt;1|0&gt;
+ ask-password &lt;1|0&gt;
+ unit &lt;monero|millinero|micronero|nanonero|piconero&gt;
+ Set the default monero (sub-)unit.
+ min-outputs-count [n]
+ Try to keep at least that many outputs of value at least min-outputs-value.
+ min-outputs-value [n]
+ Try to keep at least min-outputs-count outputs of at least that value.
+ merge-destinations &lt;1|0&gt;
+ Whether to merge multiple payments to the same destination address.
+ confirm-backlog &lt;1|0&gt;
+ Whether to warn if there is transaction backlog.
+ confirm-backlog-threshold [n]
+ Set a threshold for confirm-backlog to only warn if the transaction backlog is greater than n blocks.
+ refresh-from-block-height [n]
+ Set the height before which to ignore blocks.
+ auto-low-priority &lt;1|0&gt;
+ Whether to automatically use the low priority fee level when it&apos;s safe to do so.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../src/simplewallet/simplewallet.cpp" line="1717"/>
+ <source>Display the encrypted Electrum-style mnemonic seed.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../src/simplewallet/simplewallet.cpp" line="1720"/>
+ <source>Rescan the blockchain for spent outputs.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../src/simplewallet/simplewallet.cpp" line="1723"/>
+ <source>get_tx_key &lt;txid&gt;</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../src/simplewallet/simplewallet.cpp" line="1724"/>
+ <source>Get the transaction key (r) for a given &lt;txid&gt;.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../src/simplewallet/simplewallet.cpp" line="1727"/>
+ <source>check_tx_key &lt;txid&gt; &lt;txkey&gt; &lt;address&gt;</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../src/simplewallet/simplewallet.cpp" line="1728"/>
+ <source>Check the amount going to &lt;address&gt; in &lt;txid&gt;.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../src/simplewallet/simplewallet.cpp" line="1731"/>
+ <source>get_tx_proof &lt;txid&gt; &lt;address&gt; [&lt;message&gt;]</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../src/simplewallet/simplewallet.cpp" line="1732"/>
+ <source>Generate a signature proving funds sent to &lt;address&gt; in &lt;txid&gt;, optionally with a challenge string &lt;message&gt;, using either the transaction secret key (when &lt;address&gt; is not your wallet&apos;s address) or the view secret key (otherwise), which does not disclose the secret key.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../src/simplewallet/simplewallet.cpp" line="1735"/>
+ <source>check_tx_proof &lt;txid&gt; &lt;address&gt; &lt;signature_file&gt; [&lt;message&gt;]</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../src/simplewallet/simplewallet.cpp" line="1736"/>
+ <source>Check the proof for funds going to &lt;address&gt; in &lt;txid&gt; with the challenge string &lt;message&gt; if any.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../src/simplewallet/simplewallet.cpp" line="1739"/>
+ <source>get_spend_proof &lt;txid&gt; [&lt;message&gt;]</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../src/simplewallet/simplewallet.cpp" line="1740"/>
+ <source>Generate a signature proving that you generated &lt;txid&gt; using the spend secret key, optionally with a challenge string &lt;message&gt;.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../src/simplewallet/simplewallet.cpp" line="1743"/>
+ <source>check_spend_proof &lt;txid&gt; &lt;signature_file&gt; [&lt;message&gt;]</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../src/simplewallet/simplewallet.cpp" line="1744"/>
+ <source>Check a signature proving that the signer generated &lt;txid&gt;, optionally with a challenge string &lt;message&gt;.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../src/simplewallet/simplewallet.cpp" line="1747"/>
+ <source>get_reserve_proof (all|&lt;amount&gt;) [&lt;message&gt;]</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../src/simplewallet/simplewallet.cpp" line="1748"/>
+ <source>Generate a signature proving that you own at least this much, optionally with a challenge string &lt;message&gt;.
+If &apos;all&apos; is specified, you prove the entire sum of all of your existing accounts&apos; balances.
+Otherwise, you prove the reserve of the smallest possible amount above &lt;amount&gt; available in your current account.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../src/simplewallet/simplewallet.cpp" line="1753"/>
+ <source>check_reserve_proof &lt;address&gt; &lt;signature_file&gt; [&lt;message&gt;]</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../src/simplewallet/simplewallet.cpp" line="1754"/>
+ <source>Check a signature proving that the owner of &lt;address&gt; holds at least this much, optionally with a challenge string &lt;message&gt;.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../src/simplewallet/simplewallet.cpp" line="1757"/>
+ <source>show_transfers [in|out|pending|failed|pool] [index=&lt;N1&gt;[,&lt;N2&gt;,...]] [&lt;min_height&gt; [&lt;max_height&gt;]]</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../src/simplewallet/simplewallet.cpp" line="1758"/>
+ <source>Show the incoming/outgoing transfers within an optional height range.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../src/simplewallet/simplewallet.cpp" line="1761"/>
+ <source>unspent_outputs [index=&lt;N1&gt;[,&lt;N2&gt;,...]] [&lt;min_amount&gt; [&lt;max_amount&gt;]]</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../src/simplewallet/simplewallet.cpp" line="1762"/>
+ <source>Show the unspent outputs of a specified address within an optional amount range.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../src/simplewallet/simplewallet.cpp" line="1765"/>
+ <source>Rescan the blockchain from scratch.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../src/simplewallet/simplewallet.cpp" line="1768"/>
+ <source>set_tx_note &lt;txid&gt; [free text note]</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../src/simplewallet/simplewallet.cpp" line="1769"/>
+ <source>Set an arbitrary string note for a &lt;txid&gt;.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../src/simplewallet/simplewallet.cpp" line="1772"/>
+ <source>get_tx_note &lt;txid&gt;</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../src/simplewallet/simplewallet.cpp" line="1773"/>
+ <source>Get a string note for a txid.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../src/simplewallet/simplewallet.cpp" line="1776"/>
+ <source>set_description [free text note]</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../src/simplewallet/simplewallet.cpp" line="1777"/>
+ <source>Set an arbitrary description for the wallet.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../src/simplewallet/simplewallet.cpp" line="1780"/>
+ <source>Get the description of the wallet.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../src/simplewallet/simplewallet.cpp" line="1783"/>
+ <source>Show the wallet&apos;s status.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../src/simplewallet/simplewallet.cpp" line="1786"/>
+ <source>Show the wallet&apos;s information.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../src/simplewallet/simplewallet.cpp" line="1789"/>
+ <source>sign &lt;file&gt;</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../src/simplewallet/simplewallet.cpp" line="1790"/>
+ <source>Sign the contents of a file.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../src/simplewallet/simplewallet.cpp" line="1793"/>
+ <source>verify &lt;filename&gt; &lt;address&gt; &lt;signature&gt;</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../src/simplewallet/simplewallet.cpp" line="1794"/>
+ <source>Verify a signature on the contents of a file.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../src/simplewallet/simplewallet.cpp" line="1797"/>
+ <source>export_key_images &lt;file&gt;</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../src/simplewallet/simplewallet.cpp" line="1798"/>
+ <source>Export a signed set of key images to a &lt;file&gt;.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../src/simplewallet/simplewallet.cpp" line="1801"/>
+ <source>import_key_images &lt;file&gt;</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../src/simplewallet/simplewallet.cpp" line="1802"/>
+ <source>Import a signed key images list and verify their spent status.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../src/simplewallet/simplewallet.cpp" line="1805"/>
+ <source>export_outputs &lt;file&gt;</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../src/simplewallet/simplewallet.cpp" line="1806"/>
+ <source>Export a set of outputs owned by this wallet.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../src/simplewallet/simplewallet.cpp" line="1809"/>
+ <source>import_outputs &lt;file&gt;</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../src/simplewallet/simplewallet.cpp" line="1810"/>
+ <source>Import a set of outputs owned by this wallet.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../src/simplewallet/simplewallet.cpp" line="1813"/>
+ <source>show_transfer &lt;txid&gt;</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../src/simplewallet/simplewallet.cpp" line="1814"/>
+ <source>Show information about a transfer to/from this address.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../src/simplewallet/simplewallet.cpp" line="1817"/>
+ <source>Change the wallet&apos;s password.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../src/simplewallet/simplewallet.cpp" line="1820"/>
+ <source>Generate a new random full size payment id. These will be unencrypted on the blockchain, see integrated_address for encrypted short payment ids.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../src/simplewallet/simplewallet.cpp" line="1823"/>
+ <source>Print the information about the current fee and transaction backlog.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../src/simplewallet/simplewallet.cpp" line="1825"/>
+ <source>Export data needed to create a multisig wallet</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../src/simplewallet/simplewallet.cpp" line="1827"/>
+ <source>make_multisig &lt;threshold&gt; &lt;string1&gt; [&lt;string&gt;...]</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../src/simplewallet/simplewallet.cpp" line="1828"/>
+ <source>Turn this wallet into a multisig wallet</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../src/simplewallet/simplewallet.cpp" line="1831"/>
+ <source>finalize_multisig &lt;string&gt; [&lt;string&gt;...]</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../src/simplewallet/simplewallet.cpp" line="1832"/>
+ <source>Turn this wallet into a multisig wallet, extra step for N-1/N wallets</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../src/simplewallet/simplewallet.cpp" line="1835"/>
+ <source>export_multisig_info &lt;filename&gt;</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../src/simplewallet/simplewallet.cpp" line="1836"/>
+ <source>Export multisig info for other participants</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../src/simplewallet/simplewallet.cpp" line="1839"/>
+ <source>import_multisig_info &lt;filename&gt; [&lt;filename&gt;...]</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../src/simplewallet/simplewallet.cpp" line="1840"/>
+ <source>Import multisig info from other participants</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../src/simplewallet/simplewallet.cpp" line="1843"/>
+ <source>sign_multisig &lt;filename&gt;</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../src/simplewallet/simplewallet.cpp" line="1844"/>
+ <source>Sign a multisig transaction from a file</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../src/simplewallet/simplewallet.cpp" line="1847"/>
+ <source>submit_multisig &lt;filename&gt;</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../src/simplewallet/simplewallet.cpp" line="1848"/>
+ <source>Submit a signed multisig transaction from a file</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../src/simplewallet/simplewallet.cpp" line="1851"/>
+ <source>export_raw_multisig_tx &lt;filename&gt;</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../src/simplewallet/simplewallet.cpp" line="1852"/>
+ <source>Export a signed multisig transaction to a file</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../src/simplewallet/simplewallet.cpp" line="1855"/>
+ <source>help [&lt;command&gt;]</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../src/simplewallet/simplewallet.cpp" line="1856"/>
+ <source>Show the help section or the documentation about a &lt;command&gt;.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../src/simplewallet/simplewallet.cpp" line="1917"/>
+ <source>integer &gt;= </source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../src/simplewallet/simplewallet.cpp" line="1930"/>
+ <source>block height</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../src/simplewallet/simplewallet.cpp" line="2012"/>
+ <source>No wallet found with that name. Confirm creation of new wallet named: </source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../src/simplewallet/simplewallet.cpp" line="2068"/>
+ <source>can&apos;t specify more than one of --generate-new-wallet=&quot;wallet_name&quot;, --wallet-file=&quot;wallet_name&quot;, --generate-from-view-key=&quot;wallet_name&quot;, --generate-from-spend-key=&quot;wallet_name&quot;, --generate-from-keys=&quot;wallet_name&quot;, --generate-from-multisig-keys=&quot;wallet_name&quot; and --generate-from-json=&quot;jsonfilename&quot;</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../src/simplewallet/simplewallet.cpp" line="2084"/>
+ <source>can&apos;t specify both --restore-deterministic-wallet or --restore-multisig-wallet and --non-deterministic</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../src/simplewallet/simplewallet.cpp" line="2090"/>
+ <source>--restore-multisig-wallet uses --generate-new-wallet, not --wallet-file</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../src/simplewallet/simplewallet.cpp" line="2106"/>
+ <source>specify a recovery parameter with the --electrum-seed=&quot;multisig seed here&quot;</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../src/simplewallet/simplewallet.cpp" line="2133"/>
+ <source>Multisig seed failed verification</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../src/simplewallet/simplewallet.cpp" line="2149"/>
+ <source>Enter seed encryption passphrase, empty if none</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../src/simplewallet/simplewallet.cpp" line="2185"/>
+ <location filename="../src/simplewallet/simplewallet.cpp" line="2259"/>
+ <source>This address is a subaddress which cannot be used here.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../src/simplewallet/simplewallet.cpp" line="2337"/>
+ <source>Error: expected M/N, but got: </source>
+ <translation>エラー: N/Mを欲しかったでもこれを貰いました: </translation>
+ </message>
+ <message>
+ <location filename="../src/simplewallet/simplewallet.cpp" line="2342"/>
+ <source>Error: expected N &gt; 1 and N &lt;= M, but got: </source>
+ <translation>エラー: N > 1 と N <= M のこと欲しかったでもこれを貰いました: </translation>
+ </message>
+ <message>
+ <location filename="../src/simplewallet/simplewallet.cpp" line="2347"/>
+ <source>Error: M/N is currently unsupported. </source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../src/simplewallet/simplewallet.cpp" line="2350"/>
+ <source>Generating master wallet from %u of %u multisig wallet keys</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../src/simplewallet/simplewallet.cpp" line="2379"/>
+ <source>failed to parse secret view key</source>
+ <translation>秘密なビューキーの解析に失敗しました</translation>
+ </message>
+ <message>
+ <location filename="../src/simplewallet/simplewallet.cpp" line="2388"/>
+ <source>failed to verify secret view key</source>
+ <translation>秘密なビューキーの検証に失敗しました</translation>
+ </message>
+ <message>
+ <location filename="../src/simplewallet/simplewallet.cpp" line="2408"/>
+ <source>Secret spend key (%u of %u):</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../src/simplewallet/simplewallet.cpp" line="2432"/>
+ <source>Error: M/N is currently unsupported</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../src/simplewallet/simplewallet.cpp" line="2550"/>
+ <source>Restore height </source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../src/simplewallet/simplewallet.cpp" line="2551"/>
+ <source>Still apply restore height? (Y/Yes/N/No): </source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../src/simplewallet/simplewallet.cpp" line="2582"/>
+ <source>Warning: using an untrusted daemon at %s, privacy will be lessened</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../src/simplewallet/simplewallet.cpp" line="2636"/>
+ <source>Daemon either is not started or wrong port was passed. Please make sure daemon is running or change the daemon address using the &apos;set_daemon&apos; command.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../src/simplewallet/simplewallet.cpp" line="2768"/>
+ <source>Your wallet has been generated!
+To start synchronizing with the daemon, use the &quot;refresh&quot; command.
+Use the &quot;help&quot; command to see the list of available commands.
+Use &quot;help &lt;command&gt;&quot; to see a command&apos;s documentation.
+Always use the &quot;exit&quot; command when closing monero-wallet-cli to save
+your current session&apos;s state. Otherwise, you might need to synchronize
+your wallet again (your wallet keys are NOT at risk in any case).
+</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../src/simplewallet/simplewallet.cpp" line="2850"/>
+ <source>failed to generate new mutlisig wallet</source>
+ <translation>新しいマルチシッグウォレットの生成に失敗しました</translation>
+ </message>
+ <message>
+ <location filename="../src/simplewallet/simplewallet.cpp" line="2853"/>
+ <source>Generated new %u/%u multisig wallet: </source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../src/simplewallet/simplewallet.cpp" line="2889"/>
+ <source>Opened %u/%u multisig wallet%s</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../src/simplewallet/simplewallet.cpp" line="2942"/>
+ <source>Use &quot;help &lt;command&gt;&quot; to see a command&apos;s documentation.
+</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../src/simplewallet/simplewallet.cpp" line="3000"/>
+ <source>wallet is multisig and cannot save a watch-only version</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../src/simplewallet/simplewallet.cpp" line="3105"/>
+ <source>missing daemon URL argument</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../src/simplewallet/simplewallet.cpp" line="3116"/>
+ <source>Unexpected array length - Exited simple_wallet::set_daemon()</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../src/simplewallet/simplewallet.cpp" line="3130"/>
+ <source>This does not seem to be a valid daemon URL.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../src/simplewallet/simplewallet.cpp" line="3166"/>
+ <location filename="../src/simplewallet/simplewallet.cpp" line="3184"/>
+ <source>txid </source>
+ <translation>txid </translation>
+ </message>
+ <message>
+ <location filename="../src/simplewallet/simplewallet.cpp" line="3168"/>
+ <location filename="../src/simplewallet/simplewallet.cpp" line="3186"/>
+ <source>idx </source>
+ <translation>idx </translation>
+ </message>
+ <message>
+ <location filename="../src/simplewallet/simplewallet.cpp" line="3299"/>
+ <source> (Some owned outputs have partial key images - import_multisig_info needed)</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../src/simplewallet/simplewallet.cpp" line="3300"/>
+ <source>Currently selected account: [</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../src/simplewallet/simplewallet.cpp" line="3300"/>
+ <source>] </source>
+ <translation>] </translation>
+ </message>
+ <message>
+ <location filename="../src/simplewallet/simplewallet.cpp" line="3302"/>
+ <source>Tag: </source>
+ <translation>タグ: </translation>
+ </message>
+ <message>
+ <location filename="../src/simplewallet/simplewallet.cpp" line="3302"/>
+ <source>(No tag assigned)</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../src/simplewallet/simplewallet.cpp" line="3309"/>
+ <source>Balance per address:</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../src/simplewallet/simplewallet.cpp" line="3310"/>
+ <source>Address</source>
+ <translation>アドレス</translation>
+ </message>
+ <message>
+ <location filename="../src/simplewallet/simplewallet.cpp" line="3310"/>
+ <location filename="../src/simplewallet/simplewallet.cpp" line="5921"/>
+ <source>Balance</source>
+ <translation>残高</translation>
+ </message>
+ <message>
+ <location filename="../src/simplewallet/simplewallet.cpp" line="3310"/>
+ <location filename="../src/simplewallet/simplewallet.cpp" line="5921"/>
+ <source>Unlocked balance</source>
+ <translation>ロック解除された残高</translation>
+ </message>
+ <message>
+ <location filename="../src/simplewallet/simplewallet.cpp" line="3310"/>
+ <source>Outputs</source>
+ <translation>アウトプット</translation>
+ </message>
+ <message>
+ <location filename="../src/simplewallet/simplewallet.cpp" line="3310"/>
+ <location filename="../src/simplewallet/simplewallet.cpp" line="5921"/>
+ <source>Label</source>
+ <translation>ラベル</translation>
+ </message>
+ <message>
+ <location filename="../src/simplewallet/simplewallet.cpp" line="3318"/>
+ <source>%8u %6s %21s %21s %7u %21s</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../src/simplewallet/simplewallet.cpp" line="3327"/>
+ <source>usage: balance [detail]</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../src/simplewallet/simplewallet.cpp" line="3339"/>
+ <location filename="../src/simplewallet/simplewallet.cpp" line="3381"/>
+ <source>usage: incoming_transfers [available|unavailable] [verbose] [index=&lt;N&gt;]</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../src/simplewallet/simplewallet.cpp" line="3400"/>
+ <source>spent</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../src/simplewallet/simplewallet.cpp" line="3400"/>
+ <source>global index</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../src/simplewallet/simplewallet.cpp" line="3400"/>
+ <source>tx id</source>
+ <translation>tx id</translation>
+ </message>
+ <message>
+ <location filename="../src/simplewallet/simplewallet.cpp" line="3400"/>
+ <location filename="../src/simplewallet/simplewallet.cpp" line="3451"/>
+ <source>addr index</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../src/simplewallet/simplewallet.cpp" line="3423"/>
+ <source>No incoming transfers</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../src/simplewallet/simplewallet.cpp" line="3427"/>
+ <source>No incoming available transfers</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../src/simplewallet/simplewallet.cpp" line="3431"/>
+ <source>No incoming unavailable transfers</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../src/simplewallet/simplewallet.cpp" line="3442"/>
+ <source>expected at least one payment ID</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../src/simplewallet/simplewallet.cpp" line="3451"/>
+ <source>payment</source>
+ <translation>ペイメント</translation>
+ </message>
+ <message>
+ <location filename="../src/simplewallet/simplewallet.cpp" line="3451"/>
+ <source>transaction</source>
+ <translation>取引</translation>
+ </message>
+ <message>
+ <location filename="../src/simplewallet/simplewallet.cpp" line="3451"/>
+ <source>height</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../src/simplewallet/simplewallet.cpp" line="3451"/>
+ <source>unlock time</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../src/simplewallet/simplewallet.cpp" line="3463"/>
+ <source>No payments with id </source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../src/simplewallet/simplewallet.cpp" line="3516"/>
+ <location filename="../src/simplewallet/simplewallet.cpp" line="3582"/>
+ <location filename="../src/simplewallet/simplewallet.cpp" line="3853"/>
+ <source>failed to get blockchain height: </source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../src/simplewallet/simplewallet.cpp" line="3572"/>
+ <location filename="../src/simplewallet/simplewallet.cpp" line="5136"/>
+ <location filename="../src/simplewallet/simplewallet.cpp" line="5174"/>
+ <location filename="../src/simplewallet/simplewallet.cpp" line="5226"/>
+ <location filename="../src/simplewallet/simplewallet.cpp" line="5259"/>
+ <source>failed to connect to the daemon</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../src/simplewallet/simplewallet.cpp" line="3590"/>
+ <source>
+Transaction %llu/%llu: txid=%s</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../src/simplewallet/simplewallet.cpp" line="3600"/>
+ <source>
+Input %llu/%llu: amount=%s</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../src/simplewallet/simplewallet.cpp" line="3616"/>
+ <source>failed to get output: </source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../src/simplewallet/simplewallet.cpp" line="3624"/>
+ <source>output key&apos;s originating block height shouldn&apos;t be higher than the blockchain height</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../src/simplewallet/simplewallet.cpp" line="3628"/>
+ <source>
+Originating block heights: </source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../src/simplewallet/simplewallet.cpp" line="3643"/>
+ <source>
+|</source>
+ <translation>
+|</translation>
+ </message>
+ <message>
+ <location filename="../src/simplewallet/simplewallet.cpp" line="3643"/>
+ <location filename="../src/simplewallet/simplewallet.cpp" line="5651"/>
+ <source>|
+</source>
+ <translation>|
+</translation>
+ </message>
+ <message>
+ <location filename="../src/simplewallet/simplewallet.cpp" line="3660"/>
+ <source>
+Warning: Some input keys being spent are from </source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../src/simplewallet/simplewallet.cpp" line="3662"/>
+ <source>, which can break the anonymity of ring signature. Make sure this is intentional!</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../src/simplewallet/simplewallet.cpp" line="3705"/>
+ <location filename="../src/simplewallet/simplewallet.cpp" line="4184"/>
+ <source>Ring size must not be 0</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../src/simplewallet/simplewallet.cpp" line="3717"/>
+ <location filename="../src/simplewallet/simplewallet.cpp" line="4196"/>
+ <source>ring size %u is too small, minimum is %u</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../src/simplewallet/simplewallet.cpp" line="3724"/>
+ <source>wrong number of arguments</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../src/simplewallet/simplewallet.cpp" line="3830"/>
+ <location filename="../src/simplewallet/simplewallet.cpp" line="4266"/>
+ <location filename="../src/simplewallet/simplewallet.cpp" line="4479"/>
+ <source>No payment id is included with this transaction. Is this okay? (Y/Yes/N/No): </source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../src/simplewallet/simplewallet.cpp" line="3872"/>
+ <location filename="../src/simplewallet/simplewallet.cpp" line="4286"/>
+ <source>No outputs found, or daemon is not ready</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../src/simplewallet/simplewallet.cpp" line="6743"/>
+ <source>Transaction successfully saved to </source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../src/simplewallet/simplewallet.cpp" line="6743"/>
+ <location filename="../src/simplewallet/simplewallet.cpp" line="6745"/>
+ <source>, txid </source>
+ <translation>、txid </translation>
+ </message>
+ <message>
+ <location filename="../src/simplewallet/simplewallet.cpp" line="6745"/>
+ <source>Failed to save transaction to </source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../src/simplewallet/simplewallet.cpp" line="4081"/>
+ <location filename="../src/simplewallet/simplewallet.cpp" line="4314"/>
+ <source>Sweeping %s in %llu transactions for a total fee of %s. Is this okay? (Y/Yes/N/No): </source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../src/simplewallet/simplewallet.cpp" line="4087"/>
+ <location filename="../src/simplewallet/simplewallet.cpp" line="4320"/>
+ <location filename="../src/simplewallet/simplewallet.cpp" line="4519"/>
+ <source>Sweeping %s for a total fee of %s. Is this okay? (Y/Yes/N/No): </source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../src/simplewallet/simplewallet.cpp" line="4630"/>
+ <source>Donating </source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../src/simplewallet/simplewallet.cpp" line="4792"/>
+ <source>This is a watch only wallet</source>
+ <translation>これは閲覧専用ウォレットです</translation>
+ </message>
+ <message>
+ <location filename="../src/simplewallet/simplewallet.cpp" line="6571"/>
+ <source>usage: show_transfer &lt;txid&gt;</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../src/simplewallet/simplewallet.cpp" line="6673"/>
+ <source>Double spend seen on the network: this transaction may or may not end up being mined</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../src/simplewallet/simplewallet.cpp" line="6708"/>
+ <source>Transaction ID not found</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../src/simplewallet/simplewallet.cpp" line="214"/>
+ <source>true</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../src/simplewallet/simplewallet.cpp" line="267"/>
+ <source>failed to parse refresh type</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../src/simplewallet/simplewallet.cpp" line="541"/>
+ <location filename="../src/simplewallet/simplewallet.cpp" line="608"/>
+ <source>wallet is watch-only and has no seed</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../src/simplewallet/simplewallet.cpp" line="557"/>
+ <location filename="../src/simplewallet/simplewallet.cpp" line="613"/>
+ <source>wallet is non-deterministic and has no seed</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../src/simplewallet/simplewallet.cpp" line="1226"/>
+ <location filename="../src/simplewallet/simplewallet.cpp" line="1245"/>
+ <source>wallet is watch-only and cannot transfer</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../src/simplewallet/simplewallet.cpp" line="1321"/>
+ <source>could not change default priority</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../src/simplewallet/simplewallet.cpp" line="1919"/>
+ <source>full (slowest, no assumptions); optimize-coinbase (fast, assumes the whole coinbase is paid to a single address); no-coinbase (fastest, assumes we receive no coinbase transaction), default (same as optimize-coinbase)</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../src/simplewallet/simplewallet.cpp" line="1923"/>
+ <source>monero, millinero, micronero, nanonero, piconero</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../src/simplewallet/simplewallet.cpp" line="1975"/>
+ <source>Wallet name not valid. Please try again or use Ctrl-C to quit.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../src/simplewallet/simplewallet.cpp" line="1992"/>
+ <source>Wallet and key files found, loading...</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../src/simplewallet/simplewallet.cpp" line="1998"/>
+ <source>Key file found but not wallet file. Regenerating...</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../src/simplewallet/simplewallet.cpp" line="2004"/>
+ <source>Key file not found. Failed to open wallet: </source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../src/simplewallet/simplewallet.cpp" line="2023"/>
+ <source>Generating new wallet...</source>
+ <translation>新しいウォレットを生じてます...</translation>
+ </message>
+ <message>
+ <location filename="../src/simplewallet/simplewallet.cpp" line="2141"/>
+ <source>Electrum-style word list failed verification</source>
+ <translation>Electrumな単語表の検証に失敗しました</translation>
+ </message>
+ <message>
+ <location filename="../src/simplewallet/simplewallet.cpp" line="2174"/>
+ <location filename="../src/simplewallet/simplewallet.cpp" line="2194"/>
+ <location filename="../src/simplewallet/simplewallet.cpp" line="2229"/>
+ <location filename="../src/simplewallet/simplewallet.cpp" line="2248"/>
+ <location filename="../src/simplewallet/simplewallet.cpp" line="2268"/>
+ <location filename="../src/simplewallet/simplewallet.cpp" line="2284"/>
+ <location filename="../src/simplewallet/simplewallet.cpp" line="2332"/>
+ <location filename="../src/simplewallet/simplewallet.cpp" line="2357"/>
+ <location filename="../src/simplewallet/simplewallet.cpp" line="2373"/>
+ <location filename="../src/simplewallet/simplewallet.cpp" line="2413"/>
+ <source>No data supplied, cancelled</source>
+ <translation>データをもらいませんでしたのでキャンセルしました</translation>
+ </message>
+ <message>
+ <location filename="../src/simplewallet/simplewallet.cpp" line="2180"/>
+ <location filename="../src/simplewallet/simplewallet.cpp" line="2254"/>
+ <location filename="../src/simplewallet/simplewallet.cpp" line="2363"/>
+ <location filename="../src/simplewallet/simplewallet.cpp" line="3791"/>
+ <location filename="../src/simplewallet/simplewallet.cpp" line="4240"/>
+ <location filename="../src/simplewallet/simplewallet.cpp" line="4454"/>
+ <location filename="../src/simplewallet/simplewallet.cpp" line="4926"/>
+ <location filename="../src/simplewallet/simplewallet.cpp" line="4994"/>
+ <location filename="../src/simplewallet/simplewallet.cpp" line="5058"/>
+ <location filename="../src/simplewallet/simplewallet.cpp" line="5266"/>
+ <location filename="../src/simplewallet/simplewallet.cpp" line="6106"/>
+ <location filename="../src/simplewallet/simplewallet.cpp" line="6353"/>
+ <source>failed to parse address</source>
+ <translation>アドレスの解析に失敗しました</translation>
+ </message>
+ <message>
+ <location filename="../src/simplewallet/simplewallet.cpp" line="2200"/>
+ <location filename="../src/simplewallet/simplewallet.cpp" line="2290"/>
+ <source>failed to parse view key secret key</source>
+ <translation>秘密なビューキーの解析に失敗しました</translation>
+ </message>
+ <message>
+ <location filename="../src/simplewallet/simplewallet.cpp" line="2210"/>
+ <location filename="../src/simplewallet/simplewallet.cpp" line="2308"/>
+ <source>failed to verify view key secret key</source>
+ <translation>秘密なビューキーの検証に失敗しました</translation>
+ </message>
+ <message>
+ <location filename="../src/simplewallet/simplewallet.cpp" line="2214"/>
+ <location filename="../src/simplewallet/simplewallet.cpp" line="2312"/>
+ <location filename="../src/simplewallet/simplewallet.cpp" line="2393"/>
+ <source>view key does not match standard address</source>
+ <translation>ビューキーが一般的なアドレスと一致しませんでした</translation>
+ </message>
+ <message>
+ <location filename="../src/simplewallet/simplewallet.cpp" line="2219"/>
+ <location filename="../src/simplewallet/simplewallet.cpp" line="2238"/>
+ <location filename="../src/simplewallet/simplewallet.cpp" line="2316"/>
+ <location filename="../src/simplewallet/simplewallet.cpp" line="2450"/>
+ <location filename="../src/simplewallet/simplewallet.cpp" line="2480"/>
+ <source>account creation failed</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../src/simplewallet/simplewallet.cpp" line="2234"/>
+ <location filename="../src/simplewallet/simplewallet.cpp" line="2274"/>
+ <location filename="../src/simplewallet/simplewallet.cpp" line="2418"/>
+ <source>failed to parse spend key secret key</source>
+ <translation>秘密なスペンドキーの解析に失敗しました</translation>
+ </message>
+ <message>
+ <location filename="../src/simplewallet/simplewallet.cpp" line="2300"/>
+ <location filename="../src/simplewallet/simplewallet.cpp" line="2439"/>
+ <source>failed to verify spend key secret key</source>
+ <translation>秘密なスペンドキーの検証に失敗しました</translation>
+ </message>
+ <message>
+ <location filename="../src/simplewallet/simplewallet.cpp" line="2304"/>
+ <location filename="../src/simplewallet/simplewallet.cpp" line="2444"/>
+ <source>spend key does not match standard address</source>
+ <translation>スペンドキーが一般的なアドレスと一致しませんでした</translation>
+ </message>
+ <message>
+ <location filename="../src/simplewallet/simplewallet.cpp" line="2562"/>
+ <source>failed to open account</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../src/simplewallet/simplewallet.cpp" line="2566"/>
+ <location filename="../src/simplewallet/simplewallet.cpp" line="3030"/>
+ <location filename="../src/simplewallet/simplewallet.cpp" line="3085"/>
+ <location filename="../src/simplewallet/simplewallet.cpp" line="3142"/>
+ <location filename="../src/simplewallet/simplewallet.cpp" line="4962"/>
+ <source>wallet is null</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../src/simplewallet/simplewallet.cpp" line="2680"/>
+ <location filename="../src/simplewallet/simplewallet.cpp" line="2685"/>
+ <source>invalid language choice entered. Please try again.
+</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../src/simplewallet/simplewallet.cpp" line="2753"/>
+ <source>View key: </source>
+ <translation>ビューキー: </translation>
+ </message>
+ <message>
+ <location filename="../src/simplewallet/simplewallet.cpp" line="2935"/>
+ <source>You may want to remove the file &quot;%s&quot; and try again</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../src/simplewallet/simplewallet.cpp" line="2963"/>
+ <source>failed to deinitialize wallet</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../src/simplewallet/simplewallet.cpp" line="3021"/>
+ <location filename="../src/simplewallet/simplewallet.cpp" line="3524"/>
+ <location filename="../src/simplewallet/simplewallet.cpp" line="6410"/>
+ <source>this command requires a trusted daemon. Enable with --trusted-daemon</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../src/simplewallet/simplewallet.cpp" line="3152"/>
+ <source>blockchain can&apos;t be saved: </source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../src/simplewallet/simplewallet.cpp" line="3239"/>
+ <location filename="../src/simplewallet/simplewallet.cpp" line="3538"/>
+ <source>daemon is busy. Please try again later.</source>
+ <translation>デーモンは忙しいです。後でもう一度試してください。</translation>
+ </message>
+ <message>
+ <location filename="../src/simplewallet/simplewallet.cpp" line="3243"/>
+ <location filename="../src/simplewallet/simplewallet.cpp" line="3542"/>
+ <source>no connection to daemon. Please make sure daemon is running.</source>
+ <translation>デーモンの接続が確立ありません。デーモンが実行中になっていることを確認してください。</translation>
+ </message>
+ <message>
+ <location filename="../src/simplewallet/simplewallet.cpp" line="3253"/>
+ <source>refresh error: </source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../src/simplewallet/simplewallet.cpp" line="3303"/>
+ <source>Balance: </source>
+ <translation>残高: </translation>
+ </message>
+ <message>
+ <location filename="../src/simplewallet/simplewallet.cpp" line="3399"/>
+ <source>pubkey</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../src/simplewallet/simplewallet.cpp" line="3399"/>
+ <source>key image</source>
+ <translation>キーイメージ</translation>
+ </message>
+ <message>
+ <location filename="../src/simplewallet/simplewallet.cpp" line="3400"/>
+ <location filename="../src/simplewallet/simplewallet.cpp" line="3410"/>
+ <source>unlocked</source>
+ <translation></translation>
+ </message>
+ <message>
+ <location filename="../src/simplewallet/simplewallet.cpp" line="3400"/>
+ <source>ringct</source>
+ <translation>ringct</translation>
+ </message>
+ <message>
+ <location filename="../src/simplewallet/simplewallet.cpp" line="3409"/>
+ <source>T</source>
+ <translation>T</translation>
+ </message>
+ <message>
+ <location filename="../src/simplewallet/simplewallet.cpp" line="3409"/>
+ <source>F</source>
+ <translation>F</translation>
+ </message>
+ <message>
+ <location filename="../src/simplewallet/simplewallet.cpp" line="3410"/>
+ <source>locked</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../src/simplewallet/simplewallet.cpp" line="3411"/>
+ <source>RingCT</source>
+ <translation>RingCT</translation>
+ </message>
+ <message>
+ <location filename="../src/simplewallet/simplewallet.cpp" line="3411"/>
+ <source>-</source>
+ <translation>-</translation>
+ </message>
+ <message>
+ <location filename="../src/simplewallet/simplewallet.cpp" line="3485"/>
+ <source>payment ID has invalid format, expected 16 or 64 character hex string: </source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../src/simplewallet/simplewallet.cpp" line="3546"/>
+ <source>failed to get spent status</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../src/simplewallet/simplewallet.cpp" line="3661"/>
+ <source>the same transaction</source>
+ <translation>同じ取引</translation>
+ </message>
+ <message>
+ <location filename="../src/simplewallet/simplewallet.cpp" line="3661"/>
+ <source>blocks that are temporally very close</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../src/simplewallet/simplewallet.cpp" line="3778"/>
+ <source>Locked blocks too high, max 1000000 (˜4 yrs)</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../src/simplewallet/simplewallet.cpp" line="5077"/>
+ <location filename="../src/simplewallet/simplewallet.cpp" line="5188"/>
+ <source>Good signature</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../src/simplewallet/simplewallet.cpp" line="5104"/>
+ <location filename="../src/simplewallet/simplewallet.cpp" line="5190"/>
+ <location filename="../src/simplewallet/simplewallet.cpp" line="5293"/>
+ <source>Bad signature</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../src/simplewallet/simplewallet.cpp" line="6046"/>
+ <source>usage: integrated_address [payment ID]</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../src/simplewallet/simplewallet.cpp" line="6082"/>
+ <source>Standard address: </source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../src/simplewallet/simplewallet.cpp" line="6087"/>
+ <source>failed to parse payment ID or address</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../src/simplewallet/simplewallet.cpp" line="6098"/>
+ <source>usage: address_book [(add (&lt;address&gt; [pid &lt;long or short payment id&gt;])|&lt;integrated address&gt; [&lt;description possibly with whitespaces&gt;])|(delete &lt;index&gt;)]</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../src/simplewallet/simplewallet.cpp" line="6128"/>
+ <source>failed to parse payment ID</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../src/simplewallet/simplewallet.cpp" line="6146"/>
+ <source>failed to parse index</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../src/simplewallet/simplewallet.cpp" line="6154"/>
+ <source>Address book is empty.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../src/simplewallet/simplewallet.cpp" line="6160"/>
+ <source>Index: </source>
+ <translation>インデックス: </translation>
+ </message>
+ <message>
+ <location filename="../src/simplewallet/simplewallet.cpp" line="6161"/>
+ <location filename="../src/simplewallet/simplewallet.cpp" line="6287"/>
+ <source>Address: </source>
+ <translation>アドレス: </translation>
+ </message>
+ <message>
+ <location filename="../src/simplewallet/simplewallet.cpp" line="6162"/>
+ <source>Payment ID: </source>
+ <translation>ペイメントID: </translation>
+ </message>
+ <message>
+ <location filename="../src/simplewallet/simplewallet.cpp" line="6163"/>
+ <location filename="../src/simplewallet/simplewallet.cpp" line="6286"/>
+ <source>Description: </source>
+ <translation>記述: </translation>
+ </message>
+ <message>
+ <location filename="../src/simplewallet/simplewallet.cpp" line="6173"/>
+ <source>usage: set_tx_note [txid] free text note</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../src/simplewallet/simplewallet.cpp" line="6201"/>
+ <source>usage: get_tx_note [txid]</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../src/simplewallet/simplewallet.cpp" line="6304"/>
+ <source>usage: sign &lt;filename&gt;</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../src/simplewallet/simplewallet.cpp" line="6309"/>
+ <source>wallet is watch-only and cannot sign</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../src/simplewallet/simplewallet.cpp" line="951"/>
+ <location filename="../src/simplewallet/simplewallet.cpp" line="6323"/>
+ <location filename="../src/simplewallet/simplewallet.cpp" line="6346"/>
+ <location filename="../src/simplewallet/simplewallet.cpp" line="6501"/>
+ <source>failed to read file </source>
+ <translation>ファイルの読み込みに失敗しました </translation>
+ </message>
+ <message>
+ <location filename="../src/simplewallet/simplewallet.cpp" line="5039"/>
+ <source>usage: check_tx_proof &lt;txid&gt; &lt;address&gt; &lt;signature_file&gt; [&lt;message&gt;]</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../src/simplewallet/simplewallet.cpp" line="5066"/>
+ <location filename="../src/simplewallet/simplewallet.cpp" line="5181"/>
+ <location filename="../src/simplewallet/simplewallet.cpp" line="5278"/>
+ <source>failed to load signature file</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../src/simplewallet/simplewallet.cpp" line="5117"/>
+ <source>usage: get_spend_proof &lt;txid&gt; [&lt;message&gt;]</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../src/simplewallet/simplewallet.cpp" line="5123"/>
+ <source>wallet is watch-only and cannot generate the proof</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../src/simplewallet/simplewallet.cpp" line="5161"/>
+ <source>usage: check_spend_proof &lt;txid&gt; &lt;signature_file&gt; [&lt;message&gt;]</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../src/simplewallet/simplewallet.cpp" line="5202"/>
+ <source>usage: get_reserve_proof (all|&lt;amount&gt;) [&lt;message&gt;]</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../src/simplewallet/simplewallet.cpp" line="5208"/>
+ <source>The reserve proof can be generated only by a full wallet</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../src/simplewallet/simplewallet.cpp" line="5253"/>
+ <source>usage: check_reserve_proof &lt;address&gt; &lt;signature_file&gt; [&lt;message&gt;]</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../src/simplewallet/simplewallet.cpp" line="5271"/>
+ <source>Address must not be a subaddress</source>
+ <translation>アドレスはサブアドレスであってはならないです</translation>
+ </message>
+ <message>
+ <location filename="../src/simplewallet/simplewallet.cpp" line="5289"/>
+ <source>Good signature -- total: %s, spent: %s, unspent: %s</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../src/simplewallet/simplewallet.cpp" line="5353"/>
+ <source>usage: show_transfers [in|out|all|pending|failed] [index=&lt;N1&gt;[,&lt;N2&gt;,...]] [&lt;min_height&gt; [&lt;max_height&gt;]]</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../src/simplewallet/simplewallet.cpp" line="5490"/>
+ <source>[Double spend seen on the network: this transaction may or may not end up being mined] </source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../src/simplewallet/simplewallet.cpp" line="5526"/>
+ <source>usage: unspent_outputs [index=&lt;N1&gt;[,&lt;N2&gt;,...]] [&lt;min_amount&gt; [&lt;max_amount&gt;]]</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../src/simplewallet/simplewallet.cpp" line="5586"/>
+ <source>There is no unspent output in the specified address</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../src/simplewallet/simplewallet.cpp" line="5699"/>
+ <source> (no daemon)</source>
+ <translation> (デーモンありません)</translation>
+ </message>
+ <message>
+ <location filename="../src/simplewallet/simplewallet.cpp" line="5701"/>
+ <source> (out of sync)</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../src/simplewallet/simplewallet.cpp" line="5758"/>
+ <source>(Untitled account)</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../src/simplewallet/simplewallet.cpp" line="5771"/>
+ <location filename="../src/simplewallet/simplewallet.cpp" line="5789"/>
+ <location filename="../src/simplewallet/simplewallet.cpp" line="5814"/>
+ <location filename="../src/simplewallet/simplewallet.cpp" line="5837"/>
+ <location filename="../src/simplewallet/simplewallet.cpp" line="5990"/>
+ <location filename="../src/simplewallet/simplewallet.cpp" line="6013"/>
+ <source>failed to parse index: </source>
+ <translation>インデックスの解析に失敗しました: </translation>
+ </message>
+ <message>
+ <location filename="../src/simplewallet/simplewallet.cpp" line="5776"/>
+ <location filename="../src/simplewallet/simplewallet.cpp" line="5995"/>
+ <source>specify an index between 0 and </source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../src/simplewallet/simplewallet.cpp" line="5873"/>
+ <source>usage:
+ account
+ account new &lt;label text with white spaces allowed&gt;
+ account switch &lt;index&gt;
+ account label &lt;index&gt; &lt;label text with white spaces allowed&gt;
+ account tag &lt;tag_name&gt; &lt;account_index_1&gt; [&lt;account_index_2&gt; ...]
+ account untag &lt;account_index_1&gt; [&lt;account_index_2&gt; ...]
+ account tag_description &lt;tag_name&gt; &lt;description&gt;</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../src/simplewallet/simplewallet.cpp" line="5901"/>
+ <source>
+Grand total:
+ Balance: </source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../src/simplewallet/simplewallet.cpp" line="5901"/>
+ <source>, unlocked balance: </source>
+ <translation>、ロック解除された残高: </translation>
+ </message>
+ <message>
+ <location filename="../src/simplewallet/simplewallet.cpp" line="5909"/>
+ <source>Untagged accounts:</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../src/simplewallet/simplewallet.cpp" line="5915"/>
+ <source>Tag %s is unregistered.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../src/simplewallet/simplewallet.cpp" line="5918"/>
+ <source>Accounts with tag: </source>
+ <translation>タグを持ってるアカウント: </translation>
+ </message>
+ <message>
+ <location filename="../src/simplewallet/simplewallet.cpp" line="5919"/>
+ <source>Tag&apos;s description: </source>
+ <translation>タグの記述: </translation>
+ </message>
+ <message>
+ <location filename="../src/simplewallet/simplewallet.cpp" line="5921"/>
+ <source>Account</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../src/simplewallet/simplewallet.cpp" line="5927"/>
+ <source> %c%8u %6s %21s %21s %21s</source>
+ <translation> %c%8u %6s %21s %21s %21s</translation>
+ </message>
+ <message>
+ <location filename="../src/simplewallet/simplewallet.cpp" line="5937"/>
+ <source>----------------------------------------------------------------------------------</source>
+ <translation>----------------------------------------------------------------------------------</translation>
+ </message>
+ <message>
+ <location filename="../src/simplewallet/simplewallet.cpp" line="5938"/>
+ <source>%15s %21s %21s</source>
+ <translation>%15s %21s %21s</translation>
+ </message>
+ <message>
+ <location filename="../src/simplewallet/simplewallet.cpp" line="5961"/>
+ <source>Primary address</source>
+ <translation>プライマリアドレス</translation>
+ </message>
+ <message>
+ <location filename="../src/simplewallet/simplewallet.cpp" line="5961"/>
+ <source>(used)</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../src/simplewallet/simplewallet.cpp" line="5982"/>
+ <source>(Untitled address)</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../src/simplewallet/simplewallet.cpp" line="6022"/>
+ <source>&lt;index_min&gt; is already out of bound</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../src/simplewallet/simplewallet.cpp" line="6027"/>
+ <source>&lt;index_max&gt; exceeds the bound</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../src/simplewallet/simplewallet.cpp" line="6035"/>
+ <source>usage: address [ new &lt;label text with white spaces allowed&gt; | all | &lt;index_min&gt; [&lt;index_max&gt;] | label &lt;index&gt; &lt;label text with white spaces allowed&gt; ]</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../src/simplewallet/simplewallet.cpp" line="6053"/>
+ <location filename="../src/simplewallet/simplewallet.cpp" line="6065"/>
+ <source>Integrated addresses can only be created for account 0</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../src/simplewallet/simplewallet.cpp" line="6077"/>
+ <source>Integrated address: %s, payment ID: %s</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../src/simplewallet/simplewallet.cpp" line="6082"/>
+ <source>Subaddress: </source>
+ <translation>サブアドレス: </translation>
+ </message>
+ <message>
+ <location filename="../src/simplewallet/simplewallet.cpp" line="6242"/>
+ <source>usage: get_description</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../src/simplewallet/simplewallet.cpp" line="6248"/>
+ <source>no description found</source>
+ <translation>記述を見つかれませんでした</translation>
+ </message>
+ <message>
+ <location filename="../src/simplewallet/simplewallet.cpp" line="6250"/>
+ <source>description found: </source>
+ <translation>記述を見つかれました: </translation>
+ </message>
+ <message>
+ <location filename="../src/simplewallet/simplewallet.cpp" line="6285"/>
+ <source>Filename: </source>
+ <translation>ファイル名: </translation>
+ </message>
+ <message>
+ <location filename="../src/simplewallet/simplewallet.cpp" line="6290"/>
+ <source>Watch only</source>
+ <translation>閲覧専用</translation>
+ </message>
+ <message>
+ <location filename="../src/simplewallet/simplewallet.cpp" line="6292"/>
+ <source>%u/%u multisig%s</source>
+ <translation>%u/%u マルチサイン%s</translation>
+ </message>
+ <message>
+ <location filename="../src/simplewallet/simplewallet.cpp" line="6294"/>
+ <source>Normal</source>
+ <translation>ノーマル</translation>
+ </message>
+ <message>
+ <location filename="../src/simplewallet/simplewallet.cpp" line="6295"/>
+ <source>Type: </source>
+ <translation>タイプ: </translation>
+ </message>
+ <message>
+ <location filename="../src/simplewallet/simplewallet.cpp" line="6296"/>
+ <source>Testnet: </source>
+ <translation>テストネット: </translation>
+ </message>
+ <message>
+ <location filename="../src/simplewallet/simplewallet.cpp" line="6296"/>
+ <source>Yes</source>
+ <translation>はい</translation>
+ </message>
+ <message>
+ <location filename="../src/simplewallet/simplewallet.cpp" line="6296"/>
+ <source>No</source>
+ <translation>いいえ</translation>
+ </message>
+ <message>
+ <location filename="../src/simplewallet/simplewallet.cpp" line="6314"/>
+ <source>This wallet is multisig and cannot sign</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../src/simplewallet/simplewallet.cpp" line="6335"/>
+ <source>usage: verify &lt;filename&gt; &lt;address&gt; &lt;signature&gt;</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../src/simplewallet/simplewallet.cpp" line="6360"/>
+ <source>Bad signature from </source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../src/simplewallet/simplewallet.cpp" line="6364"/>
+ <source>Good signature from </source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../src/simplewallet/simplewallet.cpp" line="6373"/>
+ <source>usage: export_key_images &lt;filename&gt;</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../src/simplewallet/simplewallet.cpp" line="6378"/>
+ <source>wallet is watch-only and cannot export key images</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../src/simplewallet/simplewallet.cpp" line="906"/>
+ <location filename="../src/simplewallet/simplewallet.cpp" line="6391"/>
+ <location filename="../src/simplewallet/simplewallet.cpp" line="6473"/>
+ <source>failed to save file </source>
+ <translation>ファイルを保存できませんでした </translation>
+ </message>
+ <message>
+ <location filename="../src/simplewallet/simplewallet.cpp" line="6402"/>
+ <source>Signed key images exported to </source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../src/simplewallet/simplewallet.cpp" line="6416"/>
+ <source>usage: import_key_images &lt;filename&gt;</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../src/simplewallet/simplewallet.cpp" line="6447"/>
+ <source>usage: export_outputs &lt;filename&gt;</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../src/simplewallet/simplewallet.cpp" line="6484"/>
+ <source>Outputs exported to </source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../src/simplewallet/simplewallet.cpp" line="6492"/>
+ <source>usage: import_outputs &lt;filename&gt;</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../src/simplewallet/simplewallet.cpp" line="3819"/>
+ <location filename="../src/simplewallet/simplewallet.cpp" line="5219"/>
+ <location filename="../src/simplewallet/simplewallet.cpp" line="5545"/>
+ <location filename="../src/simplewallet/simplewallet.cpp" line="5553"/>
+ <source>amount is wrong: </source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../src/simplewallet/simplewallet.cpp" line="3820"/>
+ <source>expected number from 0 to </source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../src/simplewallet/simplewallet.cpp" line="4079"/>
+ <source>Sweeping </source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../src/simplewallet/simplewallet.cpp" line="4559"/>
+ <source>Money successfully sent, transaction: </source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../src/simplewallet/simplewallet.cpp" line="4716"/>
+ <source>Change goes to more than one address</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../src/simplewallet/simplewallet.cpp" line="4757"/>
+ <source>%s change to %s</source>
+ <translation>%s のお釣り %s に</translation>
+ </message>
+ <message>
+ <location filename="../src/simplewallet/simplewallet.cpp" line="4760"/>
+ <source>no change</source>
+ <translation>お釣りありません</translation>
+ </message>
+ <message>
+ <location filename="../src/simplewallet/simplewallet.cpp" line="1044"/>
+ <location filename="../src/simplewallet/simplewallet.cpp" line="1057"/>
+ <location filename="../src/simplewallet/simplewallet.cpp" line="4826"/>
+ <source>Transaction successfully signed to file </source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../src/simplewallet/simplewallet.cpp" line="4876"/>
+ <source>usage: get_tx_key &lt;txid&gt;</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../src/simplewallet/simplewallet.cpp" line="4884"/>
+ <location filename="../src/simplewallet/simplewallet.cpp" line="4919"/>
+ <location filename="../src/simplewallet/simplewallet.cpp" line="4968"/>
+ <location filename="../src/simplewallet/simplewallet.cpp" line="5050"/>
+ <location filename="../src/simplewallet/simplewallet.cpp" line="5130"/>
+ <location filename="../src/simplewallet/simplewallet.cpp" line="5168"/>
+ <location filename="../src/simplewallet/simplewallet.cpp" line="6180"/>
+ <location filename="../src/simplewallet/simplewallet.cpp" line="6208"/>
+ <location filename="../src/simplewallet/simplewallet.cpp" line="6578"/>
+ <source>failed to parse txid</source>
+ <translation>txidの解析に失敗しました</translation>
+ </message>
+ <message>
+ <location filename="../src/simplewallet/simplewallet.cpp" line="4898"/>
+ <source>Tx key: </source>
+ <translation>txキー: </translation>
+ </message>
+ <message>
+ <location filename="../src/simplewallet/simplewallet.cpp" line="4903"/>
+ <source>no tx keys found for this txid</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../src/simplewallet/simplewallet.cpp" line="4912"/>
+ <source>usage: get_tx_proof &lt;txid&gt; &lt;address&gt; [&lt;message&gt;]</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../src/simplewallet/simplewallet.cpp" line="4937"/>
+ <location filename="../src/simplewallet/simplewallet.cpp" line="5147"/>
+ <location filename="../src/simplewallet/simplewallet.cpp" line="5239"/>
+ <source>signature file saved to: </source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../src/simplewallet/simplewallet.cpp" line="4939"/>
+ <location filename="../src/simplewallet/simplewallet.cpp" line="5149"/>
+ <location filename="../src/simplewallet/simplewallet.cpp" line="5241"/>
+ <source>failed to save signature file</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../src/simplewallet/simplewallet.cpp" line="4953"/>
+ <source>usage: check_tx_key &lt;txid&gt; &lt;txkey&gt; &lt;address&gt;</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../src/simplewallet/simplewallet.cpp" line="4976"/>
+ <location filename="../src/simplewallet/simplewallet.cpp" line="4985"/>
+ <source>failed to parse tx key</source>
+ <translation>txキーの解析に失敗しました</translation>
+ </message>
+ <message>
+ <location filename="../src/simplewallet/simplewallet.cpp" line="4943"/>
+ <location filename="../src/simplewallet/simplewallet.cpp" line="5031"/>
+ <location filename="../src/simplewallet/simplewallet.cpp" line="5109"/>
+ <source>error: </source>
+ <translation>エラー: </translation>
+ </message>
+ <message>
+ <location filename="../src/simplewallet/simplewallet.cpp" line="5007"/>
+ <location filename="../src/simplewallet/simplewallet.cpp" line="5080"/>
+ <source>received</source>
+ <translation>貰いました</translation>
+ </message>
+ <message>
+ <location filename="../src/simplewallet/simplewallet.cpp" line="5007"/>
+ <location filename="../src/simplewallet/simplewallet.cpp" line="5080"/>
+ <source>in txid</source>
+ <translation>txidに</translation>
+ </message>
+ <message>
+ <location filename="../src/simplewallet/simplewallet.cpp" line="5026"/>
+ <location filename="../src/simplewallet/simplewallet.cpp" line="5099"/>
+ <source>received nothing in txid</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../src/simplewallet/simplewallet.cpp" line="5010"/>
+ <location filename="../src/simplewallet/simplewallet.cpp" line="5083"/>
+ <source>WARNING: this transaction is not yet included in the blockchain!</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../src/simplewallet/simplewallet.cpp" line="5016"/>
+ <location filename="../src/simplewallet/simplewallet.cpp" line="5089"/>
+ <source>This transaction has %u confirmations</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../src/simplewallet/simplewallet.cpp" line="5020"/>
+ <location filename="../src/simplewallet/simplewallet.cpp" line="5093"/>
+ <source>WARNING: failed to determine number of confirmations!</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../src/simplewallet/simplewallet.cpp" line="5401"/>
+ <source>bad min_height parameter:</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../src/simplewallet/simplewallet.cpp" line="5413"/>
+ <source>bad max_height parameter:</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../src/simplewallet/simplewallet.cpp" line="5473"/>
+ <source>in</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../src/simplewallet/simplewallet.cpp" line="5473"/>
+ <location filename="../src/simplewallet/simplewallet.cpp" line="5514"/>
+ <source>out</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../src/simplewallet/simplewallet.cpp" line="5514"/>
+ <source>failed</source>
+ <translation>失敗</translation>
+ </message>
+ <message>
+ <location filename="../src/simplewallet/simplewallet.cpp" line="5514"/>
+ <source>pending</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../src/simplewallet/simplewallet.cpp" line="5560"/>
+ <source>&lt;min_amount&gt; should be smaller than &lt;max_amount&gt;</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../src/simplewallet/simplewallet.cpp" line="5592"/>
+ <source>
+Amount: </source>
+ <translation>
+金額: </translation>
+ </message>
+ <message>
+ <location filename="../src/simplewallet/simplewallet.cpp" line="5592"/>
+ <source>, number of keys: </source>
+ <translation>、キーの数: </translation>
+ </message>
+ <message>
+ <location filename="../src/simplewallet/simplewallet.cpp" line="5597"/>
+ <source> </source>
+ <translation> </translation>
+ </message>
+ <message>
+ <location filename="../src/simplewallet/simplewallet.cpp" line="5602"/>
+ <source>
+Min block height: </source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../src/simplewallet/simplewallet.cpp" line="5603"/>
+ <source>
+Max block height: </source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../src/simplewallet/simplewallet.cpp" line="5604"/>
+ <source>
+Min amount found: </source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../src/simplewallet/simplewallet.cpp" line="5605"/>
+ <source>
+Max amount found: </source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../src/simplewallet/simplewallet.cpp" line="5606"/>
+ <source>
+Total count: </source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../src/simplewallet/simplewallet.cpp" line="5646"/>
+ <source>
+Bin size: </source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../src/simplewallet/simplewallet.cpp" line="5647"/>
+ <source>
+Outputs per *: </source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../src/simplewallet/simplewallet.cpp" line="5649"/>
+ <source>count
+ ^
+</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../src/simplewallet/simplewallet.cpp" line="5651"/>
+ <source> |</source>
+ <translation> |</translation>
+ </message>
+ <message>
+ <location filename="../src/simplewallet/simplewallet.cpp" line="5653"/>
+ <source> +</source>
+ <translation> +</translation>
+ </message>
+ <message>
+ <location filename="../src/simplewallet/simplewallet.cpp" line="5653"/>
+ <source>+--&gt; block height
+</source>
+ <translation>+--&gt; ブロック高
+</translation>
+ </message>
+ <message>
+ <location filename="../src/simplewallet/simplewallet.cpp" line="5654"/>
+ <source> ^</source>
+ <translation> ^</translation>
+ </message>
+ <message>
+ <location filename="../src/simplewallet/simplewallet.cpp" line="5654"/>
+ <source>^
+</source>
+ <translation>^
+</translation>
+ </message>
+ <message>
+ <location filename="../src/simplewallet/simplewallet.cpp" line="5655"/>
+ <source> </source>
+ <translation> </translation>
+ </message>
+ <message>
+ <location filename="../src/simplewallet/simplewallet.cpp" line="5696"/>
+ <source>wallet</source>
+ <translation>ウォレット</translation>
+ </message>
+ <message>
+ <location filename="../src/simplewallet/simplewallet.cpp" line="666"/>
+ <location filename="../src/simplewallet/simplewallet.cpp" line="6057"/>
+ <source>Random payment ID: </source>
+ <translation>ランダムなペイメントID: </translation>
+ </message>
+ <message>
+ <location filename="../src/simplewallet/simplewallet.cpp" line="6058"/>
+ <source>Matching integrated address: </source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>genms</name>
+ <message>
+ <location filename="../src/gen_multisig/gen_multisig.cpp" line="70"/>
+ <source>Base filename (-1, -2, etc suffixes will be appended as needed)</source>
+ <translation>ベースファイル名(必要があれば-1、-2、とかのサフィックスを追加します)</translation>
+ </message>
+ <message>
+ <location filename="../src/gen_multisig/gen_multisig.cpp" line="71"/>
+ <source>Give threshold and participants at once as M/N</source>
+ <translation>M/Nってのフォーマットで同じ時に閾値と参加者をください</translation>
+ </message>
+ <message>
+ <location filename="../src/gen_multisig/gen_multisig.cpp" line="72"/>
+ <source>How many participants will share parts of the multisig wallet</source>
+ <translation>マルチサインウォレットを分ける人はいくついますか</translation>
+ </message>
+ <message>
+ <location filename="../src/gen_multisig/gen_multisig.cpp" line="73"/>
+ <source>How many signers are required to sign a valid transaction</source>
+ <translation>有効な取引を署名するために必要な人いくついますか</translation>
+ </message>
+ <message>
+ <location filename="../src/gen_multisig/gen_multisig.cpp" line="74"/>
+ <source>Create testnet multisig wallets</source>
+ <translation>テストネットのマルチサインウォレットを作る</translation>
+ </message>
+ <message>
+ <location filename="../src/gen_multisig/gen_multisig.cpp" line="81"/>
+ <source>Generating %u %u/%u multisig wallets</source>
+ <translation>%u %u/%u マルチサインウォレットを生じてます</translation>
+ </message>
+ <message>
+ <location filename="../src/gen_multisig/gen_multisig.cpp" line="138"/>
+ <source>Error verifying multisig extra info</source>
+ <translation>マルチサインの追加情報を検証中にエラーありました</translation>
+ </message>
+ <message>
+ <location filename="../src/gen_multisig/gen_multisig.cpp" line="146"/>
+ <source>Error finalizing multisig</source>
+ <translation>マルチサイン締結中にエラーありました</translation>
+ </message>
+ <message>
+ <location filename="../src/gen_multisig/gen_multisig.cpp" line="153"/>
+ <source>Generated multisig wallets for address </source>
+ <translation>アドレスのためにマルチサインウォレットを生じなかった </translation>
+ </message>
+ <message>
+ <location filename="../src/gen_multisig/gen_multisig.cpp" line="157"/>
+ <source>Error creating multisig wallets: </source>
+ <translation>マルチサインウォレットを樹立中にエラーありました: </translation>
+ </message>
+ <message>
+ <location filename="../src/gen_multisig/gen_multisig.cpp" line="176"/>
+ <source>This program generates a set of multisig wallets - use this simpler scheme only if all the participants trust each other</source>
+ <translation>このプログラムはマルチサインウォレットのセットを生じます&#x3000;-&#x3000;みんながみんなを信用する場合にだけこの単純なスキームを使ってください</translation>
+ </message>
+ <message>
+ <location filename="../src/gen_multisig/gen_multisig.cpp" line="194"/>
+ <source>Error: expected N/M, but got: </source>
+ <translation>エラー: N/Mを欲しかったでもこれを貰いました: </translation>
+ </message>
+ <message>
+ <location filename="../src/gen_multisig/gen_multisig.cpp" line="202"/>
+ <location filename="../src/gen_multisig/gen_multisig.cpp" line="211"/>
+ <source>Error: either --scheme or both of --threshold and --participants may be given</source>
+ <translation>エラー: --scheme あるいは--threshold と --participants を上げられる</translation>
+ </message>
+ <message>
+ <location filename="../src/gen_multisig/gen_multisig.cpp" line="218"/>
+ <source>Error: expected N &gt; 1 and N &lt;= M, but got N==%u and M==%d</source>
+ <translation>エラー: N > 1 と N <= M のこと欲しかったでも N==%u と M==%d を貰いました</translation>
+ </message>
+ <message>
+ <location filename="../src/gen_multisig/gen_multisig.cpp" line="227"/>
+ <source>Error: --filename-base is required</source>
+ <translation>エラー: --filename-baseを使う必要だがあります</translation>
+ </message>
+ <message>
+ <location filename="../src/gen_multisig/gen_multisig.cpp" line="233"/>
+ <source>Error: unsupported scheme: only N/N and N-1/N are supported</source>
+ <translation>エラー: 不正なスキーム: N/N や N-1/N`のことをできます</translation>
+ </message>
+</context>
+<context>
+ <name>sw</name>
+ <message>
+ <location filename="../src/simplewallet/simplewallet.cpp" line="115"/>
+ <source>Generate new wallet and save it to &lt;arg&gt;</source>
+ <translation>新しいウォレットを生じろ &lt;arg&gt; をこっちにセーブしてください</translation>
+ </message>
+ <message>
+ <location filename="../src/simplewallet/simplewallet.cpp" line="116"/>
+ <source>Generate incoming-only wallet from view key</source>
+ <translation>ビューキーで閲覧専用ウォレットを生じろください</translation>
+ </message>
+ <message>
+ <location filename="../src/simplewallet/simplewallet.cpp" line="117"/>
+ <source>Generate deterministic wallet from spend key</source>
+ <translation>スペンドキーで決定論的なウォレットを生じろください</translation>
+ </message>
+ <message>
+ <location filename="../src/simplewallet/simplewallet.cpp" line="118"/>
+ <source>Generate wallet from private keys</source>
+ <translation>秘密なキーでウォレットを生じろください</translation>
+ </message>
+ <message>
+ <location filename="../src/simplewallet/simplewallet.cpp" line="119"/>
+ <source>Generate a master wallet from multisig wallet keys</source>
+ <translation>マルチシガーウォレットキーでマスターウォレットを生じろください</translation>
+ </message>
+ <message>
+ <location filename="../src/simplewallet/simplewallet.cpp" line="121"/>
+ <source>Language for mnemonic</source>
+ <translation>ニーモニックのための言語</translation>
+ </message>
+ <message>
+ <location filename="../src/simplewallet/simplewallet.cpp" line="122"/>
+ <source>Specify Electrum seed for wallet recovery/creation</source>
+ <translation>ウォレットの回収や作成のためにElectrumなニーモニックシードを指定してください</translation>
+ </message>
+ <message>
+ <location filename="../src/simplewallet/simplewallet.cpp" line="123"/>
+ <source>Recover wallet using Electrum-style mnemonic seed</source>
+ <translation>Electrumなニーモニックシードでウォレットを復元してください</translation>
+ </message>
+ <message>
+ <location filename="../src/simplewallet/simplewallet.cpp" line="124"/>
+ <source>Recover multisig wallet using Electrum-style mnemonic seed</source>
+ <translation>Electrumなニーモニックシードでマルチシガーウォレットを復元してください</translation>
+ </message>
+ <message>
+ <location filename="../src/simplewallet/simplewallet.cpp" line="125"/>
+ <source>Generate non-deterministic view and spend keys</source>
+ <translation>非決定論的のビューとスペンドキーを生じろください</translation>
+ </message>
+ <message>
+ <location filename="../src/simplewallet/simplewallet.cpp" line="126"/>
+ <source>Enable commands which rely on a trusted daemon</source>
+ <translation>必要な信用できるデーモンのコマンドをエネーブルしてください</translation>
+ </message>
+ <message>
+ <location filename="../src/simplewallet/simplewallet.cpp" line="127"/>
+ <source>Allow communicating with a daemon that uses a different RPC version</source>
+ <translation>別のRPCバージョンを使用してるデーモンとの通信を許可してください</translation>
+ </message>
+ <message>
+ <location filename="../src/simplewallet/simplewallet.cpp" line="128"/>
+ <source>Restore from specific blockchain height</source>
+ <translation>特定ブロックチェイン高で復元してください</translation>
+ </message>
+ <message>
+ <location filename="../src/simplewallet/simplewallet.cpp" line="129"/>
+ <source>The newly created transaction will not be relayed to the monero network</source>
+ <translation>新しい取引をネットワークに中継しません</translation>
+ </message>
+ <message>
+ <location filename="../src/simplewallet/simplewallet.cpp" line="171"/>
+ <source>daemon is busy. Please try again later.</source>
+ <translation>デーモンは忙しいです。後でもう一度試してください。</translation>
+ </message>
+ <message>
+ <location filename="../src/simplewallet/simplewallet.cpp" line="180"/>
+ <source>possibly lost connection to daemon</source>
+ <translation>デモンの接続が切れましたかもしりません</translation>
+ </message>
+ <message>
+ <location filename="../src/simplewallet/simplewallet.cpp" line="197"/>
+ <source>Error: </source>
+ <translation>エラー: </translation>
+ </message>
+ <message>
+ <location filename="../src/simplewallet/simplewallet.cpp" line="6787"/>
+ <source>This is the command line monero wallet. It needs to connect to a monero
+daemon to work correctly.</source>
+ <translation>これはMoneroのコマンドラインウォレットです。別のMoneroデモンと接続する必要があります。</translation>
+ </message>
+ <message>
+ <location filename="../src/simplewallet/simplewallet.cpp" line="6801"/>
+ <source>Failed to initialize wallet</source>
+ <translation>ウォレットを初期化できませんでした</translation>
+ </message>
+</context>
+<context>
+ <name>tools::wallet2</name>
+ <message>
+ <location filename="../src/wallet/wallet2.cpp" line="113"/>
+ <source>Use daemon instance at &lt;host&gt;:&lt;port&gt;</source>
+ <translation>&lt;host&gt;:&lt;port&gt;でデーモンインスタンスを使ってください</translation>
+ </message>
+ <message>
+ <location filename="../src/wallet/wallet2.cpp" line="114"/>
+ <source>Use daemon instance at host &lt;arg&gt; instead of localhost</source>
+ <translation>localhostの代わりにホスト &lt;arg&gt;でデーモンインスタンスを使ってください</translation>
+ </message>
+ <message>
+ <location filename="../src/wallet/wallet2.cpp" line="116"/>
+ <source>Wallet password file</source>
+ <translation>ウォレットのパスワードファイル</translation>
+ </message>
+ <message>
+ <location filename="../src/wallet/wallet2.cpp" line="117"/>
+ <source>Use daemon instance at port &lt;arg&gt; instead of 18081</source>
+ <translation>18081の代わりにポート &lt;arg&gt;でデーモンインスタンスを使ってください</translation>
+ </message>
+ <message>
+ <location filename="../src/wallet/wallet2.cpp" line="119"/>
+ <source>For testnet. Daemon must also be launched with --testnet flag</source>
+ <translation>テストネットのためにデーモンは --testnet のフラグで開始する必要があります</translation>
+ </message>
+ <message>
+ <location filename="../src/wallet/wallet2.cpp" line="120"/>
+ <source>Restricts to view-only commands</source>
+ <translation>閲覧専用コマンドに限ります</translation>
+ </message>
+ <message>
+ <location filename="../src/wallet/wallet2.cpp" line="168"/>
+ <source>can&apos;t specify daemon host or port more than once</source>
+ <translation>デーモンのホストやポートを複数回指定することはできません</translation>
+ </message>
+ <message>
+ <location filename="../src/wallet/wallet2.cpp" line="204"/>
+ <source>can&apos;t specify more than one of --password and --password-file</source>
+ <translation>--password と --passwordfile を1つしか指定しません</translation>
+ </message>
+ <message>
+ <location filename="../src/wallet/wallet2.cpp" line="217"/>
+ <source>the password file specified could not be read</source>
+ <translation>指定されたパスワードファイルを読み取れません</translation>
+ </message>
+ <message>
+ <location filename="../src/wallet/wallet2.cpp" line="240"/>
+ <source>Failed to load file </source>
+ <translation>ファイルのロードに失敗しました </translation>
+ </message>
+ <message>
+ <location filename="../src/wallet/wallet2.cpp" line="115"/>
+ <source>Wallet password (escape/quote as needed)</source>
+ <translation>ウォレットのパスワード(随時にエスケープ文字を使ってください)</translation>
+ </message>
+ <message>
+ <location filename="../src/wallet/wallet2.cpp" line="118"/>
+ <source>Specify username[:password] for daemon RPC client</source>
+ <translation>デーモンのRPCクライアントを使うためにユーザー名[:パスワード]を指定してください</translation>
+ </message>
+ <message>
+ <location filename="../src/wallet/wallet2.cpp" line="224"/>
+ <source>no password specified; use --prompt-for-password to prompt for a password</source>
+ <translation>パスワードを指定しませんでした。パスワードプロンプトを見るために--prompt-for-password を使ってください</translation>
+ </message>
+ <message>
+ <location filename="../src/wallet/wallet2.cpp" line="246"/>
+ <source>Failed to parse JSON</source>
+ <translation>JSONを解析に失敗しました</translation>
+ </message>
+ <message>
+ <location filename="../src/wallet/wallet2.cpp" line="253"/>
+ <source>Version %u too new, we can only grok up to %u</source>
+ <translation>バージョン %u 新しすぎるです。%u までグロークできます</translation>
+ </message>
+ <message>
+ <location filename="../src/wallet/wallet2.cpp" line="269"/>
+ <source>failed to parse view key secret key</source>
+ <translation>秘密なビューキーの解析に失敗しました</translation>
+ </message>
+ <message>
+ <location filename="../src/wallet/wallet2.cpp" line="274"/>
+ <location filename="../src/wallet/wallet2.cpp" line="339"/>
+ <location filename="../src/wallet/wallet2.cpp" line="380"/>
+ <source>failed to verify view key secret key</source>
+ <translation>秘密なビューキーの検証に失敗しました</translation>
+ </message>
+ <message>
+ <location filename="../src/wallet/wallet2.cpp" line="285"/>
+ <source>failed to parse spend key secret key</source>
+ <translation>秘密なスペンドキーの解析に失敗しました</translation>
+ </message>
+ <message>
+ <location filename="../src/wallet/wallet2.cpp" line="290"/>
+ <location filename="../src/wallet/wallet2.cpp" line="349"/>
+ <location filename="../src/wallet/wallet2.cpp" line="405"/>
+ <source>failed to verify spend key secret key</source>
+ <translation>秘密なスペンドキーの検証に失敗しました</translation>
+ </message>
+ <message>
+ <location filename="../src/wallet/wallet2.cpp" line="302"/>
+ <source>Electrum-style word list failed verification</source>
+ <translation>Electrumな単語表の検証に失敗しました</translation>
+ </message>
+ <message>
+ <location filename="../src/wallet/wallet2.cpp" line="319"/>
+ <source>At least one of Electrum-style word list and private view key and private spend key must be specified</source>
+ <translation>Electrumな単語表と秘密なビューキーと秘密なスペンドキーの少なくとも1つを指定する必要があります</translation>
+ </message>
+ <message>
+ <location filename="../src/wallet/wallet2.cpp" line="323"/>
+ <source>Both Electrum-style word list and private key(s) specified</source>
+ <translation>Electrumな単語表と秘密なキーを指定しました</translation>
+ </message>
+ <message>
+ <location filename="../src/wallet/wallet2.cpp" line="333"/>
+ <source>invalid address</source>
+ <translation>不正なアドレス</translation>
+ </message>
+ <message>
+ <location filename="../src/wallet/wallet2.cpp" line="342"/>
+ <source>view key does not match standard address</source>
+ <translation>ビューキーが一般的なアドレスと一致しませんでした</translation>
+ </message>
+ <message>
+ <location filename="../src/wallet/wallet2.cpp" line="352"/>
+ <source>spend key does not match standard address</source>
+ <translation>スペンドキーが一般的なアドレスと一致しませんでした</translation>
+ </message>
+ <message>
+ <location filename="../src/wallet/wallet2.cpp" line="360"/>
+ <source>Cannot generate deprecated wallets from JSON</source>
+ <translation>JSONで非推奨のウォレットを生成することはできません</translation>
+ </message>
+ <message>
+ <location filename="../src/wallet/wallet2.cpp" line="392"/>
+ <source>failed to parse address: </source>
+ <translation>アドレスの解析に失敗しました: </translation>
+ </message>
+ <message>
+ <location filename="../src/wallet/wallet2.cpp" line="398"/>
+ <source>Address must be specified in order to create watch-only wallet</source>
+ <translation>閲覧専用ウォレットを作るためにアドレスを指定する必要があります</translation>
+ </message>
+ <message>
+ <location filename="../src/wallet/wallet2.cpp" line="413"/>
+ <source>failed to generate new wallet: </source>
+ <translation>新しいウォレットの生成に失敗しました: </translation>
+ </message>
+ <message>
+ <location filename="../src/wallet/wallet2.cpp" line="2813"/>
+ <location filename="../src/wallet/wallet2.cpp" line="2873"/>
+ <location filename="../src/wallet/wallet2.cpp" line="2952"/>
+ <location filename="../src/wallet/wallet2.cpp" line="2998"/>
+ <location filename="../src/wallet/wallet2.cpp" line="3089"/>
+ <location filename="../src/wallet/wallet2.cpp" line="3189"/>
+ <location filename="../src/wallet/wallet2.cpp" line="3599"/>
+ <location filename="../src/wallet/wallet2.cpp" line="3955"/>
+ <source>Primary account</source>
+ <translation>プライマリア カウント</translation>
+ </message>
+ <message>
+ <location filename="../src/wallet/wallet2.cpp" line="7914"/>
+ <source>No funds received in this tx.</source>
+ <translation>この取引から資金貰いませんでした。</translation>
+ </message>
+ <message>
+ <location filename="../src/wallet/wallet2.cpp" line="8607"/>
+ <source>failed to read file </source>
+ <translation>ファイルの読み込みに失敗しました </translation>
+ </message>
+</context>
+<context>
+ <name>tools::wallet_rpc_server</name>
+ <message>
+ <location filename="../src/wallet/wallet_rpc_server.cpp" line="160"/>
+ <source>Daemon is local, assuming trusted</source>
+ <translation>デーモンはローカルです。信頼できるデーモン予期してます</translation>
+ </message>
+ <message>
+ <location filename="../src/wallet/wallet_rpc_server.cpp" line="175"/>
+ <source>Failed to create directory </source>
+ <translation>ディレクトリの作成に失敗しました </translation>
+ </message>
+ <message>
+ <location filename="../src/wallet/wallet_rpc_server.cpp" line="177"/>
+ <source>Failed to create directory %s: %s</source>
+ <translation>%s %s ディレクトリの作成に失敗しました</translation>
+ </message>
+ <message>
+ <location filename="../src/wallet/wallet_rpc_server.cpp" line="188"/>
+ <source>Cannot specify --</source>
+ <translation>これを指定しません: --</translation>
+ </message>
+ <message>
+ <location filename="../src/wallet/wallet_rpc_server.cpp" line="188"/>
+ <source> and --</source>
+ <translation> と --</translation>
+ </message>
+ <message>
+ <location filename="../src/wallet/wallet_rpc_server.cpp" line="207"/>
+ <source>Failed to create file </source>
+ <translation>ファイルの作成に失敗しました </translation>
+ </message>
+ <message>
+ <location filename="../src/wallet/wallet_rpc_server.cpp" line="207"/>
+ <source>. Check permissions or remove file</source>
+ <translation>。 パーミッションを確認するか、ファイルを削除してください</translation>
+ </message>
+ <message>
+ <location filename="../src/wallet/wallet_rpc_server.cpp" line="217"/>
+ <source>Error writing to file </source>
+ <translation>ファイルへの書き込みエラー </translation>
+ </message>
+ <message>
+ <location filename="../src/wallet/wallet_rpc_server.cpp" line="220"/>
+ <source>RPC username/password is stored in file </source>
+ <translation>RPCユーザー名/パスワードはファイルに保存しました </translation>
+ </message>
+ <message>
+ <location filename="../src/wallet/wallet_rpc_server.cpp" line="443"/>
+ <source>Tag %s is unregistered.</source>
+ <translation>タグ %s を登録してません。</translation>
+ </message>
+ <message>
+ <location filename="../src/wallet/wallet_rpc_server.cpp" line="2435"/>
+ <source>Transaction not possible. Available only %s, transaction amount %s = %s + %s (fee)</source>
+ <translation>取引は無理です。利用可能な金額 %s、 取引の金額 %s = %s + %s (手数料)</translation>
+ </message>
+ <message>
+ <location filename="../src/wallet/wallet_rpc_server.cpp" line="2870"/>
+ <source>This is the RPC monero wallet. It needs to connect to a monero
+daemon to work correctly.</source>
+ <translation>これはMoneroのコマンドラインウォレットです。別のMoneroデモンと接続する必要があります。</translation>
+ </message>
+ <message>
+ <location filename="../src/wallet/wallet_rpc_server.cpp" line="2893"/>
+ <source>Can&apos;t specify more than one of --wallet-file and --generate-from-json</source>
+ <translation>--wallet-file と --generate-from-json を1つしか指定しません</translation>
+ </message>
+ <message>
+ <location filename="../src/wallet/wallet_rpc_server.cpp" line="2905"/>
+ <source>Must specify --wallet-file or --generate-from-json or --wallet-dir</source>
+ <translation>--wallet-file や --generate-from-json や --wallet-dir を指定する必要があります</translation>
+ </message>
+ <message>
+ <location filename="../src/wallet/wallet_rpc_server.cpp" line="2909"/>
+ <source>Loading wallet...</source>
+ <translation>ウォレットをロードしてます...</translation>
+ </message>
+ <message>
+ <location filename="../src/wallet/wallet_rpc_server.cpp" line="2942"/>
+ <location filename="../src/wallet/wallet_rpc_server.cpp" line="2975"/>
+ <source>Saving wallet...</source>
+ <translation>ウォレットを保存してます...</translation>
+ </message>
+ <message>
+ <location filename="../src/wallet/wallet_rpc_server.cpp" line="2944"/>
+ <location filename="../src/wallet/wallet_rpc_server.cpp" line="2977"/>
+ <source>Successfully saved</source>
+ <translation>保存しました</translation>
+ </message>
+ <message>
+ <location filename="../src/wallet/wallet_rpc_server.cpp" line="2947"/>
+ <source>Successfully loaded</source>
+ <translation>ロードしました</translation>
+ </message>
+ <message>
+ <location filename="../src/wallet/wallet_rpc_server.cpp" line="2951"/>
+ <source>Wallet initialization failed: </source>
+ <translation>ウォレットを初期化できませんでした: </translation>
+ </message>
+ <message>
+ <location filename="../src/wallet/wallet_rpc_server.cpp" line="2958"/>
+ <source>Failed to initialize wallet RPC server</source>
+ <translation>ウォレットのRPCサーバを初期化できませんでした</translation>
+ </message>
+ <message>
+ <location filename="../src/wallet/wallet_rpc_server.cpp" line="2962"/>
+ <source>Starting wallet RPC server</source>
+ <translation>ウォレットのRPCサーバを開始してます</translation>
+ </message>
+ <message>
+ <location filename="../src/wallet/wallet_rpc_server.cpp" line="2969"/>
+ <source>Failed to run wallet: </source>
+ <translation>ウォレットを起動することできませんでした: </translation>
+ </message>
+ <message>
+ <location filename="../src/wallet/wallet_rpc_server.cpp" line="2972"/>
+ <source>Stopped wallet RPC server</source>
+ <translation>ウォレットのRPCサーバを停止しました</translation>
+ </message>
+ <message>
+ <location filename="../src/wallet/wallet_rpc_server.cpp" line="2981"/>
+ <source>Failed to save wallet: </source>
+ <translation>ウォレットを保存することできませんでした: </translation>
+ </message>
+</context>
+<context>
+ <name>wallet_args</name>
+ <message>
+ <location filename="../src/gen_multisig/gen_multisig.cpp" line="166"/>
+ <location filename="../src/simplewallet/simplewallet.cpp" line="6760"/>
+ <location filename="../src/wallet/wallet_rpc_server.cpp" line="2856"/>
+ <source>Wallet options</source>
+ <translation>ウォレットのオプション</translation>
+ </message>
+ <message>
+ <location filename="../src/wallet/wallet_args.cpp" line="73"/>
+ <source>Generate wallet from JSON format file</source>
+ <translation>JSONフォーマットファイルでウォレットを生じてください</translation>
+ </message>
+ <message>
+ <location filename="../src/wallet/wallet_args.cpp" line="77"/>
+ <source>Use wallet &lt;arg&gt;</source>
+ <translation>ウォレットの &lt;arg&gt; を使てください</translation>
+ </message>
+ <message>
+ <location filename="../src/wallet/wallet_args.cpp" line="104"/>
+ <source>Max number of threads to use for a parallel job</source>
+ <translation>並列ジョブのために最大スレッドの数</translation>
+ </message>
+ <message>
+ <location filename="../src/wallet/wallet_args.cpp" line="105"/>
+ <source>Specify log file</source>
+ <translation>ログファイルを指定してください</translation>
+ </message>
+ <message>
+ <location filename="../src/wallet/wallet_args.cpp" line="106"/>
+ <source>Config file</source>
+ <translation>設定ファイル</translation>
+ </message>
+ <message>
+ <location filename="../src/wallet/wallet_args.cpp" line="115"/>
+ <source>General options</source>
+ <translation>ジェネラルオプション</translation>
+ </message>
+ <message>
+ <location filename="../src/wallet/wallet_args.cpp" line="138"/>
+ <source>This is the command line monero wallet. It needs to connect to a monero
+daemon to work correctly.</source>
+ <translation>これはMoneroのコマンドラインウォレットです。別のMoneroデモンと接続する必要があります。</translation>
+ </message>
+ <message>
+ <location filename="../src/wallet/wallet_args.cpp" line="161"/>
+ <source>Can&apos;t find config file </source>
+ <translation>設定ファイルを見つかりませんでした </translation>
+ </message>
+ <message>
+ <location filename="../src/wallet/wallet_args.cpp" line="195"/>
+ <source>Logging to: </source>
+ <translation>こっちにログをしてます: </translation>
+ </message>
+ <message>
+ <location filename="../src/wallet/wallet_args.cpp" line="197"/>
+ <source>Logging to %s</source>
+ <translation>%s にログをしてます</translation>
+ </message>
+ <message>
+ <location filename="../src/wallet/wallet_args.cpp" line="140"/>
+ <source>Usage:</source>
+ <translation>使用:</translation>
+ </message>
+</context>
+</TS>
diff --git a/translations/monero_sv.ts b/translations/monero_sv.ts
index 3c5ddf9af..b2387cdb1 100644
--- a/translations/monero_sv.ts
+++ b/translations/monero_sv.ts
@@ -1855,7 +1855,16 @@ Annars bevisar du reserven för det minsta möjliga belopp över &lt;belopp&gt;
</message>
<message>
<location filename="../src/simplewallet/simplewallet.cpp" line="1758"/>
- <source>Show the incoming/outgoing transfers within an optional height range.</source>
+ <source>Show the incoming/outgoing transfers within an optional height range.
+
+Output format:
+In or Coinbase: Block Number, &quot;block&quot;|&quot;in&quot;, Time, Amount, Transaction Hash, Payment ID, Subaddress Index, &quot;-&quot;, Note\
+Out: Block Number, &quot;out&quot;, Time, Amount*, Transaction Hash, Payment ID, Fee, Destinations, Input addresses**, &quot;-&quot;, Note
+Pool: &quot;pool&quot;, &quot;in&quot;, Time, Amount, Transaction Hash, Payment Id, Subaddress Index, &quot;-&quot;, Note, Double Spend Note\
+Pending or Failed: &quot;failed&quot;|&quot;pending&quot;, &quot;out&quot;, Time, Amount*, Transaction Hash, Payment ID, Fee, Input addresses**, &quot;-&quot;, Note
+
+* Excluding change and fee.
+** Set of address indices used as inputs in this transfer.</source>
<translation>Visa inkommande/utgående överföringar inom ett valfritt höjdintervall.</translation>
</message>
<message>