diff --git a/.circleci/config.yml b/.circleci/config.yml new file mode 100644 index 00000000..339941aa --- /dev/null +++ b/.circleci/config.yml @@ -0,0 +1,100 @@ +version: 2 +jobs: + build: + docker: + - image: zondax/circleci:latest + steps: + - checkout + - run: git submodule update --init --recursive + - run: cmake -DDISABLE_DOCKER_BUILDS=ON -DCMAKE_BUILD_TYPE=Debug . && make + # Unfortunately need to disable leak sanitizer https://github.com/google/sanitizers/issues/916 + # Still run all other ASAN components + - run: GTEST_COLOR=1 ASAN_OPTIONS=detect_leaks=0 ctest -VV + + build_ledger: + docker: + - image: zondax/builder-bolos:latest + environment: + - BOLOS_SDK=/home/zondax/project/deps/nanos-secure-sdk + - BOLOS_ENV=/opt/bolos + steps: + - checkout + # Docker entrypoint is not considered + - run: git submodule update --init --recursive + - run: + name: Build + command: | + source /home/zondax/.cargo/env + cd /home/zondax/project + make + + test_zemu: + machine: + image: ubuntu-1604:201903-01 + working_directory: ~/repo + environment: + BASH_ENV: "/opt/circleci/.nvm/nvm.sh" + steps: + - checkout + - run: git submodule update --init --recursive + - run: + name: Build Ledger app + command: | + make + - run: + name: Install node + yarn + command: | + nvm install 13.12.0 + nvm use 13.12.0 + npm install -g yarn + - run: + name: Build/Install build js deps + command: | + nvm use 13.12.0 + make zemu_install + - run: + name: Workaround/Pull docker + command: | + docker pull zondax/builder-zemu + - run: + name: Run zemu tests + command: | + nvm use 13.12.0 + make zemu_test + + build_package: + docker: + - image: zondax/builder-bolos:latest + environment: + - BOLOS_SDK=/home/zondax/project/deps/nanos-secure-sdk + - BOLOS_ENV=/opt/bolos + steps: + - checkout + - run: git submodule update --init --recursive + - run: + name: Build + command: | + source /home/zondax/.cargo/env + cd /home/zondax/project + make + - store_artifacts: + path: /home/zondax/project/app/pkg/zxtool.sh + - run: /home/zondax/go/bin/ghr -t ${GITHUB_TOKEN} -u ${CIRCLE_PROJECT_USERNAME} -r ${CIRCLE_PROJECT_REPONAME} -c ${CIRCLE_SHA1} -delete $(/home/zondax/project/app/pkg/zxtool.sh version) /home/zondax/project/app/pkg/zxtool.sh + +workflows: + version: 2 + + default: + jobs: + - build + - build_ledger + - test_zemu +# - build_package: +# requires: +# - build +# - build_ledger +# - test_zemu +# filters: +# branches: +# only: +# - master diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 00000000..2d7db14f --- /dev/null +++ b/.editorconfig @@ -0,0 +1,16 @@ +# top-most EditorConfig file +root = true + +[*] +charset = utf-8 +trim_trailing_whitespace = true +end_of_line = lf +insert_final_newline = true + +[*.{c,h,cpp,hpp}] +indent_style = space +indent_size = 4 + +[*.{yml,sh}] +indent_style = space +indent_size = 2 diff --git a/.gdbinit b/.gdbinit new file mode 100644 index 00000000..8abe1fbb --- /dev/null +++ b/.gdbinit @@ -0,0 +1,11 @@ +# https://www.jetbrains.com/help/clion/configuring-debugger-options.html#gdbinit-lldbinit +# +# You need to create `$HOME/.gdbinit` with the following content: +# set auto-load local-gdbinit on +# add-auto-load-safe-path / + +set architecture arm +handle SIGILL nostop pass noprint +add-symbol-file app/bin/app.elf 0x40000000 +set backtrace limit 20 +b *0x40000000 diff --git a/.gitignore b/.gitignore index eaf7e6bd..b2ba5111 100644 --- a/.gitignore +++ b/.gitignore @@ -1,64 +1,90 @@ +# Created by .ignore support plugin (hsz.mobi) +### C template # Prerequisites *.d -# Compiled Object files -*.slo -*.lo +# Object files *.o +*.ko *.obj +*.elf + +# Linker output +*.ilk +*.map +*.exp # Precompiled Headers *.gch *.pch -# Compiled Dynamic libraries +# Libraries +*.lib +*.a +*.la +*.lo + +# Shared objects (inc. Windows DLLs) +*.dll *.so +*.so.* *.dylib -*.dll - -# Fortran module files -*.mod -*.smod - -# Compiled Static libraries -*.lai -*.la -*.a -*.lib # Executables *.exe *.out *.app - -# OS related files -.DS_Store - -# Others -cmake-build-debug/ -\.idea/workspace\.xml +*.i*86 +*.x86_64 +*.hex + +# Debug files +*.dSYM/ +*.su +*.idb +*.pdb + +# Kernel Module Compile Results +*.mod* +*.cmd +.tmp_versions/ +modules.order +Module.symvers +Mkfile.old +dkms.conf + +\cmake-build-debug \.idea/ -src/ledger/bin/ -src/ledger/debug/ -src/ledger/obj/ -src/goclient/vendor -pkg/ -src/goclient/goclient - -src/ledger/pkgdemo/loaddemo\.sh - -src/ledger/pkgdemo/app\.hex - -obj/app\.elf +/tmp/ +/deps/nano2-sdk/ + +# Created by cmake +googletest-download/ +googletest-src/ +googletest-build/ +CMakeFiles/ +CMakeCache.txt +unittests +*.cmake +Testing/ +cmake-build-fuzz/ -debug/app\.map - -debug/app\.asm - -bin/app\.hex - -bin/app\.elf - -src/glyphs\.h - -src/glyphs\.c +# Others +/cmake-build-debug/ +/cmake-build-fuzz/ +\.idea +/app/bin/ +/app/debug/ +/app/obj/ + +\deps/* +!\deps/nanos-secure-sdk +\deps/nano2-sdk +!\deps/ledger-zxlib +!\deps/tinycbor +!\deps/BLAKE + +app/src/glyphs.c + +app/src/glyphs.h +/build diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 00000000..f207938b --- /dev/null +++ b/.gitmodules @@ -0,0 +1,3 @@ +[submodule "deps/nanos-secure-sdk"] + path = deps/nanos-secure-sdk + url = https://github.com/LedgerHQ/nanos-secure-sdk.git diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 00000000..d28d6388 --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,93 @@ +#******************************************************************************* +#* (c) 2018 Zondax GmbH +#* +#* Licensed under the Apache License, Version 2.0 (the "License"); +#* you may not use this file except in compliance with the License. +#* You may obtain a copy of the License at +#* +#* http://www.apache.org/licenses/LICENSE-2.0 +#* +#* Unless required by applicable law or agreed to in writing, software +#* distributed under the License is distributed on an "AS IS" BASIS, +#* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +#* See the License for the specific language governing permissions and +#* limitations under the License. +#******************************************************************************** +cmake_minimum_required(VERSION 3.0) +project(ledger-cosmos VERSION 0.0.0) +enable_testing() + +cmake_policy(SET CMP0025 NEW) +set(CMAKE_CXX_STANDARD 11) + +include(cmake/conan/CMakeLists.txt) +add_subdirectory(cmake/gtest) + +string(APPEND CMAKE_CXX_FLAGS " -fsanitize=address -fno-omit-frame-pointer") +string(APPEND CMAKE_LINKER_FLAGS " -fsanitize=address -fno-omit-frame-pointer") + +############################################################## +############################################################## +# static libs +file(GLOB_RECURSE JSMN_SRC + deps/jsmn/src/jsmn.c + ) + +file(GLOB_RECURSE LIB_SRC + app/src/json/json_parser.c + app/src/tx_parser.c + app/src/tx_display.c + app/src/tx_validate.c + app/src/parser.c + app/src/parser_impl.c + deps/ledger-zxlib/app/common/app_mode.c + ) + +add_library(app_lib STATIC + ${LIB_SRC} + ${JSMN_SRC} + ) + +target_include_directories(app_lib PUBLIC + deps/ledger-zxlib/include + deps/jsmn/src + app/src + app/src/common + deps/ledger-zxlib/app/common + ) + +############################################################## +############################################################## +# Tests +file(GLOB_RECURSE TESTS_SRC + ${CMAKE_CURRENT_SOURCE_DIR}/tests/*.cpp) + +add_executable(unittests ${TESTS_SRC}) +target_include_directories(unittests PUBLIC + ${gtest_SOURCE_DIR}/include + ${gmock_SOURCE_DIR}/include + ${CONAN_INCLUDE_DIRS_FMT} + ${CONAN_INCLUDE_DIRS_JSONCPP} + deps/jsmn/src + ) + +target_link_libraries(unittests PRIVATE + gtest_main + app_lib + CONAN_PKG::fmt + CONAN_PKG::jsoncpp) + +add_test(unittests ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/unittests) +set_tests_properties(unittests PROPERTIES WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/tests) + +file(GLOB_RECURSE FUZZING_SRC + ${CMAKE_CURRENT_SOURCE_DIR}/fuzzing/fuzzingMain.cpp + tests/util/common.cpp + ) + +add_executable(fuzzing_stub ${FUZZING_SRC}) +target_include_directories(fuzzing_stub PUBLIC + app/src + deps/jsmn/src + ) +target_link_libraries(fuzzing_stub app_lib) diff --git a/LICENSE b/LICENSE index c8e8e95a..0fa613e8 100644 --- a/LICENSE +++ b/LICENSE @@ -186,7 +186,7 @@ same "printed page" as the copyright notice for easier identification within third-party archives. - Copyright 2018 ZondaX GmbH + Copyright 2018-2020 Zondax GmbH Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. diff --git a/Makefile b/Makefile old mode 100755 new mode 100644 index 32faa76b..2a433b73 --- a/Makefile +++ b/Makefile @@ -1,153 +1,29 @@ #******************************************************************************* -# Ledger App -# (c) 2017 Ledger -# (c) 2018 ZondaX GmbH -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -#******************************************************************************* +#* (c) 2019 Zondax GmbH +#* +#* Licensed under the Apache License, Version 2.0 (the "License"); +#* you may not use this file except in compliance with the License. +#* You may obtain a copy of the License at +#* +#* http://www.apache.org/licenses/LICENSE-2.0 +#* +#* Unless required by applicable law or agreed to in writing, software +#* distributed under the License is distributed on an "AS IS" BASIS, +#* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +#* See the License for the specific language governing permissions and +#* limitations under the License. +#******************************************************************************** + +# We use BOLOS_SDK to determine the develoment environment that is being used +# BOLOS_SDK IS DEFINED We use the plain Makefile for Ledger +# BOLOS_SDK NOT DEFINED We use a containerized build approach ifeq ($(BOLOS_SDK),) -$(error BOLOS_SDK is not set) -endif -include $(BOLOS_SDK)/Makefile.defines - -# Main app configuration -APPNAME = "Cosmos" -APPVERSION_M=1 -APPVERSION_N=5 -APPVERSION_P=3 - -APP_LOAD_PARAMS = --appFlags 0x200 --delete $(COMMON_LOAD_PARAMS) --path "44'/118'" - -ifeq ($(TARGET_NAME),TARGET_NANOS) -SCRIPT_LD:=$(CURDIR)/script.ld -ICONNAME:=$(CURDIR)/nanos_icon.gif -endif - -ifeq ($(TARGET_NAME),TARGET_NANOX) -ICONNAME:=$(CURDIR)/nanox_icon.gif -endif - -ifndef ICONNAME -$(error ICONNAME is not set) -endif - -all: default - -############ -# Platform - -DEFINES += UNUSED\(x\)=\(void\)x -DEFINES += PRINTF\(...\)= - -APPVERSION=$(APPVERSION_M).$(APPVERSION_N).$(APPVERSION_P) -DEFINES += APPVERSION=\"$(APPVERSION)\" - -DEFINES += OS_IO_SEPROXYHAL -DEFINES += HAVE_BAGL HAVE_SPRINTF -DEFINES += HAVE_IO_USB HAVE_L4_USBLIB IO_USB_MAX_ENDPOINTS=7 IO_HID_EP_LENGTH=64 HAVE_USB_APDU - -DEFINES += LEDGER_MAJOR_VERSION=$(APPVERSION_M) LEDGER_MINOR_VERSION=$(APPVERSION_N) LEDGER_PATCH_VERSION=$(APPVERSION_P) - -DEFINES += HAVE_U2F HAVE_IO_U2F -DEFINES += U2F_PROXY_MAGIC=\"CSM\" -DEFINES += USB_SEGMENT_SIZE=64 -DEFINES += U2F_MAX_MESSAGE_SIZE=264 #257+5+2 -DEFINES += HAVE_BOLOS_APP_STACK_CANARY - -WEBUSB_URL = www.ledgerwallet.com -DEFINES += HAVE_WEBUSB WEBUSB_URL_SIZE_B=$(shell echo -n $(WEBUSB_URL) | wc -c) WEBUSB_URL=$(shell echo -n $(WEBUSB_URL) | sed -e "s/./\\\'\0\\\',/g") - -ifeq ($(TARGET_NAME),TARGET_NANOX) -DEFINES += IO_SEPROXYHAL_BUFFER_SIZE_B=300 - -DEFINES += HAVE_GLO096 -DEFINES += HAVE_BAGL BAGL_WIDTH=128 BAGL_HEIGHT=64 -DEFINES += HAVE_BAGL_ELLIPSIS # long label truncation feature -DEFINES += HAVE_BAGL_FONT_OPEN_SANS_REGULAR_11PX -DEFINES += HAVE_BAGL_FONT_OPEN_SANS_EXTRABOLD_11PX -DEFINES += HAVE_BAGL_FONT_OPEN_SANS_LIGHT_16PX - -DEFINES += HAVE_UX_FLOW - -#SDK_SOURCE_PATH += lib_blewbxx lib_blewbxx_impl -SDK_SOURCE_PATH += lib_ux -else -# Assume Nano S -DEFINES += IO_SEPROXYHAL_BUFFER_SIZE_B=128 -endif - -# X specific - -#Feature temporarily disabled -DEFINES += LEDGER_SPECIFIC -#DEFINES += TESTING_ENABLED - -# Compiler, assembler, and linker - -ifneq ($(BOLOS_ENV),) -$(info BOLOS_ENV=$(BOLOS_ENV)) -CLANGPATH := $(BOLOS_ENV)/clang-arm-fropi/bin/ -GCCPATH := $(BOLOS_ENV)/gcc-arm-none-eabi-5_3-2016q1/bin/ +include $(CURDIR)/deps/ledger-zxlib/dockerized_build.mk else -$(info BOLOS_ENV is not set: falling back to CLANGPATH and GCCPATH) -endif - -ifeq ($(CLANGPATH),) -$(info CLANGPATH is not set: clang will be used from PATH) -endif - -ifeq ($(GCCPATH),) -$(info GCCPATH is not set: arm-none-eabi-* will be used from PATH) +default: + $(MAKE) -C app +%: + $(info "Calling app Makefile for target $@") + $(MAKE) -C app $@ endif - -######################### - -CC := $(CLANGPATH)clang -CFLAGS += -O3 -Os - -AS := $(GCCPATH)arm-none-eabi-gcc -AFLAGS += - -LD := $(GCCPATH)arm-none-eabi-gcc -LDFLAGS += -O3 -Os -LDLIBS += -lm -lgcc -lc - -########################## -include $(BOLOS_SDK)/Makefile.glyphs - -APP_SOURCE_PATH += src deps/jsmn/src deps/ledger-zxlib/include deps/ledger-zxlib/src -SDK_SOURCE_PATH += lib_stusb lib_u2f lib_stusb_impl - -ifeq ($(TARGET_NAME),TARGET_NANOX) -#SDK_SOURCE_PATH += lib_blewbxx lib_blewbxx_impl -SDK_SOURCE_PATH += lib_ux -endif - -load: - python -m ledgerblue.loadApp $(APP_LOAD_PARAMS) - -delete: - python -m ledgerblue.deleteApp $(COMMON_DELETE_PARAMS) - -package: - ./pkgdemo.sh ${APPNAME} ${APPVERSION} ${ICONNAME} - -# Import generic rules from the SDK -include $(BOLOS_SDK)/Makefile.rules - -#add dependency on custom makefile filename -dep/%.d: %.c Makefile - -listvariants: - @echo VARIANTS COIN cosmos diff --git a/README.md b/README.md index 1441a217..78b7d8ed 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,277 @@ -# Tendermint/Cosmos: Ledger Nano S - User App +# Ledger Cosmos app [![License](https://img.shields.io/badge/License-Apache%202.0-blue.svg)](https://opensource.org/licenses/Apache-2.0) +[![CircleCI](https://circleci.com/gh/Zondax/ledger-cosmos.svg?style=shield)](https://circleci.com/gh/Zondax/ledger-cosmos) +[![CodeFactor](https://www.codefactor.io/repository/github/zondax/ledger-cosmos/badge)](https://www.codefactor.io/repository/github/zondax/ledger-cosmos) -This is a submodule that only contains the user app according to Ledger specs. +This project contains the Cosmos app for Ledger Nano S and X. -Please refer to the [Ledger-Cosmos](https://github.com/cosmos/ledger-cosmos) for the complete source code, build instructions, etc. (unit tests, integration tests, documentation, etc.) +- Ledger Nano S/X Cosmos app +- Specs / Documentation +- C++ unit tests +- Zemu tests + +The Cosmos app is already available in [Ledger Live](https://www.ledger.com/pages/ledger-live). Our preferred and recommended hardware wallet! + +- Open Ledger Live and go to Settings (gear icon on the right): + +![](docs/img/cosmos_app1.png) + +- Enable developer mode (last option): + +![](docs/img/cosmos_app2.png) + +- Now go back to manager and search for Cosmos: + +![](docs/img/cosmos_app3.png) + +## ATTENTION + +Please: + +- **Do not use in production** +- **Do not use a Ledger device with funds for development purposes.** +- **Have a separate and marked device that is used ONLY for development and testing** + +Tip: + +- In releases, you will find a precompiled test app. If you are just curious, you can run `zxtool.sh` and avoid building. + +## Download and install a prerelease + +*Once the app is approved by Ledger, it will be available in their app store (Ledger Live). +You can get builds generated by CircleCI from the release tab. THESE ARE UNVETTED DEVELOPMENT RELEASES* + +Download a release from here (https://github.com/Zondax/ledger-cosmos/releases). You only need `zxtool.sh` + +If the file is not executable, run +```sh +chmod +x ./zxtool.sh +``` + +then run: + +```sh +./zxtool.sh load +``` + +# Development + +## Preconditions + +- Be sure you checkout submodules too: + + ``` + git submodule update --init --recursive + ``` + +- Install Docker CE + - Instructions can be found here: https://docs.docker.com/install/ + +- We only officially support Ubuntu. Install the following packages: + ``` + sudo apt update && apt-get -y install build-essential git wget cmake \ + libssl-dev libgmp-dev autoconf libtool + ``` + +- Install `node > v13.0`. We typically recommend using `n` + +- You will need python 3 and then run + - `make deps` + +- This project requires Ledger firmware 1.6 + - The current repository keeps track of Ledger's SDK but it is possible to override it by changing the git submodule. + +*Warning*: Some IDEs may not use the same python interpreter or virtual enviroment as the one you used when running `pip`. +If you see conan is not found, check that you installed the package in the same interpreter as the one that launches `cmake`. + +## How to build ? + +> We like clion or vscode but let's have some reproducible command line steps +> + +- Building the app itself + + If you installed the what is described above, just run: + ```bash + make + ``` + +## Running tests + +- Running rust tests (x64) + + If you installed the what is described above, just run: + ```bash + make rust_test + ``` + +- Running C/C++ tests (x64) + + If you installed the what is described above, just run: + ```bash + make cpp_test + ``` + +- Running device emulation+integration tests!! + + ```bash + Use Zemu! Explained below! + ``` + +## How to test with Zemu? + +> What is Zemu?? Great you asked!! +> As part of this project, we are making public a beta version of our internal testing+emulation framework for Ledger apps. +> +> Npm Package here: https://www.npmjs.com/package/@zondax/zemu +> +> Repo here: https://github.com/Zondax/zemu + +Let's go! First install everything: +> At this moment, if you change the app you will need to run `make` before running the test again. + +```bash +make zemu_install +``` + +Then you can run JS tests: + +```bash +make zemu_test +``` + +To run a single specific test: + +> At the moment, the recommendation is to run from the IDE. Remember to run `make` if you change the app. + +## How to debug a ledger app? + +You can use vscode or clion to debug the app. We recommend using CLion but we provide a vscode (unsupported) configuration too. + +### Preconditions + +If you are using CLion, you need to a configuration file in your home directory: `$HOME/.gdbinit` with the following content: + +``` +set auto-load local-gdbinit on +add-auto-load-safe-path / +``` + +### Warnings + +There are a few things to take into account when enabling Ledger App debugging: + +- Once you enable the local .gdbinit that is located in your project workspace. You **will break** local Rust debugging in your host. The reason is that debugging unit tests will use the same `.gdbinit` configuration that sets the environment to ARM. We are looking at some possible fixes. For now, if you want to debug unit tests instead of the ledger app, you need to comment out the lines in `.gdbinit` + +### Debugging + +1. Build your app + + ```bash + make + ``` + +2. Define your debug scenario + + Open `tests/zemu/tools/debug.mjs` and look for the line: + + ```bash + /// TIP you can use zemu commands here to take the app ... + ``` + + You can adjust this code to get the emulator to trigger a breakpoint in your app: + - send clicks + - send APDUs, etc + +3. Launch the emulator in debug mode + + > If you didnt install Zemu yet (previous section), then run `make zemu_install` + + ```bash + make zemu_debug + ``` + + The emulator will launch and immediately stop. You should see a black window + +4. Configure Clion debugger + + Your configuration should look similar to this: + + ![image](docs/img/clion_debugging.png) + + Check that the path mappings are correct + +5. Start CLion debugger + + You will hit a breakpoint in main. + Add breakpoints in other places and continue. + + Enjoy :) + +## Using a real device + +### How to prepare your DEVELOPMENT! device: + +> You can use an emulated device for development. This is only required if you are using a physical device +> +> **Please do not use a Ledger device with funds for development purposes.** +>> +> **Have a separate and marked device that is used ONLY for development and testing** + + There are a few additional steps that increase reproducibility and simplify development: + +**1 - Ensure your device works in your OS** +- In Linux hosts it might be necessary to adjust udev rules, etc. + + Refer to Ledger documentation: https://support.ledger.com/hc/en-us/articles/115005165269-Fix-connection-issues + +**2 - Set a test mnemonic** + +Many of our integration tests expect the device to be configured with a known test mnemonic. + +- Plug your device while pressing the right button + +- Your device will show "Recovery" in the screen + +- Double click + +- Run `make dev_init`. This will take about 2 minutes. The device will be initialized to: + + ``` + PIN: 5555 + Mnemonic: equip will roof matter pink blind book anxiety banner elbow sun young + ``` + +**3 - Add a development certificate** + +- Plug your device while pressing the right button + +- Your device will show "Recovery" in the screen + +- Click both buttons at the same time + +- Enter your pin if necessary + +- Run `make dev_ca`. The device will receive a development certificate to avoid constant manual confirmations. + + +### Loading into your development device + +The Makefile will build the firmware in a docker container and leave the binary in the correct directory. + +- Build + + ``` + make # Builds the app + ``` + +- Upload to a device + The following command will upload the application to the ledger. _Warning: The application will be deleted before uploading._ + ``` + make load # Builds and loads the app to the device + ``` + +## APDU Specifications + +- [APDU Protocol](docs/APDUSPEC.md) +- [Transaction format](docs/TXSPEC.md) diff --git a/app/.gitignore b/app/.gitignore new file mode 100644 index 00000000..1cd0c2d7 --- /dev/null +++ b/app/.gitignore @@ -0,0 +1,57 @@ +# Prerequisites +*.d + +# Compiled Object files +*.slo +*.lo +*.o +*.obj + +# Precompiled Headers +*.gch +*.pch + +# Compiled Dynamic libraries +*.so +*.dylib +*.dll + +# Fortran module files +*.mod +*.smod + +# Compiled Static libraries +*.lai +*.la +*.a +*.lib + +# Executables +*.exe +*.out +*.app + +# OS related files +.DS_Store + +# Others +cmake-build-debug/ +../.idea/workspace.xml +\.idea/ +src/ledger/bin/ +src/ledger/debug/ +src/ledger/obj/ +obj/app\.elf +debug/app\.map +debug/app\.asm +bin/app\.hex +bin/app\.elf +app/src/common/glyphs.h +app/src/common/glyphs.c +bin/app.sha256 +bin/app.apdu + +pkg/bin/app.hex +pkg/zxtool.sh + +pkg/demo.zip diff --git a/app/LICENSE b/app/LICENSE new file mode 100644 index 00000000..dbd648b9 --- /dev/null +++ b/app/LICENSE @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright 2018 Zondax GmbH + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/app/Makefile b/app/Makefile new file mode 100755 index 00000000..18255b37 --- /dev/null +++ b/app/Makefile @@ -0,0 +1,175 @@ +#******************************************************************************* +# Ledger App +# (c) 2018-2020 Zondax GmbH +# (c) 2017 Ledger +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +#******************************************************************************* + +ifeq ($(BOLOS_SDK),) +$(error BOLOS_SDK is not set) +endif + +MY_DIR := $(dir $(lastword $(MAKEFILE_LIST))) + +include $(BOLOS_SDK)/Makefile.defines + +# Main app configuration +APPNAME = "Cosmos" +APPVERSION_M=2 +APPVERSION_N=16 +APPVERSION_P=0 + +APPPATH = "44'/118'" +APP_LOAD_PARAMS = --appFlags 0x200 --delete $(COMMON_LOAD_PARAMS) --path $(APPPATH) + +ifeq ($(TARGET_NAME),TARGET_NANOS) +SCRIPT_LD:=$(CURDIR)/script.ld +ICONNAME:=$(CURDIR)/nanos_icon.gif +endif + +ifeq ($(TARGET_NAME),TARGET_NANOX) +ICONNAME:=$(CURDIR)/nanox_icon.gif +endif + +ifndef ICONNAME +$(error ICONNAME is not set) +endif + +all: default + @echo "#!/usr/bin/env bash" > $(CURDIR)/pkg/zxtool.sh + @echo "APPNAME=\"${APPNAME}\"" >> $(CURDIR)/pkg/zxtool.sh + @echo "APPVERSION=\"${APPVERSION}\"" >> $(CURDIR)/pkg/zxtool.sh + @echo "APPPATH=\""${APPPATH}"\"" >> $(CURDIR)/pkg/zxtool.sh + @echo "LOAD_PARAMS=\"${COMMON_LOAD_PARAMS}\"" >> $(CURDIR)/pkg/zxtool.sh + @echo "DELETE_PARAMS=\"${COMMON_DELETE_PARAMS}\"" >> $(CURDIR)/pkg/zxtool.sh + @echo "APPHEX=\"" >> $(CURDIR)/pkg/zxtool.sh + @cat $(CURDIR)/bin/app.hex >> $(CURDIR)/pkg/zxtool.sh + @echo "\"" >> $(CURDIR)/pkg/zxtool.sh + @cat $(CURDIR)/../deps/ledger-zxlib/scripts/template.sh >> $(CURDIR)/pkg/zxtool.sh + @chmod +x $(CURDIR)/pkg/zxtool.sh + +############ +# Platform + +DEFINES += UNUSED\(x\)=\(void\)x +DEFINES += PRINTF\(...\)= + +APPVERSION=$(APPVERSION_M).$(APPVERSION_N).$(APPVERSION_P) +DEFINES += APPVERSION=\"$(APPVERSION)\" + +DEFINES += OS_IO_SEPROXYHAL +DEFINES += HAVE_BAGL HAVE_SPRINTF +DEFINES += HAVE_IO_USB HAVE_L4_USBLIB IO_USB_MAX_ENDPOINTS=7 IO_HID_EP_LENGTH=64 HAVE_USB_APDU + +DEFINES += LEDGER_MAJOR_VERSION=$(APPVERSION_M) LEDGER_MINOR_VERSION=$(APPVERSION_N) LEDGER_PATCH_VERSION=$(APPVERSION_P) + +DEFINES += USB_SEGMENT_SIZE=64 +DEFINES += HAVE_BOLOS_APP_STACK_CANARY +DEFINES += NDEBUG + +DEFINES += HAVE_WEBUSB WEBUSB_URL_SIZE_B=0 WEBUSB_URL="" + +ifeq ($(TARGET_NAME),TARGET_NANOX) +DEFINES += IO_SEPROXYHAL_BUFFER_SIZE_B=300 + +DEFINES += HAVE_GLO096 +DEFINES += HAVE_BAGL BAGL_WIDTH=128 BAGL_HEIGHT=64 +DEFINES += HAVE_BAGL_ELLIPSIS # long label truncation feature +DEFINES += HAVE_BAGL_FONT_OPEN_SANS_REGULAR_11PX +DEFINES += HAVE_BAGL_FONT_OPEN_SANS_EXTRABOLD_11PX +DEFINES += HAVE_BAGL_FONT_OPEN_SANS_LIGHT_16PX + +DEFINES += HAVE_UX_FLOW + +DEFINES += HAVE_BLE +DEFINES += HAVE_BLE_APDU BLE_COMMAND_TIMEOUT_MS=2000 + +SDK_SOURCE_PATH += lib_blewbxx lib_blewbxx_impl +else +# Assume Nano S +DEFINES += IO_SEPROXYHAL_BUFFER_SIZE_B=128 +DEFINES += COMPLIANCE_UX_160 HAVE_UX_LEGACY HAVE_UX_FLOW +endif + +# X specific + +#Feature temporarily disabled +DEFINES += LEDGER_SPECIFIC + +# Compiler, assembler, and linker + +ifneq ($(BOLOS_ENV),) +$(info BOLOS_ENV is $(BOLOS_ENV)) +CLANGPATH := $(BOLOS_ENV)/clang-arm-fropi/bin/ +GCCPATH := $(BOLOS_ENV)/gcc-arm-none-eabi-5_3-2016q1/bin/ +else +$(info BOLOS_ENV is not set: falling back to CLANGPATH and GCCPATH) +endif + +ifeq ($(CLANGPATH),) +$(info CLANGPATH is not set: clang will be used from PATH) +endif + +ifeq ($(GCCPATH),) +$(info GCCPATH is not set: arm-none-eabi-* will be used from PATH) +endif + +######################### + +CC := $(CLANGPATH)clang +#CFLAGS += -Werror +CFLAGS += -O3 -Os -Wpedantic -Wall -Wno-unknown-pragmas -Wno-c11-extensions +# To accept Ledger SDK issues +CFLAGS += -Wno-missing-declarations -Wno-empty-translation-unit -Wno-language-extension-token +CFLAGS += -Wno-embedded-directive -Wno-pointer-arith +CFLAGS += -Wno-typedef-redefinition -Wno-newline-eof + +AS := $(GCCPATH)arm-none-eabi-gcc +AFLAGS += + +LD := $(GCCPATH)arm-none-eabi-gcc +LDFLAGS += -O3 -Os +LDLIBS += -lm -lgcc -lc + +########################## +include $(BOLOS_SDK)/Makefile.glyphs + +APP_SOURCE_PATH += $(MY_DIR)/src +APP_SOURCE_PATH += $(MY_DIR)/../deps/ledger-zxlib/include +APP_SOURCE_PATH += $(MY_DIR)/../deps/ledger-zxlib/src +APP_SOURCE_PATH += $(MY_DIR)/../deps/ledger-zxlib/app/common +APP_SOURCE_PATH += $(MY_DIR)/../deps/jsmn/src + +SDK_SOURCE_PATH += lib_stusb lib_stusb_impl + +SDK_SOURCE_PATH += lib_ux + +# Import generic rules from the SDK +include $(BOLOS_SDK)/Makefile.rules + +#add dependency on custom makefile filename +dep/%.d: %.c Makefile + +rust: + # Do nothing + +# load, delete and listvariants are provided to comply with Ledger requirements +load: + python -m ledgerblue.loadApp $(APP_LOAD_PARAMS) + +delete: + python -m ledgerblue.deleteApp $(COMMON_DELETE_PARAMS) + +listvariants: + @echo VARIANTS COIN cosmos diff --git a/cosmos.png b/app/cosmos.png similarity index 100% rename from cosmos.png rename to app/cosmos.png diff --git a/glyphs/digit_dot.gif b/app/glyphs/digit_dot.gif similarity index 100% rename from glyphs/digit_dot.gif rename to app/glyphs/digit_dot.gif diff --git a/glyphs/icon_app.gif b/app/glyphs/icon_app.gif similarity index 100% rename from glyphs/icon_app.gif rename to app/glyphs/icon_app.gif diff --git a/glyphs/icon_back.gif b/app/glyphs/icon_back.gif similarity index 100% rename from glyphs/icon_back.gif rename to app/glyphs/icon_back.gif diff --git a/glyphs/icon_close.gif b/app/glyphs/icon_close.gif similarity index 100% rename from glyphs/icon_close.gif rename to app/glyphs/icon_close.gif diff --git a/glyphs/icon_crossmark.gif b/app/glyphs/icon_crossmark.gif similarity index 100% rename from glyphs/icon_crossmark.gif rename to app/glyphs/icon_crossmark.gif diff --git a/glyphs/icon_dashboard.gif b/app/glyphs/icon_dashboard.gif similarity index 100% rename from glyphs/icon_dashboard.gif rename to app/glyphs/icon_dashboard.gif diff --git a/glyphs/icon_eye.gif b/app/glyphs/icon_eye.gif similarity index 100% rename from glyphs/icon_eye.gif rename to app/glyphs/icon_eye.gif diff --git a/glyphs/icon_validate.gif b/app/glyphs/icon_validate.gif similarity index 100% rename from glyphs/icon_validate.gif rename to app/glyphs/icon_validate.gif diff --git a/glyphs/icon_validate_14.gif b/app/glyphs/icon_validate_14.gif similarity index 100% rename from glyphs/icon_validate_14.gif rename to app/glyphs/icon_validate_14.gif diff --git a/nanos_icon.gif b/app/nanos_icon.gif similarity index 100% rename from nanos_icon.gif rename to app/nanos_icon.gif diff --git a/nanox_icon.gif b/app/nanox_icon.gif similarity index 100% rename from nanox_icon.gif rename to app/nanox_icon.gif diff --git a/pkgdemo/.gitkeep b/app/pkg/.gitkeep similarity index 100% rename from pkgdemo/.gitkeep rename to app/pkg/.gitkeep diff --git a/script.ld b/app/script.ld similarity index 93% rename from script.ld rename to app/script.ld index e0bba982..b7e4b071 100644 --- a/script.ld +++ b/app/script.ld @@ -1,7 +1,7 @@ /******************************************************************************* * Ledger Blue - Secure firmware +* (c) 2019 Zondax GmbH * (c) 2016, 2017 Ledger -* (c) 2018 Zonda GmbH * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -30,7 +30,7 @@ MEMORY } PAGE_SIZE = 64; -STACK_SIZE = 860; +STACK_SIZE = 1212; END_STACK = ORIGIN(SRAM) + LENGTH(SRAM); SECTIONS @@ -52,7 +52,7 @@ SECTIONS /* ensure main is always @ 0xC0D00000 */ *(.boot*) - + /* place the other code and rodata defined BUT nvram variables that are displaced in a r/w area */ *(.text*) *(.rodata) @@ -60,29 +60,29 @@ SECTIONS *(.rodata.N[^_]*) . = ALIGN(4); - + /* all code placed */ _etext = .; . = ALIGN(PAGE_SIZE); _nvram_data = .; - + /* NVM data (ex-filesystem) */ *(.bss.N_* .rodata.N_*) . = ALIGN(PAGE_SIZE); + _envram_data = .; + _install_parameters = .; - PROVIDE(N_install_parameters = .); _envram = .; - _nvram_data_size = _envram - _nvram_data; - + } > FLASH = 0x00 .data (NOLOAD): { . = ALIGN(4); - + /** * Place RAM initialized variables */ @@ -112,9 +112,11 @@ SECTIONS app_stack_canary = .; PROVIDE(app_stack_canary = .); . += 4; - _stack = .; - . = _stack + STACK_SIZE; - PROVIDE( _stack_size = STACK_SIZE ); + _stack_validation = .; + . = _stack_validation + STACK_SIZE; + _stack = ABSOLUTE(END_STACK) - STACK_SIZE; + PROVIDE( _stack = ABSOLUTE(END_STACK) - STACK_SIZE); + _estack = ABSOLUTE(END_STACK); PROVIDE( _estack = ABSOLUTE(END_STACK) ); } > SRAM = 0x00 @@ -165,5 +167,5 @@ SECTIONS .debug_funcnames 0 : { *(.debug_funcnames) } .debug_typenames 0 : { *(.debug_typenames) } .debug_varnames 0 : { *(.debug_varnames) } - + } diff --git a/app/src/.gitignore b/app/src/.gitignore new file mode 100644 index 00000000..4bde63b2 --- /dev/null +++ b/app/src/.gitignore @@ -0,0 +1,48 @@ +# Prerequisites +*.d + +# Compiled Object files +*.slo +*.lo +*.o +*.obj + +# Precompiled Headers +*.gch +*.pch + +# Compiled Dynamic libraries +*.so +*.dylib +*.dll + +# Fortran module files +*.mod +*.smod + +# Compiled Static libraries +*.lai +*.la +*.a +*.lib + +# Executables +*.exe +*.out +*.app + +# OS related files +.DS_Store + +# Others +cmake-build-debug/ +\.idea/workspace\.xml +\.idea/ + +src/glyphs\.h +src/glyphs\.c +obj/ +bin/ +debug/ + +pkg/zxtool.sh diff --git a/app/src/apdu_handler.c b/app/src/apdu_handler.c new file mode 100644 index 00000000..67dd86ce --- /dev/null +++ b/app/src/apdu_handler.c @@ -0,0 +1,131 @@ +/******************************************************************************* +* (c) 2018, 2019 Zondax GmbH +* (c) 2016 Ledger +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +********************************************************************************/ + +#include "app_main.h" +#include "app_mode.h" + +#include +#include +#include + +#include "view.h" +#include "actions.h" +#include "tx.h" +#include "crypto.h" +#include "coin.h" +#include "zxmacros.h" +#include "parser_impl.h" + +__Z_INLINE void handleGetAddrSecp256K1(volatile uint32_t *flags, volatile uint32_t *tx, uint32_t rx) { + uint8_t len = extractHRP(rx, OFFSET_DATA); + extractHDPath(rx, OFFSET_DATA + 1 + len); + + uint8_t requireConfirmation = G_io_apdu_buffer[OFFSET_P1]; + + if (requireConfirmation) { + app_fill_address(addr_secp256k1); + view_address_show(addr_secp256k1); + *flags |= IO_ASYNCH_REPLY; + return; + } + + *tx = app_fill_address(addr_secp256k1); + THROW(APDU_CODE_OK); +} + +__Z_INLINE void handleSignSecp256K1(volatile uint32_t *flags, volatile uint32_t *tx, uint32_t rx) { + if (!process_chunk(tx, rx)) { + THROW(APDU_CODE_OK); + } + + // Put address in output buffer, we will use it to confirm source address + app_fill_address(addr_secp256k1); + parser_tx_obj.own_addr = (const char *)(G_io_apdu_buffer + VIEW_ADDRESS_OFFSET_SECP256K1); + + const char *error_msg = tx_parse(); + + if (error_msg != NULL) { + int error_msg_length = strlen(error_msg); + MEMCPY(G_io_apdu_buffer, error_msg, error_msg_length); + *tx += (error_msg_length); + THROW(APDU_CODE_DATA_INVALID); + } + + view_sign_show(); + *flags |= IO_ASYNCH_REPLY; +} + +void handleApdu(volatile uint32_t *flags, volatile uint32_t *tx, uint32_t rx) { + uint16_t sw = 0; + + BEGIN_TRY + { + TRY + { + if (G_io_apdu_buffer[OFFSET_CLA] != CLA) { + THROW(APDU_CODE_CLA_NOT_SUPPORTED); + } + + if (rx < APDU_MIN_LENGTH) { + THROW(APDU_CODE_WRONG_LENGTH); + } + + switch (G_io_apdu_buffer[OFFSET_INS]) { + case INS_GET_VERSION: { + handle_getversion(flags, tx, rx); + break; + } + + case INS_GET_ADDR_SECP256K1: { + handleGetAddrSecp256K1(flags, tx, rx); + break; + } + + case INS_SIGN_SECP256K1: { + handleSignSecp256K1(flags, tx, rx); + break; + } + + default: + THROW(APDU_CODE_INS_NOT_SUPPORTED); + } + } + CATCH(EXCEPTION_IO_RESET) + { + THROW(EXCEPTION_IO_RESET); + } + CATCH_OTHER(e) + { + switch (e & 0xF000) { + case 0x6000: + case APDU_CODE_OK: + sw = e; + break; + default: + sw = 0x6800 | (e & 0x7FF); + break; + } + G_io_apdu_buffer[*tx] = sw >> 8; + G_io_apdu_buffer[*tx + 1] = sw; + *tx += 2; + } + FINALLY + { + } + } + END_TRY; +} diff --git a/app/src/coin.h b/app/src/coin.h new file mode 100644 index 00000000..45cfa261 --- /dev/null +++ b/app/src/coin.h @@ -0,0 +1,59 @@ +/******************************************************************************* +* (c) 2019 Zondax GmbH +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +********************************************************************************/ +#pragma once + +#ifdef __cplusplus +extern "C" { +#endif + +#include +#include + +#define CLA 0x55 + +#define HDPATH_LEN_DEFAULT 5 + +#define HDPATH_0_DEFAULT (0x80000000u | 0x2cu) +#define HDPATH_1_DEFAULT (0x80000000u | 0x76u) +#define HDPATH_2_DEFAULT (0x80000000u | 0u) +#define HDPATH_3_DEFAULT (0u) +#define HDPATH_4_DEFAULT (0u) + +#define PK_LEN_SECP256K1 33u + +typedef enum { + addr_secp256k1 = 0, +} address_kind_e; + +#define VIEW_ADDRESS_OFFSET_SECP256K1 PK_LEN_SECP256K1 +#define VIEW_ADDRESS_LAST_PAGE_DEFAULT 0 + +#define MENU_MAIN_APP_LINE1 "Cosmos" +#define MENU_MAIN_APP_LINE2 "ready" +#define APPVERSION_LINE1 "Version:" +#define APPVERSION_LINE2 ("v" APPVERSION) + +#define CRYPTO_BLOB_SKIP_BYTES 0 +#define COIN_DEFAULT_CHAINID "cosmoshub-3" + +// In non-expert mode, the app will convert from uatom to ATOM +#define COIN_DEFAULT_DENOM_BASE "uatom" +#define COIN_DEFAULT_DENOM_REPR "ATOM" +#define COIN_DEFAULT_DENOM_FACTOR 6 + +#ifdef __cplusplus +} +#endif diff --git a/app/src/common/actions.c b/app/src/common/actions.c new file mode 100644 index 00000000..ee8e1983 --- /dev/null +++ b/app/src/common/actions.c @@ -0,0 +1,45 @@ +/******************************************************************************* +* (c) 2016 Ledger +* (c) 2019 Zondax GmbH +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +********************************************************************************/ + +#include "actions.h" +#include "crypto.h" +#include "tx.h" +#include "apdu_codes.h" +#include +#include "coin.h" + +uint8_t action_addr_len; + +void app_sign() { + uint8_t *signature = G_io_apdu_buffer; + + const uint8_t *message = tx_get_buffer() + CRYPTO_BLOB_SKIP_BYTES; + const uint16_t messageLength = tx_get_buffer_length() - CRYPTO_BLOB_SKIP_BYTES; + + const uint8_t replyLen = crypto_sign(signature, IO_APDU_BUFFER_SIZE - 3, message, messageLength); + if (replyLen > 0) { + set_code(G_io_apdu_buffer, replyLen, APDU_CODE_OK); + io_exchange(CHANNEL_APDU | IO_RETURN_AFTER_TX, replyLen + 2); + } else { + set_code(G_io_apdu_buffer, 0, APDU_CODE_SIGN_VERIFY_ERROR); + io_exchange(CHANNEL_APDU | IO_RETURN_AFTER_TX, 2); + } +} + +void app_set_hrp(char *p) { + crypto_set_hrp(p); +} diff --git a/app/src/common/actions.h b/app/src/common/actions.h new file mode 100644 index 00000000..c57c79ec --- /dev/null +++ b/app/src/common/actions.h @@ -0,0 +1,55 @@ +/******************************************************************************* +* (c) 2019 Zondax GmbH +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +********************************************************************************/ +#pragma once + +#include +#include "crypto.h" +#include "tx.h" +#include "apdu_codes.h" +#include +#include "coin.h" + +void app_sign(); + +void app_set_hrp(char *p); + +extern uint8_t action_addr_len; + +__Z_INLINE uint8_t app_fill_address(address_kind_e kind) { + // Put data directly in the apdu buffer + MEMZERO(G_io_apdu_buffer, IO_APDU_BUFFER_SIZE); + + switch (kind) { + case addr_secp256k1: + action_addr_len = crypto_fillAddress(G_io_apdu_buffer, IO_APDU_BUFFER_SIZE - 2); + break; + default: + action_addr_len = 0; + break; + } + + return action_addr_len; +} + +__Z_INLINE void app_reply_address(address_kind_e kind) { + set_code(G_io_apdu_buffer, action_addr_len, APDU_CODE_OK); + io_exchange(CHANNEL_APDU | IO_RETURN_AFTER_TX, action_addr_len + 2); +} + +__Z_INLINE void app_reply_error() { + set_code(G_io_apdu_buffer, 0, APDU_CODE_DATA_INVALID); + io_exchange(CHANNEL_APDU | IO_RETURN_AFTER_TX, 2); +} diff --git a/app/src/common/app_main.c b/app/src/common/app_main.c new file mode 100644 index 00000000..e31ae9e7 --- /dev/null +++ b/app/src/common/app_main.c @@ -0,0 +1,254 @@ +/******************************************************************************* +* (c) 2018, 2019 Zondax GmbH +* (c) 2016 Ledger +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +********************************************************************************/ + +#include "app_main.h" +#include "app_mode.h" + +#include +#include +#include + +#include "view.h" +#include "actions.h" +#include "tx.h" +#include "crypto.h" +#include "coin.h" +#include "zxmacros.h" + +unsigned char G_io_seproxyhal_spi_buffer[IO_SEPROXYHAL_BUFFER_SIZE_B]; + +unsigned char io_event(unsigned char channel) { + switch (G_io_seproxyhal_spi_buffer[0]) { + case SEPROXYHAL_TAG_FINGER_EVENT: // + UX_FINGER_EVENT(G_io_seproxyhal_spi_buffer); + break; + + case SEPROXYHAL_TAG_BUTTON_PUSH_EVENT: // for Nano S + UX_BUTTON_PUSH_EVENT(G_io_seproxyhal_spi_buffer); + break; + + case SEPROXYHAL_TAG_DISPLAY_PROCESSED_EVENT: + if (!UX_DISPLAYED()) + UX_DISPLAYED_EVENT(); + break; + + case SEPROXYHAL_TAG_TICKER_EVENT: { // + UX_TICKER_EVENT(G_io_seproxyhal_spi_buffer, { + if (UX_ALLOWED) { + UX_REDISPLAY(); + } + }); + break; + } + + // unknown events are acknowledged + default: + UX_DEFAULT_EVENT(); + break; + } + if (!io_seproxyhal_spi_is_status_sent()) { + io_seproxyhal_general_status(); + } + return 1; // DO NOT reset the current APDU transport +} + +unsigned short io_exchange_al(unsigned char channel, unsigned short tx_len) { + switch (channel & ~(IO_FLAGS)) { + case CHANNEL_KEYBOARD: + break; + + // multiplexed io exchange over a SPI channel and TLV encapsulated protocol + case CHANNEL_SPI: + if (tx_len) { + io_seproxyhal_spi_send(G_io_apdu_buffer, tx_len); + + if (channel & IO_RESET_AFTER_REPLIED) { + reset(); + } + return 0; // nothing received from the master so far (it's a tx + // transaction) + } else { + return io_seproxyhal_spi_recv(G_io_apdu_buffer, sizeof(G_io_apdu_buffer), 0); + } + + default: + THROW(INVALID_PARAMETER); + } + return 0; +} + +void extractHDPath(uint32_t rx, uint32_t offset) { + if ((rx - offset) < sizeof(uint32_t) * HDPATH_LEN_DEFAULT) { + THROW(APDU_CODE_WRONG_LENGTH); + } + + MEMCPY(hdPath, G_io_apdu_buffer + offset, sizeof(uint32_t) * HDPATH_LEN_DEFAULT); + + // Check values + if (hdPath[0] != HDPATH_0_DEFAULT || + hdPath[1] != HDPATH_1_DEFAULT || + hdPath[3] != HDPATH_3_DEFAULT) { + THROW(APDU_CODE_DATA_INVALID); + } + + // Limit values unless the app is running in expert mode + if (!app_mode_expert()) { + for(int i=2; i < HDPATH_LEN_DEFAULT; i++) { + // hardened or unhardened values should be below 20 + if ( (hdPath[i] & 0x7FFFFFFF) > 100) THROW(APDU_CODE_CONDITIONS_NOT_SATISFIED); + } + } +} + +bool process_chunk(volatile uint32_t *tx, uint32_t rx) { + const uint8_t payloadType = G_io_apdu_buffer[OFFSET_PAYLOAD_TYPE]; + + if (G_io_apdu_buffer[OFFSET_P2] != 0) { + THROW(APDU_CODE_INVALIDP1P2); + } + + if (rx < OFFSET_DATA) { + THROW(APDU_CODE_WRONG_LENGTH); + } + + uint32_t added; + switch (payloadType) { + case 0: + tx_initialize(); + tx_reset(); + extractHDPath(rx, OFFSET_DATA); + return false; + case 1: + added = tx_append(&(G_io_apdu_buffer[OFFSET_DATA]), rx - OFFSET_DATA); + if (added != rx - OFFSET_DATA) { + THROW(APDU_CODE_OUTPUT_BUFFER_TOO_SMALL); + } + return false; + case 2: + added = tx_append(&(G_io_apdu_buffer[OFFSET_DATA]), rx - OFFSET_DATA); + if (added != rx - OFFSET_DATA) { + THROW(APDU_CODE_OUTPUT_BUFFER_TOO_SMALL); + } + return true; + } + + THROW(APDU_CODE_INVALIDP1P2); +} + +void handle_generic_apdu(volatile uint32_t *flags, volatile uint32_t *tx, uint32_t rx) { + if (rx > 4 && os_memcmp(G_io_apdu_buffer, "\xE0\x01\x00\x00", 4) == 0) { + // Respond to get device info command + uint8_t *p = G_io_apdu_buffer; + // Target ID 4 bytes + p[0] = (TARGET_ID >> 24) & 0xFF; + p[1] = (TARGET_ID >> 16) & 0xFF; + p[2] = (TARGET_ID >> 8) & 0xFF; + p[3] = (TARGET_ID >> 0) & 0xFF; + p += 4; + // SE Version [length][non-terminated string] + *p = os_version(p + 1, 64); + p = p + 1 + *p; + // Flags [length][flags] + *p = 0; + p++; + // MCU Version [length][non-terminated string] + *p = os_seph_version(p + 1, 64); + p = p + 1 + *p; + + *tx = p - G_io_apdu_buffer; + THROW(APDU_CODE_OK); + } +} + +void app_init() { + io_seproxyhal_init(); + +#ifdef TARGET_NANOX + // grab the current plane mode setting + G_io_app.plane_mode = os_setting_get(OS_SETTING_PLANEMODE, NULL, 0); +#endif // TARGET_NANOX + + USB_power(0); + USB_power(1); + + app_mode_reset(); + if (app_mode_expert()) { + view_idle_show(1); + } + + view_idle_show(0); + +#ifdef HAVE_BLE + // Enable Bluetooth + BLE_power(0, NULL); + BLE_power(1, "Nano X"); +#endif // HAVE_BLE +} + +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wmissing-noreturn" + +void app_main() { + volatile uint32_t rx = 0, tx = 0, flags = 0; + + for (;;) { + volatile uint16_t sw = 0; + + BEGIN_TRY; + { + TRY; + { + rx = tx; + tx = 0; + + rx = io_exchange(CHANNEL_APDU | flags, rx); + flags = 0; + CHECK_APP_CANARY() + + if (rx == 0) + THROW(APDU_CODE_EMPTY_BUFFER); + + // NOTE: Requested by Ledger +// handle_generic_apdu(&flags, &tx, rx); +// CHECK_APP_CANARY() + + handleApdu(&flags, &tx, rx); + CHECK_APP_CANARY() + } + CATCH_OTHER(e); + { + switch (e & 0xF000) { + case 0x6000: + case 0x9000: + sw = e; + break; + default: + sw = 0x6800 | (e & 0x7FF); + break; + } + G_io_apdu_buffer[tx] = sw >> 8; + G_io_apdu_buffer[tx + 1] = sw; + tx += 2; + } + FINALLY; + {} + } + END_TRY; + } +} + +#pragma clang diagnostic pop diff --git a/app/src/common/app_main.h b/app/src/common/app_main.h new file mode 100644 index 00000000..c645535d --- /dev/null +++ b/app/src/common/app_main.h @@ -0,0 +1,65 @@ +/******************************************************************************* +* (c) 2016 Ledger +* (c) 2018 Zondax GmbH +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +********************************************************************************/ +#pragma once + +#include +#include "apdu_codes.h" + +#define OFFSET_CLA 0 +#define OFFSET_INS 1 //< Instruction offset +#define OFFSET_P1 2 //< P1 +#define OFFSET_P2 3 //< P2 +#define OFFSET_DATA_LEN 4 //< Data Length +#define OFFSET_DATA 5 //< Data offset + +#define APDU_MIN_LENGTH 5 + +#define OFFSET_PAYLOAD_TYPE OFFSET_P1 + +#define INS_GET_VERSION 0x00 +#define INS_SIGN_SECP256K1 0x02 +#define INS_GET_ADDR_SECP256K1 0x04 + +void app_init(); + +void app_main(); + +void extractHDPath(uint32_t rx, uint32_t offset); + +bool process_chunk(volatile uint32_t *tx, uint32_t rx); + +void handleApdu(volatile uint32_t *flags, volatile uint32_t *tx, uint32_t rx); + +__Z_INLINE void handle_getversion(volatile uint32_t *flags, volatile uint32_t *tx, uint32_t rx) { +#ifdef DEBUG + G_io_apdu_buffer[0] = 0xFF; +#else + G_io_apdu_buffer[0] = 0; +#endif + G_io_apdu_buffer[1] = LEDGER_MAJOR_VERSION; + G_io_apdu_buffer[2] = LEDGER_MINOR_VERSION; + G_io_apdu_buffer[3] = LEDGER_PATCH_VERSION; + G_io_apdu_buffer[4] = !IS_UX_ALLOWED; + + G_io_apdu_buffer[5] = (TARGET_ID >> 24) & 0xFF; + G_io_apdu_buffer[6] = (TARGET_ID >> 16) & 0xFF; + G_io_apdu_buffer[7] = (TARGET_ID >> 8) & 0xFF; + G_io_apdu_buffer[8] = (TARGET_ID >> 0) & 0xFF; + + *tx += 9; + THROW(APDU_CODE_OK); +} diff --git a/src/main.c b/app/src/common/main.c similarity index 97% rename from src/main.c rename to app/src/common/main.c index 541f648a..87150f07 100644 --- a/src/main.c +++ b/app/src/common/main.c @@ -1,6 +1,6 @@ /******************************************************************************* * (c) 2016 Ledger -* (c) 2018 ZondaX GmbH +* (c) 2018, 2019 Zondax GmbH * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/app/src/common/parser.h b/app/src/common/parser.h new file mode 100644 index 00000000..eb8b9d95 --- /dev/null +++ b/app/src/common/parser.h @@ -0,0 +1,49 @@ +/******************************************************************************* +* (c) 2019 Zondax GmbH +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +********************************************************************************/ + +#pragma once + +#ifdef __cplusplus +extern "C" { +#endif + +#include "parser_impl.h" +#include "hexutils.h" +#include "crypto.h" + +const char *parser_getErrorDescription(parser_error_t err); + +//// parses a tx buffer +parser_error_t parser_parse(parser_context_t *ctx, + const uint8_t *data, + size_t dataLen); + +//// verifies tx fields +parser_error_t parser_validate(const parser_context_t *ctx); + +//// returns the number of items in the current parsing context +parser_error_t parser_getNumItems(const parser_context_t *ctx, uint8_t *num_items); + +// retrieves a readable output for each field / page +parser_error_t parser_getItem(const parser_context_t *ctx, + uint16_t displayIdx, + char *outKey, uint16_t outKeyLen, + char *outValue, uint16_t outValueLen, + uint8_t pageIdx, uint8_t *pageCount); + +#ifdef __cplusplus +} +#endif diff --git a/app/src/common/parser_common.h b/app/src/common/parser_common.h new file mode 100644 index 00000000..ce23e1ac --- /dev/null +++ b/app/src/common/parser_common.h @@ -0,0 +1,76 @@ +/******************************************************************************* +* (c) 2019 Zondax GmbH +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +********************************************************************************/ +#pragma once + +#ifdef __cplusplus +extern "C" { +#endif + +#include +#include + +#define CHECK_PARSER_ERR(__CALL) { \ + parser_error_t __err = __CALL; \ + CHECK_APP_CANARY() \ + if (__err!=parser_ok) return __err;} + +typedef enum { + // Generic errors + parser_ok = 0, + parser_no_data, + parser_init_context_empty, + parser_display_idx_out_of_range, + parser_display_page_out_of_range, + parser_unexpected_error, + // Coin generic + parser_unexpected_type, + parser_unexpected_method, + parser_unexpected_buffer_end, + parser_unexpected_value, + parser_unexpected_number_items, + parser_unexpected_version, + parser_unexpected_characters, + parser_unexpected_field, + parser_duplicated_field, + parser_value_out_of_range, + parser_invalid_address, + parser_unexpected_chain, + parser_missing_field, + parser_query_no_results, + // Coin Specific + parser_json_zero_tokens, + parser_json_too_many_tokens, // "NOMEM: JSON string contains too many tokens" + parser_json_incomplete_json, // "JSON string is not complete"; + parser_json_contains_whitespace, + parser_json_is_not_sorted, + parser_json_missing_chain_id, + parser_json_missing_sequence, + parser_json_missing_fee, + parser_json_missing_msgs, + parser_json_missing_account_number, + parser_json_missing_memo, + parser_json_unexpected_error, +} parser_error_t; + +typedef struct { + const uint8_t *buffer; + uint16_t bufferLen; + uint16_t offset; +} parser_context_t; + +#ifdef __cplusplus +} +#endif diff --git a/app/src/common/tx.c b/app/src/common/tx.c new file mode 100644 index 00000000..ef567c48 --- /dev/null +++ b/app/src/common/tx.c @@ -0,0 +1,138 @@ +/******************************************************************************* +* (c) 2019 Zondax GmbH +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +********************************************************************************/ + +#include "tx.h" +#include "apdu_codes.h" +#include "buffering.h" +#include "parser.h" +#include +#include "zxmacros.h" + +#if defined(TARGET_NANOX) +#define RAM_BUFFER_SIZE 8192 +#define FLASH_BUFFER_SIZE 16384 +#elif defined(TARGET_NANOS) +#define RAM_BUFFER_SIZE 256 +#define FLASH_BUFFER_SIZE 8192 +#endif + +// Ram +uint8_t ram_buffer[RAM_BUFFER_SIZE]; + +// Flash +typedef struct { + uint8_t buffer[FLASH_BUFFER_SIZE]; +} storage_t; + +#if defined(TARGET_NANOS) +storage_t N_appdata_impl __attribute__ ((aligned(64))); +#define N_appdata (*(storage_t *)PIC(&N_appdata_impl)) + +#elif defined(TARGET_NANOX) +storage_t const N_appdata_impl __attribute__ ((aligned(64))); +#define N_appdata (*(volatile storage_t *)PIC(&N_appdata_impl)) +#endif + +parser_context_t ctx_parsed_tx; + +void tx_initialize() { + buffering_init( + ram_buffer, + sizeof(ram_buffer), + N_appdata.buffer, + sizeof(N_appdata.buffer) + ); +} + +void tx_reset() { + buffering_reset(); +} + +uint32_t tx_append(unsigned char *buffer, uint32_t length) { + return buffering_append(buffer, length); +} + +uint32_t tx_get_buffer_length() { + return buffering_get_buffer()->pos; +} + +uint8_t *tx_get_buffer() { + return buffering_get_buffer()->data; +} + +const char *tx_parse() { + uint8_t err = parser_parse( + &ctx_parsed_tx, + tx_get_buffer(), + tx_get_buffer_length()); + + if (err != parser_ok) { + return parser_getErrorDescription(err); + } + + err = parser_validate(&ctx_parsed_tx); + CHECK_APP_CANARY() + + if (err != parser_ok) { + return parser_getErrorDescription(err); + } + + return NULL; +} + +tx_error_t tx_getNumItems(uint8_t *num_items) { + parser_error_t err = parser_getNumItems(&ctx_parsed_tx, num_items); + + if (err != parser_ok) { + return tx_no_data; + } + + return tx_no_error; +} + +tx_error_t tx_getItem(uint8_t displayIdx, + char *outKey, uint16_t outKeyLen, + char *outVal, uint16_t outValLen, + uint8_t pageIdx, uint8_t *pageCount) { + tx_error_t err = tx_no_error; + + uint8_t numItems = 0; + err = tx_getNumItems(&numItems); + if (err != tx_no_error) { + return err; + } + + if (displayIdx < 0 || displayIdx > numItems) { + return tx_no_data; + } + + err = (tx_error_t) parser_getItem(&ctx_parsed_tx, + displayIdx, + outKey, outKeyLen, + outVal, outValLen, + pageIdx, pageCount); + + // Convert error codes + if (err == parser_no_data || + err == parser_display_idx_out_of_range || + err == parser_display_page_out_of_range) + return tx_no_data; + + if (err == parser_ok) + return tx_no_error; + + return err; +} diff --git a/src/lib/transaction.h b/app/src/common/tx.h similarity index 64% rename from src/lib/transaction.h rename to app/src/common/tx.h index 0ccf023b..d5df157c 100644 --- a/src/lib/transaction.h +++ b/app/src/common/tx.h @@ -1,5 +1,5 @@ /******************************************************************************* -* (c) ZondaX GmbH +* (c) 2019 Zondax GmbH * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -16,28 +16,43 @@ #pragma once #include "os.h" +#include "coin.h" -void transaction_initialize(); +typedef enum { + tx_no_error = 0, + tx_no_data = 1, +} tx_error_t; + +void tx_initialize(); /// Clears the transaction buffer -void transaction_reset(); +void tx_reset(); /// Appends buffer to the end of the current transaction buffer /// Transaction buffer will grow until it reaches the maximum allowed size /// \param buffer /// \param length /// \return It returns an error message if the buffer is too small. -uint32_t transaction_append(unsigned char *buffer, uint32_t length); +uint32_t tx_append(unsigned char *buffer, uint32_t length); /// Returns size of the raw json transaction buffer /// \return -uint32_t transaction_get_buffer_length(); +uint32_t tx_get_buffer_length(); /// Returns the raw json transaction buffer /// \return -uint8_t *transaction_get_buffer(); +uint8_t *tx_get_buffer(); -/// Parse json message stored in transaction buffer +/// Parse message stored in transaction buffer /// This function should be called as soon as full buffer data is loaded. /// \return It returns NULL if json is valid or error message otherwise. -const char *transaction_parse(); +const char *tx_parse(); + +/// Return the number of items in the transaction +tx_error_t tx_getNumItems(uint8_t *num_items); + +/// Gets an specific item from the transaction (including paging) +tx_error_t tx_getItem(uint8_t displayIdx, + char *outKey, uint16_t outKeyLen, + char *outValue, uint16_t outValueLen, + uint8_t pageIdx, uint8_t *pageCount); diff --git a/app/src/crypto.c b/app/src/crypto.c new file mode 100644 index 00000000..5722c51e --- /dev/null +++ b/app/src/crypto.c @@ -0,0 +1,191 @@ +/******************************************************************************* +* (c) 2019 Zondax GmbH +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +********************************************************************************/ + +#include "crypto.h" +#include "coin.h" +#include "zxmacros.h" +#include "apdu_codes.h" + +#include + +uint32_t hdPath[HDPATH_LEN_DEFAULT]; + +uint8_t bech32_hrp_len; +char bech32_hrp[MAX_BECH32_HRP_LEN + 1]; + +#if defined(TARGET_NANOS) || defined(TARGET_NANOX) +#include "cx.h" + +void crypto_extractPublicKey(const uint32_t path[HDPATH_LEN_DEFAULT], uint8_t *pubKey, uint16_t pubKeyLen) { + cx_ecfp_public_key_t cx_publicKey; + cx_ecfp_private_key_t cx_privateKey; + uint8_t privateKeyData[32]; + + if (pubKeyLen < PK_LEN_SECP256K1) { + return; + } + + BEGIN_TRY + { + TRY { + // Generate keys + os_perso_derive_node_bip32(CX_CURVE_256K1, + path, + HDPATH_LEN_DEFAULT, + privateKeyData, + NULL); + + cx_ecfp_init_private_key(CX_CURVE_256K1, privateKeyData, 32, &cx_privateKey); + cx_ecfp_init_public_key(CX_CURVE_256K1, NULL, 0, &cx_publicKey); + cx_ecfp_generate_pair(CX_CURVE_256K1, &cx_publicKey, &cx_privateKey, 1); + } + FINALLY { + MEMZERO(&cx_privateKey, sizeof(cx_privateKey)); + MEMZERO(privateKeyData, 32); + } + } + END_TRY; + + // Format pubkey + for (int i = 0; i < 32; i++) { + pubKey[i] = cx_publicKey.W[64 - i]; + } + cx_publicKey.W[0] = cx_publicKey.W[64] & 1 ? 0x03 : 0x02; // "Compress" public key in place + if ((cx_publicKey.W[32] & 1) != 0) { + pubKey[31] |= 0x80; + } + ////////////////////// + MEMCPY(pubKey, cx_publicKey.W, PK_LEN_SECP256K1); +} + +uint16_t crypto_sign(uint8_t *signature, + uint16_t signatureMaxlen, + const uint8_t *message, + uint16_t messageLen) { + uint8_t messageDigest[CX_SHA256_SIZE]; + + // Hash it + cx_hash_sha256(message, messageLen, messageDigest, CX_SHA256_SIZE); + + cx_ecfp_private_key_t cx_privateKey; + uint8_t privateKeyData[32]; + int signatureLength; + unsigned int info = 0; + + BEGIN_TRY + { + TRY + { + // Generate keys + os_perso_derive_node_bip32(CX_CURVE_256K1, + hdPath, + HDPATH_LEN_DEFAULT, + privateKeyData, NULL); + + cx_ecfp_init_private_key(CX_CURVE_256K1, privateKeyData, 32, &cx_privateKey); + + // Sign + signatureLength = cx_ecdsa_sign(&cx_privateKey, + CX_RND_RFC6979 | CX_LAST, + CX_SHA256, + messageDigest, + CX_SHA256_SIZE, + signature, + signatureMaxlen, + &info); + } + FINALLY { + MEMZERO(&cx_privateKey, sizeof(cx_privateKey)); + MEMZERO(privateKeyData, 32); + } + } + END_TRY; + + return signatureLength; +} + +#else + +void crypto_extractPublicKey(const uint32_t path[HDPATH_LEN_DEFAULT], uint8_t *pubKey, uint16_t pubKeyLen) { + /////////////////////////////////////// + // THIS IS ONLY USED FOR TEST PURPOSES + /////////////////////////////////////// + + // Empty version for non-Ledger devices + MEMZERO(pubKey, pubKeyLen); +} + +uint16_t crypto_sign(uint8_t *signature, + uint16_t signatureMaxlen, + const uint8_t *message, + uint16_t messageLen) { + // Empty version for non-Ledger devices + return 0; +} + +#endif + +uint8_t extractHRP(uint32_t rx, uint32_t offset) { + if (rx < offset + 1) { + THROW(APDU_CODE_DATA_INVALID); + } + MEMZERO(bech32_hrp, MAX_BECH32_HRP_LEN); + + bech32_hrp_len = G_io_apdu_buffer[offset]; + + if (bech32_hrp_len == 0 || bech32_hrp_len > MAX_BECH32_HRP_LEN) { + THROW(APDU_CODE_DATA_INVALID); + } + + memcpy(bech32_hrp, G_io_apdu_buffer + offset + 1, bech32_hrp_len); + bech32_hrp[bech32_hrp_len] = 0; // zero terminate + + return bech32_hrp_len; +} + +void ripemd160_32(uint8_t *out, uint8_t *in) { + cx_ripemd160_t rip160; + cx_ripemd160_init(&rip160); + cx_hash(&rip160.header, CX_LAST, in, CX_SHA256_SIZE, out, CX_RIPEMD160_SIZE); +} + +void crypto_set_hrp(char *p) { + bech32_hrp_len = strlen(p); + if (bech32_hrp_len < MAX_BECH32_HRP_LEN) { + strcpy(bech32_hrp, p); + } +} + +uint16_t crypto_fillAddress(uint8_t *buffer, uint16_t buffer_len) { + if (buffer_len < PK_LEN_SECP256K1 + 50) { + return 0; + } + + // extract pubkey + crypto_extractPublicKey(hdPath, buffer, buffer_len); + + // Hash it + uint8_t hashed1_pk[CX_SHA256_SIZE]; + cx_hash_sha256(buffer, PK_LEN_SECP256K1, hashed1_pk, CX_SHA256_SIZE); + + uint8_t hashed2_pk[CX_RIPEMD160_SIZE]; + ripemd160_32(hashed2_pk, hashed1_pk); + + char *addr = (char *) (buffer + PK_LEN_SECP256K1); + bech32EncodeFromBytes(addr, buffer_len - PK_LEN_SECP256K1, bech32_hrp, hashed2_pk, CX_RIPEMD160_SIZE, 1); + + return PK_LEN_SECP256K1 + strlen(addr); +} diff --git a/app/src/crypto.h b/app/src/crypto.h new file mode 100644 index 00000000..5c61f8cb --- /dev/null +++ b/app/src/crypto.h @@ -0,0 +1,44 @@ +/******************************************************************************* +* (c) 2019 Zondax GmbH +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +********************************************************************************/ + +#pragma once + +#include +#include "coin.h" + +#ifdef __cplusplus +extern "C" { +#endif + +#define MAX_BECH32_HRP_LEN 83u + +extern uint32_t hdPath[HDPATH_LEN_DEFAULT]; +extern char *hrp; + +uint8_t extractHRP(uint32_t rx, uint32_t offset); + +void crypto_set_hrp(char *p); + +uint16_t crypto_fillAddress(uint8_t *buffer, uint16_t bufferLen); + +uint16_t crypto_sign(uint8_t *signature, + uint16_t signatureMaxlen, + const uint8_t *message, + uint16_t messageLen); + +#ifdef __cplusplus +} +#endif diff --git a/app/src/json/json_parser.c b/app/src/json/json_parser.c new file mode 100644 index 00000000..0763fd1a --- /dev/null +++ b/app/src/json/json_parser.c @@ -0,0 +1,254 @@ +/******************************************************************************* +* (c) 2018, 2019 Zondax GmbH +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +********************************************************************************/ + +#include +#include +#include +#include "json_parser.h" + +#define EQUALS(_P, _Q, _LEN) (MEMCMP( PIC(_P), PIC(_Q), (_LEN))==0) + +parser_error_t json_parse(parsed_json_t *parsed_json, const char *buffer, uint16_t bufferLen) { + jsmn_parser parser; + jsmn_init(&parser); + + MEMZERO(parsed_json, sizeof(parsed_json_t)); + parsed_json->buffer = buffer; + parsed_json->bufferLen = bufferLen; + + int32_t num_tokens = jsmn_parse( + &parser, + parsed_json->buffer, + parsed_json->bufferLen, + parsed_json->tokens, + MAX_NUMBER_OF_TOKENS); + + if (num_tokens < 0) { + switch (num_tokens) { + case JSMN_ERROR_NOMEM: + return parser_json_too_many_tokens; + case JSMN_ERROR_INVAL: + return parser_unexpected_characters; + case JSMN_ERROR_PART: + return parser_json_incomplete_json; + default: + return parser_json_unexpected_error; + } + } + + parsed_json->numberOfTokens = 0; + parsed_json->isValid = 0; + + // Parsing error + if (num_tokens <= 0) { + return parser_json_zero_tokens; + } + + // We cannot support if number of tokens exceeds the limit + if (num_tokens > MAX_NUMBER_OF_TOKENS) { + return parser_json_too_many_tokens; + } + + parsed_json->numberOfTokens = num_tokens; + parsed_json->isValid = true; + + return parser_ok; +} + +parser_error_t array_get_element_count(const parsed_json_t *json, + uint16_t array_token_index, + uint16_t *number_elements) { + *number_elements = 0; + if (array_token_index < 0 || array_token_index > json->numberOfTokens) { + return parser_no_data; + } + + jsmntok_t array_token = json->tokens[array_token_index]; + uint16_t token_index = array_token_index; + uint16_t prev_element_end = array_token.start; + while (true) { + token_index++; + if (token_index >= json->numberOfTokens) { + break; + } + jsmntok_t current_token = json->tokens[token_index]; + if (current_token.start > array_token.end) { + break; + } + if (current_token.start <= prev_element_end) { + continue; + } + prev_element_end = current_token.end; + (*number_elements)++; + } + + return parser_ok; +} + +parser_error_t array_get_nth_element(const parsed_json_t *json, + uint16_t array_token_index, + uint16_t element_index, + uint16_t *token_index) { + if (array_token_index < 0 || array_token_index > json->numberOfTokens) { + return parser_no_data; + } + + jsmntok_t array_token = json->tokens[array_token_index]; + *token_index = array_token_index; + + uint16_t element_count = 0; + uint16_t prev_element_end = array_token.start; + while (true) { + (*token_index)++; + if (*token_index >= json->numberOfTokens) { + break; + } + jsmntok_t current_token = json->tokens[*token_index]; + if (current_token.start > array_token.end) { + break; + } + if (current_token.start <= prev_element_end) { + continue; + } + prev_element_end = current_token.end; + if (element_count == element_index) { + return parser_ok; + } + element_count++; + } + + return parser_no_data; +} + +parser_error_t object_get_element_count(const parsed_json_t *json, + uint16_t object_token_index, + uint16_t *element_count) { + *element_count = 0; + if (object_token_index < 0 || object_token_index > json->numberOfTokens) { + return parser_no_data; + } + + jsmntok_t object_token = json->tokens[object_token_index]; + uint16_t token_index = object_token_index; + uint16_t prev_element_end = object_token.start; + token_index++; + while (true) { + if (token_index >= json->numberOfTokens) { + break; + } + jsmntok_t key_token = json->tokens[token_index++]; + jsmntok_t value_token = json->tokens[token_index]; + if (key_token.start > object_token.end) { + break; + } + if (key_token.start <= prev_element_end) { + continue; + } + prev_element_end = value_token.end; + (*element_count)++; + } + + return parser_ok; +} + +parser_error_t object_get_nth_key(const parsed_json_t *json, + uint16_t object_token_index, + uint16_t object_element_index, + uint16_t *token_index) { + *token_index = object_token_index; + if (object_token_index < 0 || object_token_index > json->numberOfTokens) { + return parser_no_data; + } + + jsmntok_t object_token = json->tokens[object_token_index]; + uint16_t element_count = 0; + uint16_t prev_element_end = object_token.start; + (*token_index)++; + while (true) { + if (*token_index >= json->numberOfTokens) { + break; + } + jsmntok_t key_token = json->tokens[(*token_index)++]; + jsmntok_t value_token = json->tokens[*token_index]; + if (key_token.start > object_token.end) { + break; + } + if (key_token.start <= prev_element_end) { + continue; + } + prev_element_end = value_token.end; + if (element_count == object_element_index) { + (*token_index)--; + return parser_ok; + } + element_count++; + } + + return parser_no_data; +} + +parser_error_t object_get_nth_value(const parsed_json_t *json, + uint16_t object_token_index, + uint16_t object_element_index, + uint16_t *key_index) { + if (object_token_index < 0 || object_token_index > json->numberOfTokens) { + return parser_no_data; + } + + CHECK_PARSER_ERR(object_get_nth_key(json, object_token_index, object_element_index, key_index)); + (*key_index) ++; + + return parser_ok; +} + +parser_error_t object_get_value(const parsed_json_t *json, + uint16_t object_token_index, + const char *key_name, + uint16_t *token_index) { + if (object_token_index < 0 || object_token_index > json->numberOfTokens) { + return parser_no_data; + } + + const jsmntok_t object_token = json->tokens[object_token_index]; + + *token_index = object_token_index; + int prev_element_end = object_token.start; + (*token_index)++; + + while (*token_index < json->numberOfTokens) { + const jsmntok_t key_token = json->tokens[*token_index]; + (*token_index)++; + const jsmntok_t value_token = json->tokens[*token_index]; + + if (key_token.start > object_token.end) { + break; + } + if (key_token.start <= prev_element_end) { + continue; + } + prev_element_end = value_token.end; + + if (((uint16_t) strlen(key_name)) == (key_token.end - key_token.start)) { + if (EQUALS(key_name, + json->buffer + key_token.start, + key_token.end - key_token.start)) { + return parser_ok; + } + } + } + + return parser_no_data; +} diff --git a/src/lib/json_parser.h b/app/src/json/json_parser.h similarity index 51% rename from src/lib/json_parser.h rename to app/src/json/json_parser.h index d050dd98..57409ecb 100644 --- a/src/lib/json_parser.h +++ b/app/src/json/json_parser.h @@ -1,5 +1,5 @@ /******************************************************************************* -* (c) ZondaX GmbH +* (c) 2018, 2019 Zondax GmbH * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -20,6 +20,7 @@ #include #include #include +#include "common/parser_common.h" #ifdef __cplusplus extern "C" { @@ -30,7 +31,7 @@ extern "C" { #endif /// Max number of accepted tokens in the JSON input -#define MAX_NUMBER_OF_TOKENS 2048 +#define MAX_NUMBER_OF_TOKENS 1536 // we must limit the number #if defined(TARGET_NANOS) @@ -46,23 +47,13 @@ extern "C" { // - parsed json tokens // - re-created SendMsg struct with indices pointing to tokens in parsed json typedef struct { - int8_t IsValid; - uint16_t NumberOfTokens; - jsmntok_t Tokens[MAX_NUMBER_OF_TOKENS]; + uint8_t isValid; + uint32_t numberOfTokens; + jsmntok_t tokens[MAX_NUMBER_OF_TOKENS]; + const char *buffer; + uint16_t bufferLen; } parsed_json_t; -/// Resets parsed_json data structure -/// \param -void reset_parsed_json(parsed_json_t *); - -typedef struct { - const parsed_json_t *parsed_tx; - uint16_t max_chars_per_key_line; - uint16_t max_chars_per_value_line; - const char *tx; - uint8_t cache_valid; -} parsing_context_t; - //--------------------------------------------- // NEW JSON PARSER CODE @@ -71,68 +62,70 @@ typedef struct { /// \param transaction /// \param transaction_length /// \return Error message -const char *json_parse_s(parsed_json_t *parsed_json, - const char *transaction, - uint16_t transaction_length); - -/// Parse json to create a token representation -/// \param parsed_json -/// \param transaction -/// \return Error message -const char *json_parse(parsed_json_t *parsed_json, - const char *transaction); +parser_error_t json_parse(parsed_json_t *parsed_json, + const char *transaction, + uint16_t transaction_length); /// Get the number of elements in the array +/// \param json /// \param array_token_index -/// \param parsed_transaction -/// \return number of elements -uint16_t array_get_element_count(uint16_t array_token_index, - const parsed_json_t *parsed_transaction); +/// \param number of elements (out) +/// \return Error message +parser_error_t array_get_element_count(const parsed_json_t *json, + uint16_t array_token_index, + uint16_t *number_elements); /// Get the token index of the nth array's element +/// \param json /// \param array_token_index /// \param element_index -/// \param parsed_transaction -/// \return returns the token index or -1 if not found -int16_t array_get_nth_element(uint16_t array_token_index, - uint16_t element_index, - const parsed_json_t *parsed_transaction); +/// \param token index +/// \return Error message +parser_error_t array_get_nth_element(const parsed_json_t *json, + uint16_t array_token_index, + uint16_t element_index, + uint16_t *token_index); /// Get the number of dictionary elements (key/value pairs) under given object +/// \param json /// \param object_token_index: token index of the parent object -/// \param parsed_transaction -/// \return number of elements -uint16_t object_get_element_count(uint16_t object_token_index, - const parsed_json_t *parsed_transaction); +/// \param number of elements (out) +/// \return Error message +parser_error_t object_get_element_count(const parsed_json_t *json, + uint16_t object_token_index, + uint16_t *number_elements); /// Get the token index for the nth dictionary key +/// \param json /// \param object_token_index: token index of the parent object /// \param object_element_index -/// \param parsed_transaction -/// \return returns token index or -1 if not found -int16_t object_get_nth_key(uint16_t object_token_index, - uint16_t object_element_index, - const parsed_json_t *parsed_transaction); +/// \return token index (out) +/// \return Error message +parser_error_t object_get_nth_key(const parsed_json_t *json, + uint16_t object_token_index, + uint16_t object_element_index, + uint16_t *token_index); /// Get the token index for the nth dictionary value +/// \param json /// \param object_token_index: token index of the parent object /// \param object_element_index -/// \param parsed_transaction -/// \return returns token index or -1 if not found -int16_t object_get_nth_value(uint16_t object_token_index, - uint16_t object_element_index, - const parsed_json_t *parsed_transaction); +/// \return token index (out)) +/// \return Error message +parser_error_t object_get_nth_value(const parsed_json_t *json, + uint16_t object_token_index, + uint16_t object_element_index, + uint16_t *token_index); /// Get the token index of the value that matches the given key +/// \param json /// \param object_token_index: token index of the parent object /// \param key_name: key name of the wanted value -/// \param parsed_transaction -/// \param transaction -/// \return returns token index or -1 if not found -int16_t object_get_value(uint16_t object_token_index, - const char *key_name, - const parsed_json_t *parsed_transaction, - const char *transaction); +/// \return Error message +parser_error_t object_get_value(const parsed_json_t *json, + uint16_t object_token_index, + const char *key_name, + uint16_t *token_index); #ifdef __cplusplus } diff --git a/app/src/parser.c b/app/src/parser.c new file mode 100644 index 00000000..9fdda176 --- /dev/null +++ b/app/src/parser.c @@ -0,0 +1,246 @@ +/******************************************************************************* +* (c) 2019 Zondax GmbH +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +********************************************************************************/ + +#include +#include +#include +#include +#include "tx_parser.h" +#include "tx_display.h" +#include "parser_impl.h" +#include "common/parser.h" + +parser_error_t parser_parse(parser_context_t *ctx, + const uint8_t *data, + size_t dataLen) { + CHECK_PARSER_ERR(tx_display_readTx(ctx, data, dataLen)) + return parser_ok; +} + +parser_error_t parser_validate(const parser_context_t *ctx) { + CHECK_PARSER_ERR(tx_validate(&parser_tx_obj.json)) + + // Iterate through all items to check that all can be shown and are valid + uint8_t numItems = 0; + CHECK_PARSER_ERR(parser_getNumItems(ctx, &numItems)); + + char tmpKey[40]; + char tmpVal[40]; + + for (uint8_t idx = 0; idx < numItems; idx++) { + uint8_t pageCount = 0; + CHECK_PARSER_ERR(parser_getItem(ctx, idx, tmpKey, sizeof(tmpKey), tmpVal, sizeof(tmpVal), 0, &pageCount)) + } + + return parser_ok; +} + +parser_error_t parser_getNumItems(const parser_context_t *ctx, uint8_t *num_items) { + *num_items = 0; + return tx_display_numItems(num_items); +} + +__Z_INLINE bool_t parser_areEqual(uint16_t tokenidx, char *expected) { + if (parser_tx_obj.json.tokens[tokenidx].type != JSMN_STRING) { + return bool_false; + } + + int16_t len = parser_tx_obj.json.tokens[tokenidx].end - parser_tx_obj.json.tokens[tokenidx].start; + if (len < 0) { + return bool_false; + } + + if (strlen(expected) != (size_t) len) { + return bool_false; + } + + const char *p = parser_tx_obj.tx + parser_tx_obj.json.tokens[tokenidx].start; + for (uint16_t i = 0; i < len; i++) { + if (expected[i] != *(p + i)) { + return bool_false; + } + } + + return bool_true; +} + +__Z_INLINE bool_t parser_isAmount(char *key) { + if (strcmp(key, "fee/amount") == 0) + return bool_true; + + if (strcmp(key, "msgs/inputs/coins") == 0) + return bool_true; + + if (strcmp(key, "msgs/outputs/coins") == 0) + return bool_true; + + if (strcmp(key, "msgs/value/amount") == 0) + return bool_true; + + return bool_false; +} + +__Z_INLINE bool_t is_default_denom_base(const char *denom, uint8_t denom_len) { + if (tx_is_expert_mode()){ + return false; + } + + if (strlen(COIN_DEFAULT_DENOM_BASE) != denom_len) { + return bool_false; + } + + if (memcmp(denom, COIN_DEFAULT_DENOM_BASE, denom_len) == 0) + return bool_true; + + return bool_false; +} + +__Z_INLINE parser_error_t parser_formatAmount(uint16_t amountToken, + char *outVal, uint16_t outValLen, + uint8_t pageIdx, uint8_t *pageCount) { + *pageCount = 0; + if (parser_tx_obj.json.tokens[amountToken].type == JSMN_ARRAY) { + amountToken++; + } + + uint16_t numElements; + + CHECK_PARSER_ERR(array_get_element_count(&parser_tx_obj.json, amountToken, &numElements)); + + if (numElements == 0) { + *pageCount = 1; + snprintf(outVal, outValLen, "Empty"); + return parser_ok; + } + + if (numElements != 4) + return parser_unexpected_field; + + if (parser_tx_obj.json.tokens[amountToken].type != JSMN_OBJECT) + return parser_unexpected_field; + + if (!parser_areEqual(amountToken + 1u, "amount")) + return parser_unexpected_field; + + if (!parser_areEqual(amountToken + 3u, "denom")) + return parser_unexpected_field; + + char bufferUI[160]; + MEMZERO(outVal, outValLen); + MEMZERO(bufferUI, sizeof(bufferUI)); + + const char *amountPtr = parser_tx_obj.tx + parser_tx_obj.json.tokens[amountToken + 2].start; + if (parser_tx_obj.json.tokens[amountToken + 2].start < 0) { + return parser_unexpected_buffer_end; + } + + const int16_t amountLen = parser_tx_obj.json.tokens[amountToken + 2].end - + parser_tx_obj.json.tokens[amountToken + 2].start; + const char *denomPtr = parser_tx_obj.tx + parser_tx_obj.json.tokens[amountToken + 4].start; + const int16_t denomLen = parser_tx_obj.json.tokens[amountToken + 4].end - + parser_tx_obj.json.tokens[amountToken + 4].start; + + if (amountLen <= 0) { + return parser_unexpected_buffer_end; + } + + if (denomLen <= 0) { + return parser_unexpected_buffer_end; + } + + if (sizeof(bufferUI) < (size_t) (amountLen + denomLen + 2) ) { + return parser_unexpected_buffer_end; + } + + if (is_default_denom_base(denomPtr, denomLen)) { + // Then we convert denomination + char tmp[50]; + if (amountLen < 0 || ((uint16_t) amountLen) >= sizeof(tmp)) { + return parser_unexpected_error; + } + MEMZERO(tmp, sizeof(tmp)); + MEMCPY(tmp, amountPtr, amountLen); + + if (fpstr_to_str(bufferUI, sizeof(tmp), tmp, COIN_DEFAULT_DENOM_FACTOR)!=0) { + return parser_unexpected_error; + } + + const uint16_t formatted_len =strlen(bufferUI); + bufferUI[formatted_len] = ' '; + MEMCPY(bufferUI + 1 + formatted_len, COIN_DEFAULT_DENOM_REPR, strlen(COIN_DEFAULT_DENOM_REPR)); + } else { + MEMCPY(bufferUI, amountPtr, amountLen); + bufferUI[amountLen] = ' '; + MEMCPY(bufferUI + 1 + amountLen, denomPtr, denomLen); + } + + pageString(outVal, outValLen, bufferUI, pageIdx, pageCount); + + return parser_ok; +} + +parser_error_t parser_getItem(const parser_context_t *ctx, + uint16_t displayIdx, + char *outKey, uint16_t outKeyLen, + char *outVal, uint16_t outValLen, + uint8_t pageIdx, uint8_t *pageCount) { + *pageCount = 0; + + MEMZERO(outKey, outKeyLen); + MEMZERO(outVal, outValLen); + + uint8_t numItems; + CHECK_PARSER_ERR(parser_getNumItems(ctx, &numItems)) + CHECK_APP_CANARY() + + if (numItems == 0) { + return parser_unexpected_number_items; + } + + if (displayIdx < 0 || displayIdx >= numItems) { + return parser_display_idx_out_of_range; + } + + uint16_t ret_value_token_index = 0; + CHECK_PARSER_ERR(tx_display_query(displayIdx, outKey, outKeyLen, &ret_value_token_index)); + CHECK_APP_CANARY() + + if (parser_isAmount(outKey)) { + CHECK_PARSER_ERR(parser_formatAmount( + ret_value_token_index, + outVal, outValLen, + pageIdx, pageCount)) + } else { + CHECK_PARSER_ERR(tx_getToken( + ret_value_token_index, + outVal, outValLen, + pageIdx, pageCount)) + } + CHECK_APP_CANARY() + + CHECK_PARSER_ERR(tx_display_make_friendly()) + CHECK_APP_CANARY() + + if (*pageCount > 1) { + size_t keyLen = strlen(outKey); + if (keyLen < outKeyLen) { + snprintf(outKey + keyLen, outKeyLen - keyLen, " [%d/%d]", pageIdx + 1, *pageCount); + } + } + + CHECK_APP_CANARY() + return parser_ok; +} diff --git a/app/src/parser_impl.c b/app/src/parser_impl.c new file mode 100644 index 00000000..641e2e8c --- /dev/null +++ b/app/src/parser_impl.c @@ -0,0 +1,123 @@ +/******************************************************************************* +* (c) 2019 Zondax GmbH +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +********************************************************************************/ + +#include "parser_impl.h" + +parser_tx_t parser_tx_obj; + +parser_error_t parser_init_context(parser_context_t *ctx, + const uint8_t *buffer, + uint16_t bufferSize) { + ctx->offset = 0; + + if (bufferSize == 0 || buffer == NULL) { + // Not available, use defaults + ctx->buffer = NULL; + ctx->bufferLen = 0; + return parser_init_context_empty; + } + + ctx->buffer = buffer; + ctx->bufferLen = bufferSize; + + return parser_ok; +} + +parser_error_t parser_init(parser_context_t *ctx, const uint8_t *buffer, size_t bufferSize) { + parser_error_t err = parser_init_context(ctx, buffer, bufferSize); + if (err != parser_ok) + return err; + + return err; +} + +const char *parser_getErrorDescription(parser_error_t err) { + switch (err) { + case parser_ok: + return "No error"; + case parser_no_data: + return "No more data"; + case parser_init_context_empty: + return "Initialized empty context"; + case parser_unexpected_buffer_end: + return "Unexpected buffer end"; + case parser_unexpected_version: + return "Unexpected version"; + case parser_unexpected_characters: + return "Unexpected characters"; + case parser_unexpected_field: + return "Unexpected field"; + case parser_duplicated_field: + return "Unexpected duplicated field"; + case parser_value_out_of_range: + return "Value out of range"; + case parser_unexpected_chain: + return "Unexpected chain"; + case parser_query_no_results: + return "item query returned no results"; + case parser_missing_field: + return "missing field"; +////// + case parser_display_idx_out_of_range: + return "display index out of range"; + case parser_display_page_out_of_range: + return "display page out of range"; +////// + case parser_json_zero_tokens: + return "JSON. Zero tokens"; + case parser_json_too_many_tokens: + return "JSON. Too many tokens"; + case parser_json_incomplete_json: + return "JSON string is not complete"; + case parser_json_contains_whitespace: + return "JSON Contains whitespace in the corpus"; + case parser_json_is_not_sorted: + return "JSON Dictionaries are not sorted"; + case parser_json_missing_chain_id: + return "JSON Missing chain_id"; + case parser_json_missing_sequence: + return "JSON Missing sequence"; + case parser_json_missing_fee: + return "JSON Missing fee"; + case parser_json_missing_msgs: + return "JSON Missing msgs"; + case parser_json_missing_account_number: + return "JSON Missing account number"; + case parser_json_missing_memo: + return "JSON Missing memo"; + case parser_json_unexpected_error: + return "JSON Unexpected error"; + + default: + return "Unrecognized error code"; + } +} + +parser_error_t _readTx(parser_context_t *c, parser_tx_t *v) { + parser_error_t err = json_parse(&parser_tx_obj.json, + (const char *) c->buffer, + c->bufferLen); + if (err != parser_ok) { + return err; + } + + parser_tx_obj.tx = (const char *) c->buffer; + parser_tx_obj.flags.cache_valid = 0; + parser_tx_obj.filter_msg_type_count = 0; + parser_tx_obj.filter_msg_from_count = 0; + + return parser_ok; +} diff --git a/app/src/parser_impl.h b/app/src/parser_impl.h new file mode 100644 index 00000000..032df72d --- /dev/null +++ b/app/src/parser_impl.h @@ -0,0 +1,41 @@ +/******************************************************************************* +* (c) 2019 Zondax GmbH +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +********************************************************************************/ +#pragma once + +#include "parser_common.h" +#include "json/json_parser.h" +#include "parser_txdef.h" + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct { + char str1[50]; + char str2[50]; +} key_subst_t; + +extern parser_tx_t parser_tx_obj; + +parser_error_t parser_init(parser_context_t *ctx, + const uint8_t *buffer, + size_t bufferSize); + +parser_error_t _readTx(parser_context_t *c, parser_tx_t *v); + +#ifdef __cplusplus +} +#endif diff --git a/app/src/parser_txdef.h b/app/src/parser_txdef.h new file mode 100644 index 00000000..b00aac9f --- /dev/null +++ b/app/src/parser_txdef.h @@ -0,0 +1,79 @@ +/******************************************************************************* +* (c) 2019 Zondax GmbH +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +********************************************************************************/ +#pragma once + +#ifdef __cplusplus +extern "C" { +#endif + +#include +#include + +#include + +typedef struct { + // These are internal values used for tracking the state of the query/search + uint16_t _item_index_current; + + // maximum json tree level. Beyond this tree depth, key/values are flattened + uint8_t max_level; + + // maximum tree traversal depth. This limits possible stack overflow issues + uint8_t max_depth; + + // Index of the item to retrieve + int16_t item_index; + // Chunk of the item to retrieve (assuming partitioning based on out_val_len chunks) + int16_t page_index; + + // These fields (out_*) are where query results are placed + char *out_key; + uint16_t out_key_len; + char *out_val; + int16_t out_val_len; +} tx_query_t; + +typedef struct { + // Buffer to the original tx blob + const char *tx; + + // parsed data (tokens, etc.) + parsed_json_t json; + + // internal flags + struct { + unsigned int cache_valid:1; + unsigned int msg_type_grouping:1; + unsigned int msg_from_grouping:1; + unsigned int msg_from_grouping_hide_all:1; + } flags; + + // indicates that N identical msg_type fields have been detected + uint8_t filter_msg_type_count; + int32_t filter_msg_type_valid_idx; + + // indicates that N identical msg_from fields have been detected + uint8_t filter_msg_from_count; + int32_t filter_msg_from_valid_idx; + const char *own_addr; + + // current tx query + tx_query_t query; +} parser_tx_t; + +#ifdef __cplusplus +} +#endif diff --git a/app/src/tx_display.c b/app/src/tx_display.c new file mode 100644 index 00000000..9dc12fba --- /dev/null +++ b/app/src/tx_display.c @@ -0,0 +1,482 @@ +/******************************************************************************* +* (c) 2018, 2019 Zondax GmbH +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +********************************************************************************/ + +#include "coin.h" +#include "app_mode.h" +#include "tx_display.h" +#include "tx_parser.h" +#include "parser_impl.h" +#include + +#define NUM_REQUIRED_ROOT_PAGES 6 + +const char *get_required_root_item(root_item_e i) { + switch (i) { + case root_item_chain_id: + return "chain_id"; + case root_item_account_number: + return "account_number"; + case root_item_sequence: + return "sequence"; + case root_item_fee: + return "fee"; + case root_item_memo: + return "memo"; + case root_item_msgs: + return "msgs"; + default: + return "?"; + } +} + +__Z_INLINE uint8_t get_root_max_level(root_item_e i) { + switch (i) { + case root_item_chain_id: + return 2; + case root_item_account_number: + return 2; + case root_item_sequence: + return 2; + case root_item_fee: + return 1; + case root_item_memo: + return 2; + case root_item_msgs: + return 2; + default: + return 0; + } +} + +typedef struct { + bool root_item_start_token_valid[NUM_REQUIRED_ROOT_PAGES]; + // token where the root_item starts (negative for non-existing) + uint16_t root_item_start_token_idx[NUM_REQUIRED_ROOT_PAGES]; + + // total items + uint16_t total_item_count; + // number of items the root_item contains + uint8_t root_item_number_subitems[NUM_REQUIRED_ROOT_PAGES]; + + uint8_t is_default_chain; +} display_cache_t; + +display_cache_t display_cache; + +parser_error_t tx_display_readTx(parser_context_t *ctx, const uint8_t *data, size_t dataLen) { + CHECK_PARSER_ERR(parser_init(ctx, data, dataLen)) + CHECK_PARSER_ERR(_readTx(ctx, &parser_tx_obj)) + return parser_ok; +} + +__Z_INLINE parser_error_t calculate_is_default_chainid() { + display_cache.is_default_chain = false; + + // get chain_id + char outKey[2]; + char outVal[20]; + uint8_t pageCount; + INIT_QUERY_CONTEXT(outKey, sizeof(outKey), + outVal, sizeof(outVal), + 0, get_root_max_level(root_item_chain_id)) + parser_tx_obj.query.item_index = 0; + parser_tx_obj.query._item_index_current = 0; + + uint16_t ret_value_token_index; + CHECK_PARSER_ERR(tx_traverse_find( + display_cache.root_item_start_token_idx[root_item_chain_id], + &ret_value_token_index)) + + CHECK_PARSER_ERR(tx_getToken( + ret_value_token_index, + outVal, sizeof(outVal), + 0, &pageCount)) + + if (strcmp(outVal, COIN_DEFAULT_CHAINID) != 0) { + // If we don't match the default chainid, switch to expert mode + display_cache.is_default_chain = true; + } + + return parser_ok; +} + +__Z_INLINE bool address_matches_own(char *addr) { + if (parser_tx_obj.own_addr == NULL) { + return false; + } + if (strcmp(parser_tx_obj.own_addr, addr) != 0) { + return false; + } + return true; +} + +parser_error_t tx_indexRootFields() { + if (parser_tx_obj.flags.cache_valid) { + return parser_ok; + } + + // Clear cache + MEMZERO(&display_cache, sizeof(display_cache_t)); + char tmp_key[70]; + char tmp_val[70]; + MEMZERO(&tmp_key, sizeof(tmp_key)); + MEMZERO(&tmp_val, sizeof(tmp_val)); + + // Grouping references + char reference_msg_type[40]; + MEMZERO(&reference_msg_type, sizeof(reference_msg_type)); + char reference_msg_from[70]; + MEMZERO(&reference_msg_from, sizeof(reference_msg_from)); + + parser_tx_obj.filter_msg_type_count = 0; + parser_tx_obj.filter_msg_from_count = 0; + parser_tx_obj.flags.msg_type_grouping = 1; + parser_tx_obj.flags.msg_from_grouping = 1; + + for (root_item_e root_item_idx = 0; root_item_idx < NUM_REQUIRED_ROOT_PAGES; root_item_idx++) { + uint16_t req_root_item_key_token_idx = 0; + + parser_error_t err = object_get_value( + &parser_tx_obj.json, + ROOT_TOKEN_INDEX, + get_required_root_item(root_item_idx), + &req_root_item_key_token_idx); + + if (err == parser_no_data) { + continue; + } + CHECK_PARSER_ERR(err) + + // Remember root item start token + display_cache.root_item_start_token_valid[root_item_idx] = 1; + display_cache.root_item_start_token_idx[root_item_idx] = req_root_item_key_token_idx; + + // Now count how many items can be found in this root item + int32_t current_item_idx = 0; + while (err == parser_ok) { + INIT_QUERY_CONTEXT(tmp_key, sizeof(tmp_key), + tmp_val, sizeof(tmp_val), + 0, get_root_max_level(root_item_idx)) + parser_tx_obj.query.item_index = current_item_idx; + strncpy_s(parser_tx_obj.query.out_key, + get_required_root_item(root_item_idx), + parser_tx_obj.query.out_key_len); + + uint16_t ret_value_token_index; + + err = tx_traverse_find( + display_cache.root_item_start_token_idx[root_item_idx], + &ret_value_token_index); + + if (err != parser_ok) { + continue; + } + + uint8_t pageCount; + CHECK_PARSER_ERR(tx_getToken( + ret_value_token_index, + parser_tx_obj.query.out_val, + parser_tx_obj.query.out_key_len, + 0, &pageCount)) + + switch (root_item_idx) { + case root_item_memo: { + if (strlen(parser_tx_obj.query.out_val) == 0) { + err = parser_query_no_results; + continue; + } + break; + } + case root_item_msgs: { + // GROUPING: Message Type + if (parser_tx_obj.flags.msg_type_grouping && is_msg_type_field(tmp_key)) { + // First message, initialize expected type + if (parser_tx_obj.filter_msg_type_count == 0) { + strcpy(reference_msg_type, tmp_val); + parser_tx_obj.filter_msg_type_valid_idx = current_item_idx; + } + + if (strcmp(reference_msg_type, tmp_val) != 0) { + // different values, so disable grouping + parser_tx_obj.flags.msg_type_grouping = 0; + parser_tx_obj.filter_msg_type_count = 0; + } + + parser_tx_obj.filter_msg_type_count++; + } + + // GROUPING: Message From + if (parser_tx_obj.flags.msg_from_grouping && is_msg_from_field(tmp_key)) { + // First message, initialize expected from + if (parser_tx_obj.filter_msg_from_count == 0) { + strcpy(reference_msg_from, tmp_val); + parser_tx_obj.filter_msg_from_valid_idx = current_item_idx; + } + + if (strcmp(reference_msg_from, tmp_val) != 0) { + // different values, so disable grouping + parser_tx_obj.flags.msg_from_grouping = 0; + parser_tx_obj.filter_msg_from_count = 0; + } + + parser_tx_obj.filter_msg_from_count++; + } + } + default: + break; + } + + display_cache.root_item_number_subitems[root_item_idx]++; + current_item_idx++; + } + + if (err != parser_query_no_results && err != parser_no_data) { + return err; + } + + display_cache.total_item_count += display_cache.root_item_number_subitems[root_item_idx]; + } + + parser_tx_obj.flags.cache_valid = 1; + + CHECK_PARSER_ERR(calculate_is_default_chainid()); + + // turn off grouping if we are not in expert mode + if (tx_is_expert_mode()) { + parser_tx_obj.flags.msg_from_grouping = 0; + } + + // check if from reference value matches the device address that will be signing + parser_tx_obj.flags.msg_from_grouping_hide_all = 0; + if (address_matches_own(reference_msg_from)){ + parser_tx_obj.flags.msg_from_grouping_hide_all = 1; + } + + return parser_ok; +} + +__Z_INLINE bool is_default_chainid() { + CHECK_PARSER_ERR(tx_indexRootFields()); + return display_cache.is_default_chain; +} + +bool tx_is_expert_mode() { + return app_mode_expert() || is_default_chainid(); +} + +__Z_INLINE uint8_t get_subitem_count(root_item_e root_item) { + CHECK_PARSER_ERR(tx_indexRootFields()) + if (display_cache.total_item_count == 0) + return 0; + + int16_t tmp_num_items = display_cache.root_item_number_subitems[root_item]; + + switch (root_item) { + case root_item_chain_id: + case root_item_sequence: + case root_item_account_number: + if (!tx_is_expert_mode()) { + tmp_num_items = 0; + } + break; + case root_item_msgs: + // Remove grouped items from list + if (parser_tx_obj.flags.msg_type_grouping == 1u && parser_tx_obj.filter_msg_type_count > 0) { + tmp_num_items += 1; // we leave main type + tmp_num_items -= parser_tx_obj.filter_msg_type_count; + } + if (parser_tx_obj.flags.msg_from_grouping == 1u && parser_tx_obj.filter_msg_from_count > 0) { + if (!parser_tx_obj.flags.msg_from_grouping_hide_all) { + tmp_num_items += 1; // we leave main from + } + tmp_num_items -= parser_tx_obj.filter_msg_from_count; + } + break; + case root_item_memo: + break; + case root_item_fee: + if (!tx_is_expert_mode()) { + tmp_num_items -= 1; // Hide Gas field + } + default: + break; + } + + return tmp_num_items; +} + +__Z_INLINE parser_error_t retrieve_tree_indexes(uint8_t display_index, root_item_e *root_item, uint8_t *subitem_index) { + // Find root index | display_index idx -> item_index + // consume indexed subpages until we get the item index in the subpage + *root_item = 0; + *subitem_index = 0; + while (get_subitem_count(*root_item) == 0) { + (*root_item)++; + } + + for (uint16_t i = 0; i < display_index; i++) { + (*subitem_index)++; + const uint8_t subitem_count = get_subitem_count(*root_item); + if (*subitem_index >= subitem_count) { + // Advance root index and skip empty items + *subitem_index = 0; + (*root_item)++; + while (get_subitem_count(*root_item) == 0){ + (*root_item)++; + } + } + } + + if (*root_item > NUM_REQUIRED_ROOT_PAGES) { + return parser_no_data; + } + + return parser_ok; +} + +parser_error_t tx_display_numItems(uint8_t *num_items) { + *num_items = 0; + CHECK_PARSER_ERR(tx_indexRootFields()) + + *num_items = 0; + for (root_item_e root_item = 0; root_item < NUM_REQUIRED_ROOT_PAGES; root_item++) { + *num_items += get_subitem_count(root_item); + } + + return parser_ok; +} + +// This function assumes that the tx_ctx has been set properly +parser_error_t tx_display_query(uint16_t displayIdx, + char *outKey, uint16_t outKeyLen, + uint16_t *ret_value_token_index) { + CHECK_PARSER_ERR(tx_indexRootFields()) + + uint8_t num_items; + CHECK_PARSER_ERR(tx_display_numItems(&num_items)); + + if (displayIdx < 0 || displayIdx >= num_items) { + return parser_display_idx_out_of_range; + } + + root_item_e root_index = 0; + uint8_t subitem_index = 0; + CHECK_PARSER_ERR(retrieve_tree_indexes(displayIdx, &root_index, &subitem_index)); + + // Prepare query + char tmp_val[2]; + INIT_QUERY_CONTEXT(outKey, outKeyLen, tmp_val, sizeof(tmp_val), + 0, get_root_max_level(root_index)) + parser_tx_obj.query.item_index = subitem_index; + parser_tx_obj.query._item_index_current = 0; + + strncpy_s(outKey, get_required_root_item(root_index), outKeyLen); + + if (!display_cache.root_item_start_token_valid[root_index]) { + return parser_no_data; + } + + CHECK_PARSER_ERR(tx_traverse_find( + display_cache.root_item_start_token_idx[root_index], + ret_value_token_index)) + + return parser_ok; +} + +///////////////////////////////////////////////////////////////////////////////////////////////////// +///////////////////////////////////////////////////////////////////////////////////////////////////// +///////////////////////////////////////////////////////////////////////////////////////////////////// +///////////////////////////////////////////////////////////////////////////////////////////////////// +///////////////////////////////////////////////////////////////////////////////////////////////////// +///////////////////////////////////////////////////////////////////////////////////////////////////// +///////////////////////////////////////////////////////////////////////////////////////////////////// +///////////////////////////////////////////////////////////////////////////////////////////////////// +///////////////////////////////////////////////////////////////////////////////////////////////////// +///////////////////////////////////////////////////////////////////////////////////////////////////// +///////////////////////////////////////////////////////////////////////////////////////////////////// +///////////////////////////////////////////////////////////////////////////////////////////////////// +///////////////////////////////////////////////////////////////////////////////////////////////////// + +static const key_subst_t key_substitutions[] = { + {"chain_id", "Chain ID"}, + {"account_number", "Account"}, + {"sequence", "Sequence"}, + {"memo", "Memo"}, + {"fee/amount", "Fee"}, + {"fee/gas", "Gas"}, + {"msgs/type", "Type"}, + + // FIXME: Are these obsolete?? multisend? + {"msgs/inputs/address", "Source Address"}, + {"msgs/inputs/coins", "Source Coins"}, + {"msgs/outputs/address", "Dest Address"}, + {"msgs/outputs/coins", "Dest Coins"}, + + // MsgSend + {"msgs/value/from_address", "From"}, + {"msgs/value/to_address", "To"}, + {"msgs/value/amount", "Amount"}, + + // MsgDelegate + {"msgs/value/delegator_address", "Delegator"}, + {"msgs/value/validator_address", "Validator"}, + + // MsgUndelegate +// {"msgs/value/delegator_address", "Delegator"}, +// {"msgs/value/validator_address", "Validator"}, + + // MsgBeginRedelegate +// {"msgs/value/delegator_address", "Delegator"}, + {"msgs/value/validator_src_address", "Validator Source"}, + {"msgs/value/validator_dst_address", "Validator Dest"}, + + // MsgSubmitProposal + {"msgs/value/description", "Description"}, + {"msgs/value/initial_deposit/amount", "Deposit Amount"}, + {"msgs/value/initial_deposit/denom", "Deposit Denom"}, + {"msgs/value/proposal_type", "Proposal"}, + {"msgs/value/proposer", "Proposer"}, + {"msgs/value/title", "Title"}, + + // MsgDeposit + {"msgs/value/depositer", "Sender"}, + {"msgs/value/proposal_id", "Proposal ID"}, + {"msgs/value/amount", "Amount"}, + + // MsgVote + {"msgs/value/voter", "Description"}, +// {"msgs/value/proposal_id", "Proposal ID"}, + {"msgs/value/option", "Option"}, + + // MsgWithdrawDelegationReward +// {"msgs/value/delegator_address", "Delegator"}, // duplicated +// {"msgs/value/validator_address", "Validator"}, // duplicated +}; + +parser_error_t tx_display_make_friendly() { + CHECK_PARSER_ERR(tx_indexRootFields()) + + // post process keys + for (size_t i = 0; i < array_length(key_substitutions); i++) { + if (!strcmp(parser_tx_obj.query.out_key, key_substitutions[i].str1)) { + strncpy_s(parser_tx_obj.query.out_key, key_substitutions[i].str2, parser_tx_obj.query.out_key_len); + break; + } + } + + return parser_ok; +} + diff --git a/app/src/tx_display.h b/app/src/tx_display.h new file mode 100644 index 00000000..2e51e5b3 --- /dev/null +++ b/app/src/tx_display.h @@ -0,0 +1,55 @@ +/******************************************************************************* +* (c) 2018, 2019 Zondax GmbH +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +********************************************************************************/ + +#pragma once + +#include +#include +#include "parser_txdef.h" + +#ifdef __cplusplus +extern "C" { +#endif + +typedef enum { + root_item_chain_id = 0, + root_item_account_number, + root_item_sequence, + root_item_msgs, + root_item_memo, + root_item_fee, +} root_item_e; + +bool tx_is_expert_mode(); + +const char *get_required_root_item(root_item_e i); + +parser_error_t tx_display_query(uint16_t displayIdx, + char *outKey, uint16_t outKeyLen, + uint16_t *ret_value_token_index); + +parser_error_t tx_display_readTx(parser_context_t *c, + const uint8_t *data, size_t dataLen); + +parser_error_t tx_display_numItems(uint8_t *num_items); + +parser_error_t tx_display_make_friendly(); + +//--------------------------------------------- + +#ifdef __cplusplus +} +#endif diff --git a/app/src/tx_parser.c b/app/src/tx_parser.c new file mode 100644 index 00000000..519be032 --- /dev/null +++ b/app/src/tx_parser.c @@ -0,0 +1,244 @@ +/******************************************************************************* +* (c) 2018, 2019 Zondax GmbH +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +********************************************************************************/ + +#include +#include +#include "tx_parser.h" +#include "zxmacros.h" +#include "parser_impl.h" + +// strcat but source does not need to be terminated (a chunk from a bigger string is concatenated) +// dst_max is measured in bytes including the space for NULL termination +// src_size does not include NULL termination +__Z_INLINE void strcat_chunk_s(char *dst, uint16_t dst_max, const char *src_chunk, size_t src_chunk_size) { + *(dst + dst_max - 1) = 0; // last character terminates with zero in case we go beyond bounds + const size_t prev_size = strlen(dst); + + size_t space_left = dst_max - prev_size - 1; // -1 because requires termination + + if (src_chunk_size > space_left) { + src_chunk_size = space_left; + } + + if (src_chunk_size > 0) { + // Check bounds + MEMCPY(dst + prev_size, src_chunk, src_chunk_size); + // terminate + *(dst + prev_size + src_chunk_size) = 0; + } +} + +/////////////////////////// +/////////////////////////// +/////////////////////////// +/////////////////////////// +/////////////////////////// +/////////////////////////// +/////////////////////////// + +static const key_subst_t value_substitutions[] = { + {"cosmos-sdk/MsgSend", "Send"}, + {"cosmos-sdk/MsgDelegate", "Delegate"}, + {"cosmos-sdk/MsgUndelegate", "Undelegate"}, + {"cosmos-sdk/MsgBeginRedelegate", "Redelegate"}, + {"cosmos-sdk/MsgSubmitProposal", "Propose"}, + {"cosmos-sdk/MsgDeposit", "Deposit"}, + {"cosmos-sdk/MsgVote", "Vote"}, + {"cosmos-sdk/MsgWithdrawDelegationReward", "Withdraw Reward"}, +}; + +parser_error_t tx_getToken(uint16_t token_index, + char *out_val, uint16_t out_val_len, + uint8_t pageIdx, uint8_t *pageCount) { + *pageCount = 0; + MEMZERO(out_val, out_val_len); + + const int16_t token_start = parser_tx_obj.json.tokens[token_index].start; + const int16_t token_end = parser_tx_obj.json.tokens[token_index].end; + + if (token_start > token_end) { + return parser_unexpected_buffer_end; + } + + const char *inValue = parser_tx_obj.tx + token_start; + uint16_t inLen = token_end - token_start; + + // empty strings are considered the first page + *pageCount = 1; + if (inLen > 0) { + for (uint8_t i = 0; i < array_length(value_substitutions); i++) { + const char *substStr = value_substitutions[i].str1; + const size_t substStrLen = strlen(substStr); + if (inLen == substStrLen && !MEMCMP(inValue, substStr, substStrLen)) { + inValue = value_substitutions[i].str2; + inLen = strlen(value_substitutions[i].str2); + break; + } + } + + pageStringExt(out_val, out_val_len, + inValue, inLen, + pageIdx, pageCount); + + } + + if (pageIdx >= *pageCount) { + return parser_display_page_out_of_range; + } + + return parser_ok; +} + +__Z_INLINE void append_key_item(int16_t token_index) { + if (*parser_tx_obj.query.out_key > 0) { + // There is already something there, add separator + strcat_chunk_s(parser_tx_obj.query.out_key, + parser_tx_obj.query.out_key_len, + "/", + 1); + } + + const int16_t token_start = parser_tx_obj.json.tokens[token_index].start; + const int16_t token_end = parser_tx_obj.json.tokens[token_index].end; + const char *address_ptr = parser_tx_obj.tx + token_start; + const int16_t new_item_size = token_end - token_start; + + strcat_chunk_s(parser_tx_obj.query.out_key, + parser_tx_obj.query.out_key_len, + address_ptr, + new_item_size); +} +/////////////////////////// +/////////////////////////// +/////////////////////////// +/////////////////////////// +/////////////////////////// +/////////////////////////// + +parser_error_t tx_traverse_find(int16_t root_token_index, uint16_t *ret_value_token_index) { + const jsmntype_t token_type = parser_tx_obj.json.tokens[root_token_index].type; + + CHECK_APP_CANARY() + + if (parser_tx_obj.tx == NULL || root_token_index < 0) { + return parser_no_data; + } + + if (parser_tx_obj.query.max_level <= 0 || parser_tx_obj.query.max_depth <= 0 || + token_type == JSMN_STRING || + token_type == JSMN_PRIMITIVE) { + const bool skipTypeField = + parser_tx_obj.flags.cache_valid && + parser_tx_obj.flags.msg_type_grouping && + is_msg_type_field(parser_tx_obj.query.out_key) && + parser_tx_obj.filter_msg_type_valid_idx != parser_tx_obj.query._item_index_current; + + const bool skipFromFieldHidingRule = + parser_tx_obj.flags.msg_from_grouping_hide_all || + parser_tx_obj.filter_msg_from_valid_idx != parser_tx_obj.query._item_index_current; + + const bool skipFromField = + parser_tx_obj.flags.cache_valid && + parser_tx_obj.flags.msg_from_grouping && + is_msg_from_field(parser_tx_obj.query.out_key) && + skipFromFieldHidingRule; + + const bool skipField = skipFromField || skipTypeField; + + CHECK_APP_CANARY() + + // Early bail out + if (!skipField && parser_tx_obj.query._item_index_current == parser_tx_obj.query.item_index) { + *ret_value_token_index = root_token_index; + CHECK_APP_CANARY() + return parser_ok; + } + + if (skipField) { + parser_tx_obj.query.item_index++; + } + + parser_tx_obj.query._item_index_current++; + CHECK_APP_CANARY() + return parser_query_no_results; + } + + uint16_t el_count; + parser_error_t err; + + CHECK_PARSER_ERR(object_get_element_count(&parser_tx_obj.json, root_token_index, &el_count)) + + switch (token_type) { + case JSMN_OBJECT: { + const size_t key_len = strlen(parser_tx_obj.query.out_key); + for (uint16_t i = 0; i < el_count; ++i) { + uint16_t key_index; + uint16_t value_index; + + CHECK_PARSER_ERR(object_get_nth_key(&parser_tx_obj.json, root_token_index, i, &key_index)); + CHECK_PARSER_ERR(object_get_nth_value(&parser_tx_obj.json, root_token_index, i, &value_index)); + + // Skip writing keys if we are actually exploring to count + append_key_item(key_index); + CHECK_APP_CANARY() + + // When traversing objects both level and depth should be considered + parser_tx_obj.query.max_level--; + parser_tx_obj.query.max_depth--; + + // Traverse the value, extracting subkeys + err = tx_traverse_find(value_index, ret_value_token_index); + CHECK_APP_CANARY() + parser_tx_obj.query.max_level++; + parser_tx_obj.query.max_depth++; + + if (err == parser_ok) { + return parser_ok; + } + + *(parser_tx_obj.query.out_key + key_len) = 0; + CHECK_APP_CANARY() + } + break; + } + case JSMN_ARRAY: { + for (int16_t i = 0; i < el_count; ++i) { + uint16_t element_index; + CHECK_PARSER_ERR(array_get_nth_element(&parser_tx_obj.json, + root_token_index, i, + &element_index)); + CHECK_APP_CANARY() + + // When iterating along an array, + // the level does not change but we need to count the recursion + parser_tx_obj.query.max_depth--; + err = tx_traverse_find(element_index, ret_value_token_index); + parser_tx_obj.query.max_depth++; + + CHECK_APP_CANARY() + + if (err == parser_ok) { + return parser_ok; + } + } + break; + } + default: + break; + } + + return parser_query_no_results; +} diff --git a/app/src/tx_parser.h b/app/src/tx_parser.h new file mode 100644 index 00000000..022bacd6 --- /dev/null +++ b/app/src/tx_parser.h @@ -0,0 +1,65 @@ +/******************************************************************************* +* (c) 2018, 2019 Zondax GmbH +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +********************************************************************************/ + +#pragma once + +#include "json/json_parser.h" +#include +#include +#include "zxmacros.h" + +#ifdef __cplusplus +extern "C" { +#endif + +#define MAX_RECURSION_DEPTH 6 + +#define INIT_QUERY_CONTEXT(_KEY, _KEY_LEN, _VAL, _VAL_LEN, _PAGE_IDX, _MAX_LEVEL) \ + parser_tx_obj.query._item_index_current = 0; \ + parser_tx_obj.query.max_depth = MAX_RECURSION_DEPTH; \ + parser_tx_obj.query.max_level = _MAX_LEVEL; \ + \ + parser_tx_obj.query.item_index= 0; \ + parser_tx_obj.query.page_index = (_PAGE_IDX); \ + \ + MEMZERO(_KEY, (_KEY_LEN)); \ + MEMZERO(_VAL, (_VAL_LEN)); \ + parser_tx_obj.query.out_key= _KEY; \ + parser_tx_obj.query.out_val= _VAL; \ + parser_tx_obj.query.out_key_len = (_KEY_LEN); \ + parser_tx_obj.query.out_val_len = (_VAL_LEN); + +parser_error_t tx_traverse_find(int16_t root_token_index, uint16_t *ret_value_token_index); + +// Traverses transaction data and fills tx_context +parser_error_t tx_traverse(int16_t root_token_index, uint8_t *numChunks); + +// Retrieves the value for the corresponding token index. If the value goes beyond val_len, the chunk_idx will be used +parser_error_t tx_getToken(uint16_t token_index, + char *out_val, uint16_t out_val_len, + uint8_t pageIdx, uint8_t *pageCount); + +__Z_INLINE bool is_msg_type_field(char *field_name) { + return strcmp(field_name, "msgs/type") == 0; +} + +__Z_INLINE bool is_msg_from_field(char *field_name) { + return strcmp(field_name, "msgs/value/delegator_address") == 0; +} + +#ifdef __cplusplus +} +#endif diff --git a/app/src/tx_validate.c b/app/src/tx_validate.c new file mode 100644 index 00000000..48fae7d4 --- /dev/null +++ b/app/src/tx_validate.c @@ -0,0 +1,159 @@ +/******************************************************************************* +* (c) 2018, 2019 Zondax GmbH +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +********************************************************************************/ + +#include +#include +#include "json/json_parser.h" + +const char whitespaces[] = { + 0x20,// space ' ' + 0x0c, // form_feed '\f' + 0x0a, // line_feed, '\n' + 0x0d, // carriage_return, '\r' + 0x09, // horizontal_tab, '\t' + 0x0b // vertical_tab, '\v' +}; + +int8_t is_space(char c) { + for (uint16_t i = 0; i < sizeof(whitespaces); i++) { + if (whitespaces[i] == c) { + return 1; + } + } + return 0; +} + +int8_t contains_whitespace(parsed_json_t *json) { + int start = 0; + const int last_element_index = json->tokens[0].end; + + // Starting at token 1 because token 0 contains full tx + for (uint32_t i = 1; i < json->numberOfTokens; i++) { + if (json->tokens[i].type != JSMN_UNDEFINED) { + const int end = json->tokens[i].start; + for (int j = start; j < end; j++) { + if (is_space(json->buffer[j]) == 1) { + return 1; + } + } + start = json->tokens[i].end + 1; + } else { + return 0; + } + } + while (start <= last_element_index && json->buffer[start] != '\0') { + if (is_space(json->buffer[start])) { + return 1; + } + start++; + } + return 0; +} + +int8_t is_sorted(int16_t first_index, + int16_t second_index, + parsed_json_t *json) { +#if DEBUG_SORTING + char first[256]; + char second[256]; + + int size = parsed_tx->Tokens[first_index].end - parsed_tx->Tokens[first_index].start; + strncpy(first, tx + parsed_tx->Tokens[first_index].start, size); + first[size] = '\0'; + size = parsed_tx->Tokens[second_index].end - parsed_tx->Tokens[second_index].start; + strncpy(second, tx + parsed_tx->Tokens[second_index].start, size); + second[size] = '\0'; +#endif + + if (strcmp((json->buffer + json->tokens[first_index].start), + (json->buffer + json->tokens[second_index].start)) <= 0) { + return 1; + } + return 0; +} + +int8_t dictionaries_sorted(parsed_json_t *json) { + for (uint32_t i = 0; i < json->numberOfTokens; i++) { + if (json->tokens[i].type == JSMN_OBJECT) { + + uint16_t count; + + if (object_get_element_count(json, i, &count) != parser_ok) { + return 0; + } + + if (count > 1) { + uint16_t prev_token_index; + if (object_get_nth_key(json, i, 0, &prev_token_index) != parser_ok) { + return 0; + } + + for (int j = 1; j < count; j++) { + uint16_t next_token_index; + + if (object_get_nth_key(json, i, j, &next_token_index) != parser_ok) { + return 0; + } + + if (!is_sorted(prev_token_index, next_token_index, json)) { + return 0; + } + prev_token_index = next_token_index; + } + } + } + } + return 1; +} + +parser_error_t tx_validate(parsed_json_t *json) { + if (contains_whitespace(json) == 1) { + return parser_json_contains_whitespace; + } + + if (dictionaries_sorted(json) != 1) { + return parser_json_is_not_sorted; + } + + uint16_t token_index; + parser_error_t err; + + err = object_get_value(json, 0, "chain_id", &token_index); + if (err != parser_ok) + return parser_json_missing_chain_id; + + err = object_get_value(json, 0, "sequence", &token_index); + if (err != parser_ok) + return parser_json_missing_sequence; + + err = object_get_value(json, 0, "fee", &token_index); + if (err != parser_ok) + return parser_json_missing_fee; + + err = object_get_value(json, 0, "msgs", &token_index); + if (err != parser_ok) + return parser_json_missing_msgs; + + err = object_get_value(json, 0, "account_number", &token_index); + if (err != parser_ok) + return parser_json_missing_account_number; + + err = object_get_value(json, 0, "memo", &token_index); + if (err != parser_ok) + return parser_json_missing_memo; + + return parser_ok; +} diff --git a/src/lib/tx_validate.h b/app/src/tx_validate.h similarity index 86% rename from src/lib/tx_validate.h rename to app/src/tx_validate.h index 73e598f4..acd72030 100644 --- a/src/lib/tx_validate.h +++ b/app/src/tx_validate.h @@ -1,5 +1,5 @@ /******************************************************************************* -* (c) ZondaX GmbH +* (c) 2018, 2019 Zondax GmbH * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -16,8 +16,9 @@ #pragma once -#include "json_parser.h" +#include "json/json_parser.h" #include +#include #ifdef __cplusplus extern "C" { @@ -27,7 +28,7 @@ extern "C" { /// \param parsed_transacton /// \param transaction /// \return -const char *json_validate(parsed_json_t *parsed_transaction, const char *transaction); +parser_error_t tx_validate(parsed_json_t *json); #ifdef __cplusplus } diff --git a/app/src/view_custom.c b/app/src/view_custom.c new file mode 100644 index 00000000..cf6fea2c --- /dev/null +++ b/app/src/view_custom.c @@ -0,0 +1,37 @@ +/******************************************************************************* +* (c) 2018, 2019 Zondax GmbH +* (c) 2016 Ledger +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +********************************************************************************/ + +#include "view.h" +#include "coin.h" +#include "crypto.h" +#include "view_internal.h" +#include + +#include +#include + +view_error_t view_printAddr() { + snprintf(viewdata.addr, MAX_CHARS_ADDR, "%s", (char *) (G_io_apdu_buffer + VIEW_ADDRESS_OFFSET_SECP256K1)); + splitValueField(); + return view_no_error; +} + +view_error_t view_printPath() { + bip32_to_str(viewdata.addr, MAX_CHARS_ADDR, hdPath, HDPATH_LEN_DEFAULT); + splitValueField(); + return view_no_error; +} diff --git a/cmake/conan/CMakeLists.txt b/cmake/conan/CMakeLists.txt new file mode 100644 index 00000000..4899e70e --- /dev/null +++ b/cmake/conan/CMakeLists.txt @@ -0,0 +1,12 @@ +# Download automatically, you can also just copy the conan.cmake file + +if (NOT EXISTS "${CMAKE_BINARY_DIR}/conan.cmake") + message(STATUS "Downloading conan.cmake from https://github.com/conan-io/cmake-conan") + file(DOWNLOAD "https://raw.githubusercontent.com/conan-io/cmake-conan/v0.13/conan.cmake" + "${CMAKE_BINARY_DIR}/conan.cmake") +endif () +include(${CMAKE_BINARY_DIR}/conan.cmake) + +conan_check(REQUIRED) + +conan_cmake_run(CONANFILE conanfile.txt BASIC_SETUP CMAKE_TARGETS BUILD missing) diff --git a/cmake/gtest/CMakeLists.txt b/cmake/gtest/CMakeLists.txt new file mode 100644 index 00000000..eea7d079 --- /dev/null +++ b/cmake/gtest/CMakeLists.txt @@ -0,0 +1,31 @@ +############################## +# Google Test +# Based on instructions in https://github.com/google/googletest/tree/master/googletest#incorporating-into-an-existing-cmake-project +# Download and unpack googletest at configure time +configure_file(CMakeLists.txt.gtest.in ${CMAKE_BINARY_DIR}/googletest-download/CMakeLists.txt) + +execute_process(COMMAND ${CMAKE_COMMAND} -G "${CMAKE_GENERATOR}" . + RESULT_VARIABLE result + WORKING_DIRECTORY ${CMAKE_BINARY_DIR}/googletest-download) +if (result) + message(FATAL_ERROR "CMake step for googletest failed: ${result}") +endif () + +execute_process(COMMAND ${CMAKE_COMMAND} --build . + RESULT_VARIABLE result + WORKING_DIRECTORY ${CMAKE_BINARY_DIR}/googletest-download) +if (result) + message(FATAL_ERROR "Build step for googletest failed: ${result}") +endif () + +# Prevent overriding the parent project's compiler/linker settings on Windows +set(gtest_force_shared_crt ON CACHE BOOL "" FORCE) + +add_subdirectory( + ${CMAKE_BINARY_DIR}/googletest-src + ${CMAKE_BINARY_DIR}/googletest-build +) + +if (CMAKE_VERSION VERSION_LESS 2.8.11) + include_directories("${gtest_SOURCE_DIR}/include") +endif () diff --git a/cmake/gtest/CMakeLists.txt.gtest.in b/cmake/gtest/CMakeLists.txt.gtest.in new file mode 100644 index 00000000..202d3c3d --- /dev/null +++ b/cmake/gtest/CMakeLists.txt.gtest.in @@ -0,0 +1,16 @@ +# Based on https://github.com/google/googletest/tree/master/googletest#incorporating-into-an-existing-cmake-project +cmake_minimum_required(VERSION 2.8.2) + +project(googletest-download NONE) + +include(ExternalProject) +ExternalProject_Add(googletest + GIT_REPOSITORY https://github.com/google/googletest.git + GIT_TAG master + SOURCE_DIR "${CMAKE_BINARY_DIR}/googletest-src" + BINARY_DIR "${CMAKE_BINARY_DIR}/googletest-build" + CONFIGURE_COMMAND "" + BUILD_COMMAND "" + INSTALL_COMMAND "" + TEST_COMMAND "" + ) diff --git a/conanfile.txt b/conanfile.txt new file mode 100644 index 00000000..501ad7d1 --- /dev/null +++ b/conanfile.txt @@ -0,0 +1,6 @@ +[requires] +jsoncpp/1.9.0@theirix/stable +fmt/6.0.0@bincrafters/stable + +[generators] +cmake diff --git a/deps/jsmn/src/jsmn.c b/deps/jsmn/src/jsmn.c index 8579f2e6..3d227c0a 100644 --- a/deps/jsmn/src/jsmn.c +++ b/deps/jsmn/src/jsmn.c @@ -4,18 +4,18 @@ * Allocates a fresh unused token from the token pull. */ static jsmntok_t *jsmn_alloc_token(jsmn_parser *parser, - jsmntok_t *tokens, size_t num_tokens) { - jsmntok_t *tok; - if (parser->toknext >= num_tokens) { - return NULL; - } - tok = &tokens[parser->toknext++]; - tok->start = tok->end = -1; - tok->size = 0; + jsmntok_t *tokens, size_t num_tokens) { + jsmntok_t *tok; + if (parser->toknext >= num_tokens) { + return NULL; + } + tok = &tokens[parser->toknext++]; + tok->start = tok->end = -1; + tok->size = 0; #ifdef JSMN_PARENT_LINKS - tok->parent = -1; + tok->parent = -1; #endif - return tok; + return tok; } /** @@ -23,283 +23,299 @@ static jsmntok_t *jsmn_alloc_token(jsmn_parser *parser, */ static void jsmn_fill_token(jsmntok_t *token, jsmntype_t type, short int start, short int end) { - token->type = type; - token->start = start; - token->end = end; - token->size = 0; + token->type = type; + token->start = start; + token->end = end; + token->size = 0; } /** * Fills next available token with JSON primitive. */ static int jsmn_parse_primitive(jsmn_parser *parser, const char *js, - size_t len, jsmntok_t *tokens, size_t num_tokens) { - jsmntok_t *token; - short int start; + size_t len, jsmntok_t *tokens, size_t num_tokens) { + jsmntok_t *token; + short int start; - start = parser->pos; + start = parser->pos; - for (; parser->pos < len && js[parser->pos] != '\0'; parser->pos++) { - switch (js[parser->pos]) { + for (; parser->pos < len && js[parser->pos] != '\0'; parser->pos++) { + switch (js[parser->pos]) { #ifndef JSMN_STRICT - /* In strict mode primitive must be followed by "," or "}" or "]" */ - case ':': + /* In strict mode primitive must be followed by "," or "}" or "]" */ + case ':': #endif - case '\t' : case '\r' : case '\n' : case ' ' : - case ',' : case ']' : case '}' : - goto found; - } - if (js[parser->pos] < 32 || js[parser->pos] >= 127) { - parser->pos = start; - return JSMN_ERROR_INVAL; - } - } + case '\t' : + case '\r' : + case '\n' : + case ' ' : + case ',' : + case ']' : + case '}' : + goto found; + } + if (js[parser->pos] < 32 || js[parser->pos] >= 127) { + parser->pos = start; + return JSMN_ERROR_INVAL; + } + } #ifdef JSMN_STRICT - /* In strict mode primitive must be followed by a comma/object/array */ - parser->pos = start; - return JSMN_ERROR_PART; + /* In strict mode primitive must be followed by a comma/object/array */ + parser->pos = start; + return JSMN_ERROR_PART; #endif -found: - if (tokens == NULL) { - parser->pos--; - return 0; - } - token = jsmn_alloc_token(parser, tokens, num_tokens); - if (token == NULL) { - parser->pos = start; - return JSMN_ERROR_NOMEM; - } - jsmn_fill_token(token, JSMN_PRIMITIVE, start, parser->pos); + found: + if (tokens == NULL) { + parser->pos--; + return 0; + } + token = jsmn_alloc_token(parser, tokens, num_tokens); + if (token == NULL) { + parser->pos = start; + return JSMN_ERROR_NOMEM; + } + jsmn_fill_token(token, JSMN_PRIMITIVE, start, parser->pos); #ifdef JSMN_PARENT_LINKS - token->parent = parser->toksuper; + token->parent = parser->toksuper; #endif - parser->pos--; - return 0; + parser->pos--; + return 0; } /** * Fills next token with JSON string. */ static int jsmn_parse_string(jsmn_parser *parser, const char *js, - size_t len, jsmntok_t *tokens, size_t num_tokens) { - jsmntok_t *token; + size_t len, jsmntok_t *tokens, size_t num_tokens) { + jsmntok_t *token; - short int start = parser->pos; + short int start = parser->pos; - parser->pos++; + parser->pos++; - /* Skip starting quote */ - for (; parser->pos < len && js[parser->pos] != '\0'; parser->pos++) { - char c = js[parser->pos]; + /* Skip starting quote */ + for (; parser->pos < len && js[parser->pos] != '\0'; parser->pos++) { + char c = js[parser->pos]; - /* Quote: end of string */ - if (c == '\"') { - if (tokens == NULL) { - return 0; - } - token = jsmn_alloc_token(parser, tokens, num_tokens); - if (token == NULL) { - parser->pos = start; - return JSMN_ERROR_NOMEM; - } - jsmn_fill_token(token, JSMN_STRING, start+1, parser->pos); + /* Quote: end of string */ + if (c == '\"') { + if (tokens == NULL) { + return 0; + } + token = jsmn_alloc_token(parser, tokens, num_tokens); + if (token == NULL) { + parser->pos = start; + return JSMN_ERROR_NOMEM; + } + jsmn_fill_token(token, JSMN_STRING, start + 1, parser->pos); #ifdef JSMN_PARENT_LINKS - token->parent = parser->toksuper; + token->parent = parser->toksuper; #endif - return 0; - } + return 0; + } - /* Backslash: Quoted symbol expected */ - if (c == '\\' && parser->pos + 1 < len) { - short int i; - parser->pos++; - switch (js[parser->pos]) { - /* Allowed escaped symbols */ - case '\"': case '/' : case '\\' : case 'b' : - case 'f' : case 'r' : case 'n' : case 't' : - break; - /* Allows escaped symbol \uXXXX */ - case 'u': - parser->pos++; - for(i = 0; i < 4 && parser->pos < len && js[parser->pos] != '\0'; i++) { - /* If it isn't a hex character we have an error */ - if(!((js[parser->pos] >= 48 && js[parser->pos] <= 57) || /* 0-9 */ - (js[parser->pos] >= 65 && js[parser->pos] <= 70) || /* A-F */ - (js[parser->pos] >= 97 && js[parser->pos] <= 102))) { /* a-f */ - parser->pos = start; - return JSMN_ERROR_INVAL; - } - parser->pos++; - } - parser->pos--; - break; - /* Unexpected symbol */ - default: - parser->pos = start; - return JSMN_ERROR_INVAL; - } - } - } - parser->pos = start; - return JSMN_ERROR_PART; + /* Backslash: Quoted symbol expected */ + if (c == '\\' && parser->pos + 1 < len) { + short int i; + parser->pos++; + switch (js[parser->pos]) { + /* Allowed escaped symbols */ + case '\"': + case '/' : + case '\\' : + case 'b' : + case 'f' : + case 'r' : + case 'n' : + case 't' : + break; + /* Allows escaped symbol \uXXXX */ + case 'u': + parser->pos++; + for (i = 0; i < 4 && parser->pos < len && js[parser->pos] != '\0'; i++) { + /* If it isn't a hex character we have an error */ + if (!((js[parser->pos] >= 48 && js[parser->pos] <= 57) || /* 0-9 */ + (js[parser->pos] >= 65 && js[parser->pos] <= 70) || /* A-F */ + (js[parser->pos] >= 97 && js[parser->pos] <= 102))) { /* a-f */ + parser->pos = start; + return JSMN_ERROR_INVAL; + } + parser->pos++; + } + parser->pos--; + break; + /* Unexpected symbol */ + default: + parser->pos = start; + return JSMN_ERROR_INVAL; + } + } + } + parser->pos = start; + return JSMN_ERROR_PART; } /** * Parse JSON string and fill tokens. */ int jsmn_parse(jsmn_parser *parser, const char *js, size_t len, - jsmntok_t *tokens, unsigned int num_tokens) { + jsmntok_t *tokens, unsigned int num_tokens) { short int r; - short int i; - jsmntok_t *token; - short int count = parser->toknext; + short int i; + jsmntok_t *token; + short int count = parser->toknext; - for (; parser->pos < len && js[parser->pos] != '\0'; parser->pos++) { - char c; - jsmntype_t type; + for (; parser->pos < len && js[parser->pos] != '\0'; parser->pos++) { + char c; + jsmntype_t type; - c = js[parser->pos]; - switch (c) { - case '{': case '[': - count++; - if (tokens == NULL) { - break; - } - token = jsmn_alloc_token(parser, tokens, num_tokens); - if (token == NULL) - return JSMN_ERROR_NOMEM; - if (parser->toksuper != -1) { - tokens[parser->toksuper].size++; + c = js[parser->pos]; + switch (c) { + case '{': + case '[': + count++; + if (tokens == NULL) { + break; + } + token = jsmn_alloc_token(parser, tokens, num_tokens); + if (token == NULL) + return JSMN_ERROR_NOMEM; + if (parser->toksuper != -1) { + tokens[parser->toksuper].size++; #ifdef JSMN_PARENT_LINKS - token->parent = parser->toksuper; + token->parent = parser->toksuper; #endif - } - token->type = (c == '{' ? JSMN_OBJECT : JSMN_ARRAY); - token->start = parser->pos; - parser->toksuper = parser->toknext - 1; - break; - case '}': case ']': - if (tokens == NULL) - break; - type = (c == '}' ? JSMN_OBJECT : JSMN_ARRAY); + } + token->type = (c == '{' ? JSMN_OBJECT : JSMN_ARRAY); + token->start = parser->pos; + parser->toksuper = parser->toknext - 1; + break; + case '}': + case ']': + if (tokens == NULL) + break; + type = (c == '}' ? JSMN_OBJECT : JSMN_ARRAY); #ifdef JSMN_PARENT_LINKS - if (parser->toknext < 1) { - return JSMN_ERROR_INVAL; - } - token = &tokens[parser->toknext - 1]; - for (;;) { - if (token->start != -1 && token->end == -1) { - if (token->type != type) { - return JSMN_ERROR_INVAL; - } - token->end = parser->pos + 1; - parser->toksuper = token->parent; - break; - } - if (token->parent == -1) { - if(token->type != type || parser->toksuper == -1) { - return JSMN_ERROR_INVAL; - } - break; - } - token = &tokens[token->parent]; - } + if (parser->toknext < 1) { + return JSMN_ERROR_INVAL; + } + token = &tokens[parser->toknext - 1]; + for (;;) { + if (token->start != -1 && token->end == -1) { + if (token->type != type) { + return JSMN_ERROR_INVAL; + } + token->end = parser->pos + 1; + parser->toksuper = token->parent; + break; + } + if (token->parent == -1) { + if(token->type != type || parser->toksuper == -1) { + return JSMN_ERROR_INVAL; + } + break; + } + token = &tokens[token->parent]; + } #else - for (i = parser->toknext - 1; i >= 0; i--) { - token = &tokens[i]; - if (token->start != -1 && token->end == -1) { - if (token->type != type) { - return JSMN_ERROR_INVAL; - } - parser->toksuper = -1; - token->end = parser->pos + 1; - break; - } - } - /* Error if unmatched closing bracket */ - if (i == -1) return JSMN_ERROR_INVAL; - for (; i >= 0; i--) { - token = &tokens[i]; - if (token->start != -1 && token->end == -1) { - parser->toksuper = i; - break; - } - } + for (i = parser->toknext - 1; i >= 0; i--) { + token = &tokens[i]; + if (token->start != -1 && token->end == -1) { + if (token->type != type) { + return JSMN_ERROR_INVAL; + } + parser->toksuper = -1; + token->end = parser->pos + 1; + break; + } + } + /* Error if unmatched closing bracket */ + if (i == -1) return JSMN_ERROR_INVAL; + for (; i >= 0; i--) { + token = &tokens[i]; + if (token->start != -1 && token->end == -1) { + parser->toksuper = i; + break; + } + } #endif - break; - case '\"': - r = jsmn_parse_string(parser, js, len, tokens, num_tokens); - if (r < 0) return r; - count++; - if (parser->toksuper != -1 && tokens != NULL) - tokens[parser->toksuper].size++; - break; - case '\t' : case '\r' : case '\n' : case ' ': - break; - case ':': - parser->toksuper = parser->toknext - 1; - break; - case ',': - if (tokens != NULL && parser->toksuper != -1 && - tokens[parser->toksuper].type != JSMN_ARRAY && - tokens[parser->toksuper].type != JSMN_OBJECT) { + break; + case '\"': + r = jsmn_parse_string(parser, js, len, tokens, num_tokens); + if (r < 0) return r; + count++; + if (parser->toksuper != -1 && tokens != NULL) + tokens[parser->toksuper].size++; + break; + case '\t' : + case '\r' : + case '\n' : + case ' ': + break; + case ':': + parser->toksuper = parser->toknext - 1; + break; + case ',': + if (tokens != NULL && parser->toksuper != -1 && + tokens[parser->toksuper].type != JSMN_ARRAY && + tokens[parser->toksuper].type != JSMN_OBJECT) { #ifdef JSMN_PARENT_LINKS - parser->toksuper = tokens[parser->toksuper].parent; + parser->toksuper = tokens[parser->toksuper].parent; #else - for (i = parser->toknext - 1; i >= 0; i--) { - if (tokens[i].type == JSMN_ARRAY || tokens[i].type == JSMN_OBJECT) { - if (tokens[i].start != -1 && tokens[i].end == -1) { - parser->toksuper = i; - break; - } - } - } + for (i = parser->toknext - 1; i >= 0; i--) { + if (tokens[i].type == JSMN_ARRAY || tokens[i].type == JSMN_OBJECT) { + if (tokens[i].start != -1 && tokens[i].end == -1) { + parser->toksuper = i; + break; + } + } + } #endif - } - break; + } + break; #ifdef JSMN_STRICT - /* In strict mode primitives are: numbers and booleans */ - case '-': case '0': case '1' : case '2': case '3' : case '4': - case '5': case '6': case '7' : case '8': case '9': - case 't': case 'f': case 'n' : - /* And they must not be keys of the object */ - if (tokens != NULL && parser->toksuper != -1) { - jsmntok_t *t = &tokens[parser->toksuper]; - if (t->type == JSMN_OBJECT || - (t->type == JSMN_STRING && t->size != 0)) { - return JSMN_ERROR_INVAL; - } - } + /* In strict mode primitives are: numbers and booleans */ + case '-': case '0': case '1' : case '2': case '3' : case '4': + case '5': case '6': case '7' : case '8': case '9': + case 't': case 'f': case 'n' : + /* And they must not be keys of the object */ + if (tokens != NULL && parser->toksuper != -1) { + jsmntok_t *t = &tokens[parser->toksuper]; + if (t->type == JSMN_OBJECT || + (t->type == JSMN_STRING && t->size != 0)) { + return JSMN_ERROR_INVAL; + } + } #else - /* In non-strict mode every unquoted value is a primitive */ - default: + /* In non-strict mode every unquoted value is a primitive */ + default: #endif - r = jsmn_parse_primitive(parser, js, len, tokens, num_tokens); - if (r < 0) return r; - count++; - if (parser->toksuper != -1 && tokens != NULL) - tokens[parser->toksuper].size++; - break; + r = jsmn_parse_primitive(parser, js, len, tokens, num_tokens); + if (r < 0) return r; + count++; + if (parser->toksuper != -1 && tokens != NULL) + tokens[parser->toksuper].size++; + break; #ifdef JSMN_STRICT - /* Unexpected char in strict mode */ - default: - return JSMN_ERROR_INVAL; + /* Unexpected char in strict mode */ + default: + return JSMN_ERROR_INVAL; #endif - } - } + } + } - if (tokens != NULL) { - for (i = parser->toknext - 1; i >= 0; i--) { - /* Unmatched opened object or array */ - if (tokens[i].start != -1 && tokens[i].end == -1) { - return JSMN_ERROR_PART; - } - } - } + if (tokens != NULL) { + for (i = parser->toknext - 1; i >= 0; i--) { + /* Unmatched opened object or array */ + if (tokens[i].start != -1 && tokens[i].end == -1) { + return JSMN_ERROR_PART; + } + } + } - return count; + return count; } /** @@ -307,8 +323,8 @@ int jsmn_parse(jsmn_parser *parser, const char *js, size_t len, * available. */ void jsmn_init(jsmn_parser *parser) { - parser->pos = 0; - parser->toknext = 0; - parser->toksuper = -1; + parser->pos = 0; + parser->toknext = 0; + parser->toksuper = -1; } diff --git a/deps/ledger-zxlib/.circleci/config.yml b/deps/ledger-zxlib/.circleci/config.yml new file mode 100644 index 00000000..64441bcb --- /dev/null +++ b/deps/ledger-zxlib/.circleci/config.yml @@ -0,0 +1,19 @@ +version: 2 +jobs: + build: + docker: + - image: ubuntu:18.04 + steps: + - run: + name: Install dependencies + command: apt update && apt-get -y install build-essential git sudo wget cmake libssl-dev libgmp-dev autoconf libtool + - checkout + - run: git submodule update --init --recursive + - run: cmake -DDISABLE_DOCKER_BUILDS=ON . && make + - run: export GTEST_COLOR=1 && ctest -VV + +workflows: + version: 2 + build_all: + jobs: + - build diff --git a/deps/ledger-zxlib/.editorconfig b/deps/ledger-zxlib/.editorconfig index 4e47fb96..2d7db14f 100644 --- a/deps/ledger-zxlib/.editorconfig +++ b/deps/ledger-zxlib/.editorconfig @@ -10,3 +10,7 @@ insert_final_newline = true [*.{c,h,cpp,hpp}] indent_style = space indent_size = 4 + +[*.{yml,sh}] +indent_style = space +indent_size = 2 diff --git a/deps/ledger-zxlib/CMakeLists.txt b/deps/ledger-zxlib/CMakeLists.txt index cad1ebb3..43db7a07 100644 --- a/deps/ledger-zxlib/CMakeLists.txt +++ b/deps/ledger-zxlib/CMakeLists.txt @@ -1,5 +1,5 @@ #******************************************************************************* -#* (c) 2018 ZondaX GmbH +#* (c) 2018 Zondax GmbH #* #* Licensed under the Apache License, Version 2.0 (the "License"); #* you may not use this file except in compliance with the License. @@ -55,4 +55,4 @@ target_include_directories(zxlib_tests PRIVATE target_link_libraries(zxlib_tests gtest_main zxlib) -add_test(ZXLIB_TESTS ledger_qrl_tests) +add_test(ZXLIB_TESTS zxlib_tests) diff --git a/deps/ledger-zxlib/LICENSE b/deps/ledger-zxlib/LICENSE index c8e8e95a..0fa613e8 100644 --- a/deps/ledger-zxlib/LICENSE +++ b/deps/ledger-zxlib/LICENSE @@ -186,7 +186,7 @@ same "printed page" as the copyright notice for easier identification within third-party archives. - Copyright 2018 ZondaX GmbH + Copyright 2018-2020 Zondax GmbH Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. diff --git a/deps/ledger-zxlib/README.md b/deps/ledger-zxlib/README.md index 6ab06178..9dabbf58 100644 --- a/deps/ledger-zxlib/README.md +++ b/deps/ledger-zxlib/README.md @@ -1 +1,5 @@ -ledger-zxlib +# ledger-zxlib + +[![License](https://img.shields.io/badge/License-Apache%202.0-blue.svg)](https://opensource.org/licenses/Apache-2.0) +[![CircleCI](https://circleci.com/gh/Zondax/ledger-zxlib/tree/master.svg?style=shield)](https://circleci.com/gh/Zondax/ledger-zxlib/tree/master) +[![CodeFactor](https://www.codefactor.io/repository/github/zondax/ledger-zxlib/badge)](https://www.codefactor.io/repository/github/zondax/ledger-zxlib) diff --git a/deps/ledger-zxlib/app/common/app_mode.c b/deps/ledger-zxlib/app/common/app_mode.c new file mode 100644 index 00000000..d7924f14 --- /dev/null +++ b/deps/ledger-zxlib/app/common/app_mode.c @@ -0,0 +1,77 @@ +/******************************************************************************* +* (c) 2020 Zondax GmbH +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +********************************************************************************/ + +#include "app_mode.h" + +typedef struct { + uint32_t expert; +} app_mode_t; + +#if defined(TARGET_NANOS) || defined(TARGET_NANOX) +////////////////////////////////////////////////////////////// +////////////////////////////////////////////////////////////// +////////////////////////////////////////////////////////////// +////////////////////////////////////////////////////////////// + +//NV_CONST app_mode_t N_appmode NV_ALIGN; +//#define N_APPMODE_PTR ((NV_VOL app_mode_t *)PIC(&N_appmode)) +app_mode_t app_mode; + +void app_mode_reset(){ + app_mode.expert = 0; +} + +bool app_mode_expert() { +// TODO: read from NVRAM +// app_mode_t *p = N_APPMODE_PTR; +// uint8_t expert = p->expert; +// return expert; +// app_mode_t* p =(NV_VOL app_mode_t *)PIC(&N_appmode_impl); +// return p->expert; + return app_mode.expert; +} + +void app_mode_set_expert(uint8_t val) { +// TODO: write to NVRAM + app_mode.expert = val; +} + +#else +////////////////////////////////////////////////////////////// +////////////////////////////////////////////////////////////// +////////////////////////////////////////////////////////////// +////////////////////////////////////////////////////////////// + +app_mode_t app_mode; + +void app_mode_reset() { + app_mode.expert = 0; +} + +bool app_mode_expert() { + return app_mode.expert; +} + +void app_mode_set_expert(uint8_t val) { + app_mode.expert = val; +} + +////////////////////////////////////////////////////////////// +////////////////////////////////////////////////////////////// +////////////////////////////////////////////////////////////// +////////////////////////////////////////////////////////////// + +#endif diff --git a/src/view_expl.h b/deps/ledger-zxlib/app/common/app_mode.h similarity index 70% rename from src/view_expl.h rename to deps/ledger-zxlib/app/common/app_mode.h index 279625d3..1142dd54 100644 --- a/src/view_expl.h +++ b/deps/ledger-zxlib/app/common/app_mode.h @@ -1,6 +1,6 @@ /******************************************************************************* * (c) 2016 Ledger -* (c) 2018, 2019 ZondaX GmbH +* (c) 2018 Zondax GmbH * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -15,15 +15,19 @@ * limitations under the License. ********************************************************************************/ #pragma once +#include "zxmacros.h" +#include "stdbool.h" -#include "os.h" -#include "cx.h" -#include "view_common.h" +#ifdef __cplusplus +extern "C" { +#endif -// Initialize and show control -void viewexpl_start( - int start_page, - viewctl_delegate_getData delegate_update, - viewctl_delegate_ready delegate_ready, - viewctl_delegate_exit delegate_exit - ); +void app_mode_reset(); + +bool app_mode_expert(); + +void app_mode_set_expert(uint8_t val); + +#ifdef __cplusplus +} +#endif diff --git a/deps/ledger-zxlib/app/common/view.c b/deps/ledger-zxlib/app/common/view.c new file mode 100644 index 00000000..258d2604 --- /dev/null +++ b/deps/ledger-zxlib/app/common/view.c @@ -0,0 +1,211 @@ +/******************************************************************************* +* (c) 2018, 2019 Zondax GmbH +* (c) 2016 Ledger +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +********************************************************************************/ + +#include "view.h" +#include "coin.h" +#include "view_internal.h" +#include "crypto.h" + +#include "actions.h" +#include "apdu_codes.h" +#include "glyphs.h" +#include "bagl.h" +#include "zxmacros.h" +#include "view_templates.h" +#include "tx.h" +#include "app_mode.h" + +#include +#include + +view_t viewdata; + +void h_address_accept(unsigned int _) { + UNUSED(_); + view_idle_show(0); + UX_WAIT(); + app_reply_address(viewdata.addrKind); +} + +void h_error_accept(unsigned int _) { + UNUSED(_); + view_idle_show(0); + UX_WAIT(); + app_reply_error(); +} + +void h_sign_accept(unsigned int _) { + UNUSED(_); + app_sign(); + view_idle_show(0); + UX_WAIT(); +} + +void h_sign_reject(unsigned int _) { + UNUSED(_); + view_idle_show(0); + UX_WAIT(); + + set_code(G_io_apdu_buffer, 0, APDU_CODE_COMMAND_NOT_ALLOWED); + io_exchange(CHANNEL_APDU | IO_RETURN_AFTER_TX, 2); +} + +void h_paging_init() { + viewdata.itemIdx = 0; + viewdata.pageIdx = 0; + viewdata.pageCount = 1; +} + +uint8_t h_paging_can_increase() { + if (viewdata.pageIdx + 1 < viewdata.pageCount) { + return 1; + } else { + // passed page count, go to next index + if (viewdata.itemIdx + 1 < viewdata.itemCount) { + return 1; + } + } + return 0; +} + +void h_paging_increase() { + if (viewdata.pageIdx + 1 < viewdata.pageCount) { + // increase page + viewdata.pageIdx++; + } else { + // passed page count, go to next index + if (viewdata.itemIdx + 1 < viewdata.itemCount) { + viewdata.itemIdx++; + viewdata.pageIdx = 0; + } + } +} + +uint8_t h_paging_can_decrease() { + if (viewdata.pageIdx != 0) { + return 1; + } else { + if (viewdata.itemIdx > 0) { + return 1; + } + } + return 0; +} + +void h_paging_decrease() { + if (viewdata.pageIdx != 0) { + viewdata.pageIdx--; + } else { + if (viewdata.itemIdx > 0) { + viewdata.itemIdx--; + // jump to last page. update will cap this value + viewdata.pageIdx = VIEW_ADDRESS_LAST_PAGE_DEFAULT; + } + } +} + +void h_paging_set_page_count(uint8_t pageCount) { + viewdata.pageCount = pageCount; + if (viewdata.pageIdx > viewdata.pageCount) { + viewdata.pageIdx = viewdata.pageCount - 1; + } +} + +view_error_t h_review_update_data() { + tx_error_t err = tx_no_error; + + do { + err = tx_getNumItems(&viewdata.itemCount); + viewdata.itemCount++; + + if (err == tx_no_data) { + return view_no_data; + } + + if (err != tx_no_error) { + return view_error_detected; + } + + err = tx_getItem(viewdata.itemIdx, + viewdata.key, MAX_CHARS_PER_KEY_LINE, + viewdata.value, MAX_CHARS_PER_VALUE1_LINE, + viewdata.pageIdx, &viewdata.pageCount); + + if (err == tx_no_data) { + return view_no_data; + } + + if (err != tx_no_error) { + return view_error_detected; + } + + if (viewdata.pageCount == 0) { + h_paging_increase(); + } + } while (viewdata.pageCount == 0); + + splitValueField(); + return view_no_error; +} + +view_error_t h_addr_update_item(uint8_t idx) { + MEMZERO(viewdata.value, MAX_CHARS_PER_VALUE1_LINE); + + if (idx == 0) { + return view_printAddr(); + } + + if (idx == 1 && app_mode_expert()) { + return view_printPath(); + } + + return view_error_detected; +} + +void io_seproxyhal_display(const bagl_element_t *element) { + io_seproxyhal_display_default((bagl_element_t *) element); +} + +void view_init(void) { + UX_INIT(); +} + +void view_idle_show(uint8_t item_idx) { + view_idle_show_impl(item_idx); +} + +void view_address_show(address_kind_e addressKind) { + viewdata.addrKind = addressKind; + + viewdata.itemCount = 1; + if (app_mode_expert()) { + viewdata.itemCount = 2; + } + + view_address_show_impl(); +} + +void view_error_show() { + snprintf(viewdata.key, MAX_CHARS_PER_KEY_LINE, "ERROR"); + snprintf(viewdata.value, MAX_CHARS_PER_VALUE1_LINE, "SHOWING DATA"); + splitValueField(); + view_error_show_impl(); +} + +void view_sign_show() { + view_sign_show_impl(); +} diff --git a/src/view.h b/deps/ledger-zxlib/app/common/view.h similarity index 55% rename from src/view.h rename to deps/ledger-zxlib/app/common/view.h index 051b92ea..8e3c4481 100644 --- a/src/view.h +++ b/deps/ledger-zxlib/app/common/view.h @@ -1,6 +1,6 @@ /******************************************************************************* +* (c) 2018-2020 Zondax GmbH * (c) 2016 Ledger -* (c) 2018 ZondaX GmbH * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -16,29 +16,28 @@ ********************************************************************************/ #pragma once +#include +#include "coin.h" + +#if defined(LEDGER_SPECIFIC) +#include "bolos_target.h" +#if defined(BOLOS_SDK) #include "os.h" #include "cx.h" -#include "view_common.h" -#include "view_expl.h" - -//------ Event handlers -/// view_set_handlers -void view_set_handlers(viewctl_delegate_getData func_getData, - viewctl_delegate_accept func_accept, - viewctl_delegate_reject func_reject); +#endif +#endif -//------ Common functions /// view_init (initializes UI) -void view_init(void); +void view_init(); -/// view_idle (idle view - main menu + status) -void view_idle(unsigned int ignored); +/// view_idle_show (idle view - main menu + status) +void view_idle_show(uint8_t item_idx); -/// view_status -void view_status(); +/// view_error (error view) +void view_error_show(); -/// view_tx_show (show/review transaction view) -void view_tx_show(unsigned int start_page); +// shows address in the screen +void view_address_show(address_kind_e addressKind); -/// view_addr_confirm (show/accept public key + address request) -void view_addr_confirm(unsigned int _); +// Shows review screen + later sign menu +void view_sign_show(); diff --git a/deps/ledger-zxlib/app/common/view_internal.h b/deps/ledger-zxlib/app/common/view_internal.h new file mode 100644 index 00000000..6c530006 --- /dev/null +++ b/deps/ledger-zxlib/app/common/view_internal.h @@ -0,0 +1,120 @@ +/******************************************************************************* +* (c) 2019 Zondax GmbH +* (c) 2016 Ledger +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +********************************************************************************/ +#pragma once + +#include +#include "coin.h" + +#define CUR_FLOW G_ux.flow_stack[G_ux.stack_count-1] + +#if defined(TARGET_NANOX) +#define MAX_CHARS_PER_KEY_LINE 64 +#define MAX_CHARS_PER_VALUE1_LINE 4096 +#define MAX_CHARS_HEXMESSAGE 160 +#else +#define MAX_CHARS_PER_KEY_LINE (32+1) +#define MAX_CHARS_PER_VALUE_LINE (18) +#define MAX_CHARS_PER_VALUE1_LINE (2*MAX_CHARS_PER_VALUE_LINE+1) +#define MAX_CHARS_PER_VALUE2_LINE (MAX_CHARS_PER_VALUE_LINE+1) +#define MAX_CHARS_HEXMESSAGE 40 +#endif +#define MAX_CHARS_ADDR (MAX_CHARS_PER_KEY_LINE + MAX_CHARS_PER_VALUE1_LINE) + +// This takes data from G_io_apdu_buffer that is prefilled with the address + +typedef struct { + union { + struct { + char key[MAX_CHARS_PER_KEY_LINE]; + char value[MAX_CHARS_PER_VALUE1_LINE]; +#if defined(TARGET_NANOS) + char value2[MAX_CHARS_PER_VALUE2_LINE]; +#endif + }; + struct { + char addr[MAX_CHARS_ADDR]; + }; + }; + address_kind_e addrKind; + uint8_t itemIdx; + uint8_t itemCount; + uint8_t pageIdx; + uint8_t pageCount; +} view_t; + +extern view_t viewdata; + +typedef enum { + view_no_error = 0, + view_no_data = 1, + view_error_detected = 2 +} view_error_t; + +#define print_title(...) snprintf(viewdata.title, sizeof(viewdata.title), __VA_ARGS__) +#define print_key(...) snprintf(viewdata.key, sizeof(viewdata.key), __VA_ARGS__); +#define print_value(...) snprintf(viewdata.value, sizeof(viewdata.value), __VA_ARGS__); + +#if defined(TARGET_NANOS) +#define print_value2(...) snprintf(viewdata.value2, sizeof(viewdata.value2), __VA_ARGS__); +#endif + +void splitValueField(); + +/////////////////////////////////////////////// +/////////////////////////////////////////////// +/////////////////////////////////////////////// +/////////////////////////////////////////////// +/////////////////////////////////////////////// +/////////////////////////////////////////////// +/////////////////////////////////////////////// +/////////////////////////////////////////////// + +void view_idle_show_impl(uint8_t item_idx); + +void view_address_show_impl(); + +void view_error_show_impl(); + +void view_sign_show_impl(); + +void h_address_accept(unsigned int _); + +void h_error_accept(unsigned int _); + +void h_sign_accept(unsigned int _); + +void h_sign_reject(unsigned int _); + +void h_paging_init(); + +uint8_t h_paging_can_increase(); + +void h_paging_increase(); + +uint8_t h_paging_can_decrease(); + +void h_paging_decrease(); + +void h_paging_set_page_count(uint8_t pageCount); + +view_error_t h_review_update_data(); + +view_error_t h_addr_update_item(uint8_t idx); + +view_error_t view_printAddr(); + +view_error_t view_printPath(); diff --git a/deps/ledger-zxlib/app/common/view_s.c b/deps/ledger-zxlib/app/common/view_s.c new file mode 100644 index 00000000..8930f85a --- /dev/null +++ b/deps/ledger-zxlib/app/common/view_s.c @@ -0,0 +1,386 @@ +/******************************************************************************* +* (c) 2018, 2019 Zondax GmbH +* (c) 2016 Ledger +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +********************************************************************************/ + +#include "app_mode.h" +#include "view.h" +#include "view_internal.h" +#include "actions.h" +#include "apdu_codes.h" +#include "glyphs.h" +#include "bagl.h" +#include "zxmacros.h" +#include "view_templates.h" +#include "tx.h" + +#include +#include + +#if defined(TARGET_NANOS) + +void h_expert_toggle(); +void h_expert_update(); +void h_review_button_left(); +void h_review_button_right(); +void view_review_show(); +void view_sign_show_s(); + +ux_state_t ux; + +void os_exit(uint32_t id) { + os_sched_exit(0); +} + +const ux_menu_entry_t menu_main[] = { + {NULL, NULL, 0, &C_icon_app, MENU_MAIN_APP_LINE1, MENU_MAIN_APP_LINE2, 33, 12}, + {NULL, h_expert_toggle, 0, &C_icon_app, "Expert mode:", viewdata.value, 33, 12}, + {NULL, NULL, 0, &C_icon_app, APPVERSION_LINE1, APPVERSION_LINE2, 33, 12}, + {NULL, NULL, 0, &C_icon_app, "Developed by:", "Zondax.ch", 33, 12}, + {NULL, NULL, 0, &C_icon_app, "License: ", "Apache 2.0", 33, 12}, + {NULL, os_exit, 0, &C_icon_dashboard, "Quit", NULL, 50, 29}, + UX_MENU_END +}; + +#if !defined(HAVE_UX_FLOW) +void h_addr_button_left(); +void h_addr_button_right(); +void h_addr_button_both(); +void view_addr_show(); + +static const bagl_element_t view_address[] = { + UI_BACKGROUND_LEFT_RIGHT_ICONS, + UI_LabelLine(UIID_LABEL + 0, 0, 8, UI_SCREEN_WIDTH, UI_11PX, UI_WHITE, UI_BLACK, viewdata.key), + UI_LabelLine(UIID_LABEL + 0, 0, 19, UI_SCREEN_WIDTH, UI_11PX, UI_WHITE, UI_BLACK, viewdata.value), + UI_LabelLineScrolling(UIID_LABELSCROLL, 0, 30, 128, UI_11PX, UI_WHITE, UI_BLACK, viewdata.value2), +}; +#else +UX_STEP_NOCB_INIT(ux_addr_flow_1_step, paging, + { h_addr_update_item(CUR_FLOW.index); }, + { .title = "Address", .text = viewdata.addr, }); +UX_STEP_NOCB_INIT(ux_addr_flow_2_step, paging, + { h_addr_update_item(CUR_FLOW.index); }, + { .title = "Path", .text = viewdata.addr, }); +UX_STEP_VALID(ux_addr_flow_3_step, pb, h_address_accept(0), { &C_icon_validate_14, "Ok"}); + +UX_FLOW( + ux_addr_flow_no_path, + &ux_addr_flow_1_step, + &ux_addr_flow_3_step +); + +UX_FLOW( + ux_addr_flow_with_path, + &ux_addr_flow_1_step, + &ux_addr_flow_2_step, + &ux_addr_flow_3_step +); +#endif + +void h_review(unsigned int _) { UNUSED(_); view_sign_show_impl(); } + +const ux_menu_entry_t menu_sign[] = { + {NULL, h_sign_accept, 0, NULL, "Approve", NULL, 0, 0}, + {NULL, h_sign_reject, 0, NULL, "Reject", NULL, 0, 0}, + UX_MENU_END +}; + +static const bagl_element_t view_review[] = { + UI_BACKGROUND_LEFT_RIGHT_ICONS, + UI_LabelLine(UIID_LABEL + 0, 0, 8, UI_SCREEN_WIDTH, UI_11PX, UI_WHITE, UI_BLACK, viewdata.key), + UI_LabelLine(UIID_LABEL + 1, 0, 19, UI_SCREEN_WIDTH, UI_11PX, UI_WHITE, UI_BLACK, viewdata.value), + UI_LabelLine(UIID_LABEL + 2, 0, 30, UI_SCREEN_WIDTH, UI_11PX, UI_WHITE, UI_BLACK, viewdata.value2), +}; + +static const bagl_element_t view_error[] = { + UI_FillRectangle(0, 0, 0, UI_SCREEN_WIDTH, UI_SCREEN_HEIGHT, 0x000000, 0xFFFFFF), + UI_Icon(0, 128 - 7, 0, 7, 7, BAGL_GLYPH_ICON_CHECK), + UI_LabelLine(UIID_LABEL + 0, 0, 8, UI_SCREEN_WIDTH, UI_11PX, UI_WHITE, UI_BLACK, viewdata.key), + UI_LabelLine(UIID_LABEL + 0, 0, 19, UI_SCREEN_WIDTH, UI_11PX, UI_WHITE, UI_BLACK, viewdata.value), + UI_LabelLineScrolling(UIID_LABELSCROLL, 0, 30, 128, UI_11PX, UI_WHITE, UI_BLACK, viewdata.value2), +}; + +#if !defined(HAVE_UX_FLOW) +static unsigned int view_address_button(unsigned int button_mask, unsigned int button_mask_counter) { + switch (button_mask) { + case BUTTON_EVT_RELEASED | BUTTON_LEFT | BUTTON_RIGHT: + // Press both left and right buttons to quit + h_addr_button_both(); + break; + + case BUTTON_EVT_RELEASED | BUTTON_LEFT: + // Press left to progress to the previous element + h_addr_button_left(); + break; + + case BUTTON_EVT_RELEASED | BUTTON_RIGHT: + // Press right to progress to the next element + h_addr_button_right(); + break; + } + return 0; +} +#endif + +static unsigned int view_error_button(unsigned int button_mask, unsigned int button_mask_counter) { + switch (button_mask) { + case BUTTON_EVT_RELEASED | BUTTON_LEFT | BUTTON_RIGHT: + case BUTTON_EVT_RELEASED | BUTTON_LEFT: + break; + case BUTTON_EVT_RELEASED | BUTTON_RIGHT: + h_error_accept(0); + break; + } + return 0; +} + +static unsigned int view_review_button(unsigned int button_mask, unsigned int button_mask_counter) { + switch (button_mask) { + case BUTTON_EVT_RELEASED | BUTTON_LEFT | BUTTON_RIGHT: + if (app_mode_expert()) { + // Press both left and right buttons to quit + view_sign_show_s(); + } + break; + case BUTTON_EVT_RELEASED | BUTTON_LEFT: + // Press left to progress to the previous element + h_review_button_left(); + break; + + case BUTTON_EVT_RELEASED | BUTTON_RIGHT: + // Press right to progress to the next element + h_review_button_right(); + break; + } + return 0; +} + +const bagl_element_t *view_prepro(const bagl_element_t *element) { + switch (element->component.userid) { + case UIID_ICONLEFT: + if (!h_paging_can_decrease()){ + return NULL; + } + UX_CALLBACK_SET_INTERVAL(2000); + break; + case UIID_ICONRIGHT: + if (!h_paging_can_increase()){ + return NULL; + } + UX_CALLBACK_SET_INTERVAL(2000); + break; + case UIID_LABELSCROLL: + UX_CALLBACK_SET_INTERVAL( + MAX(3000, 1000 + bagl_label_roundtrip_duration_ms(element, 7)) + ); + break; + } + return element; +} + +void h_review_button_left() { + h_paging_decrease(); + + view_error_t err = h_review_update_data(); + switch(err) { + case view_no_error: + view_review_show(); + UX_WAIT(); + break; + case view_no_data: + view_sign_show_s(); + break; + case view_error_detected: + default: + view_error_show(); + UX_WAIT(); + break; + } +} + +void h_review_button_right() { + h_paging_increase(); + + view_error_t err = h_review_update_data(); + + switch(err) { + case view_no_error: + view_review_show(); + UX_WAIT(); + break; + case view_no_data: + view_sign_show_s(); + break; + case view_error_detected: + default: + view_error_show(); + UX_WAIT(); + break; + } +} + +void splitValueField() { + print_value2(""); + uint16_t vlen = strlen(viewdata.value); + if (vlen > MAX_CHARS_PER_VALUE2_LINE - 1) { + strcpy(viewdata.value2, viewdata.value + MAX_CHARS_PER_VALUE_LINE); + viewdata.value[MAX_CHARS_PER_VALUE_LINE] = 0; + } +} + +////////////////////////// +////////////////////////// +////////////////////////// +////////////////////////// +////////////////////////// + +void view_idle_show_impl(uint8_t item_idx) { + h_expert_update(); + UX_MENU_DISPLAY(item_idx, menu_main, NULL); +} + +void view_address_show_impl() { +#if !defined(HAVE_UX_FLOW) + h_paging_init(); + + view_error_t err = h_addr_update_item(viewdata.itemIdx); + switch(err) { + case view_no_error: + view_addr_show(); + break; + case view_error_detected: + default: + view_error_show(); + break; + } +#else + ux_layout_paging_reset(); + if(G_ux.stack_count == 0) { + ux_stack_push(); + } + + if (app_mode_expert()) { + ux_flow_init(0, ux_addr_flow_with_path, NULL); + } else { + ux_flow_init(0, ux_addr_flow_no_path, NULL); + } +#endif +} + +void view_error_show_impl() { + UX_DISPLAY(view_error, view_prepro); +} + +void view_sign_show_impl() { + h_paging_init(); + + view_error_t err = h_review_update_data(); + switch(err) { + case view_no_error: + view_review_show(); + break; + case view_no_data: + view_sign_show_s(); + break; + case view_error_detected: + default: + view_error_show(); + break; + } +} + +void view_sign_show_s(void){ + UX_MENU_DISPLAY(0, menu_sign, NULL); +} + +void view_review_show() { + UX_DISPLAY(view_review, view_prepro); +} + +void h_expert_toggle() { + app_mode_set_expert(!app_mode_expert()); + view_idle_show(1); +} + +void h_expert_update() { + strcpy(viewdata.value, "disabled"); + if (app_mode_expert()) { + strcpy(viewdata.value, "enabled"); + } +} + +#if !defined(HAVE_UX_FLOW) +void h_addr_button_left() { + h_paging_decrease(); + + view_error_t err = h_addr_update_item(viewdata.itemIdx); + + switch(err) { + case view_no_error: + view_addr_show(); + break; + case view_no_data: + // FIXME: + case view_error_detected: + default: + view_error_show(); + break; + } + + UX_WAIT(); +} + +void h_addr_button_right() { + h_paging_increase(); + view_error_t err = h_addr_update_item(viewdata.itemIdx); + + switch(err) { + case view_no_error: + view_addr_show(); + break; + case view_no_data: + // FIXME: + case view_error_detected: + default: + view_error_show(); + break; + } + + UX_WAIT(); +} + +void h_addr_button_both() { + view_error_t err = h_addr_update_item(viewdata.itemIdx); + + switch(err) { + case view_no_error: + h_address_accept(0); + break; + case view_no_data: + // FIXME: + case view_error_detected: + default: + view_error_show(); + break; + } + UX_WAIT(); +} + +void view_addr_show() { + UX_DISPLAY(view_address, view_prepro); +} +#endif + +#endif diff --git a/deps/ledger-zxlib/app/common/view_x.c b/deps/ledger-zxlib/app/common/view_x.c new file mode 100644 index 00000000..0dc7633d --- /dev/null +++ b/deps/ledger-zxlib/app/common/view_x.c @@ -0,0 +1,260 @@ +/******************************************************************************* +* (c) 2018, 2019 Zondax GmbH +* (c) 2016 Ledger +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +********************************************************************************/ + +#include "app_mode.h" +#include "view.h" +#include "view_internal.h" +#include "actions.h" +#include "apdu_codes.h" +#include "glyphs.h" +#include "bagl.h" +#include "zxmacros.h" +#include "view_templates.h" +#include "tx.h" + +#include +#include + +#if defined(TARGET_NANOX) + +void h_expert_toggle(); +void h_expert_update(); +void h_review_loop_start(); +void h_review_loop_inside(); +void h_review_loop_end(); + +#include "ux.h" +ux_state_t G_ux; +bolos_ux_params_t G_ux_params; +uint8_t flow_inside_loop; + + +UX_FLOW_DEF_NOCB(ux_idle_flow_1_step, pbb, { &C_icon_app, MENU_MAIN_APP_LINE1, MENU_MAIN_APP_LINE2,}); +UX_STEP_CB_INIT(ux_idle_flow_2_step, bn, h_expert_update(), h_expert_toggle(), { "Expert mode:", viewdata.value, }); +UX_FLOW_DEF_NOCB(ux_idle_flow_3_step, bn, { APPVERSION_LINE1, APPVERSION_LINE2, }); +UX_FLOW_DEF_NOCB(ux_idle_flow_4_step, bn, { "Developed by:", "Zondax.ch", }); +UX_FLOW_DEF_NOCB(ux_idle_flow_5_step, bn, { "License:", "Apache 2.0", }); +UX_FLOW_DEF_VALID(ux_idle_flow_6_step, pb, os_sched_exit(-1), { &C_icon_dashboard, "Quit",}); + +const ux_flow_step_t *const ux_idle_flow [] = { + &ux_idle_flow_1_step, + &ux_idle_flow_2_step, + &ux_idle_flow_3_step, + &ux_idle_flow_4_step, + &ux_idle_flow_5_step, + &ux_idle_flow_6_step, + FLOW_END_STEP, +}; + +/////////// + +UX_STEP_NOCB_INIT(ux_addr_flow_1_step, bnnn_paging, + { h_addr_update_item(CUR_FLOW.index); }, + { .title = "Address", .text = viewdata.addr, }); +UX_STEP_NOCB_INIT(ux_addr_flow_2_step, bnnn_paging, + { h_addr_update_item(CUR_FLOW.index); }, + { .title = "Path", .text = viewdata.addr, }); +UX_STEP_VALID(ux_addr_flow_3_step, pb, h_address_accept(0), { &C_icon_validate_14, "Ok"}); + +UX_FLOW( + ux_addr_flow_no_path, + &ux_addr_flow_1_step, + &ux_addr_flow_3_step +); + +UX_FLOW( + ux_addr_flow_with_path, + &ux_addr_flow_1_step, + &ux_addr_flow_2_step, + &ux_addr_flow_3_step +); + +/////////// + +UX_STEP_NOCB(ux_error_flow_1_step, bnnn_paging, { .title = viewdata.key, .text = viewdata.value, }); +UX_STEP_VALID(ux_error_flow_2_step, pb, h_error_accept(0), { &C_icon_validate_14, "Ok"}); + +UX_FLOW( + ux_error_flow, + &ux_error_flow_1_step, + &ux_error_flow_2_step +); + +/////////// +UX_STEP_NOCB(ux_sign_flow_1_step, pbb, { &C_icon_eye, "View", "Transaction" }); + +UX_STEP_INIT(ux_sign_flow_2_start_step, NULL, NULL, { h_review_loop_start(); }); +UX_STEP_NOCB_INIT(ux_sign_flow_2_step, bnnn_paging, { h_review_loop_inside(); }, { .title = viewdata.key, .text = viewdata.value, }); +UX_STEP_INIT(ux_sign_flow_2_end_step, NULL, NULL, { h_review_loop_end(); }); + +UX_STEP_VALID(ux_sign_flow_3_step, pbb, h_sign_accept(0), { &C_icon_validate_14, "Sign", "Transaction" }); +UX_STEP_VALID(ux_sign_flow_4_step, pbb, h_sign_reject(0), { &C_icon_crossmark, "Reject", "Transaction" }); +const ux_flow_step_t *const ux_sign_flow[] = { + &ux_sign_flow_1_step, + &ux_sign_flow_2_start_step, + &ux_sign_flow_2_step, + &ux_sign_flow_2_end_step, + &ux_sign_flow_3_step, + &ux_sign_flow_4_step, + FLOW_END_STEP, +}; + +////////////////////////// +////////////////////////// +////////////////////////// +////////////////////////// +////////////////////////// + +void h_review_loop_start() { + if (flow_inside_loop) { + // coming from right + h_paging_decrease(); + if (viewdata.itemIdx<0) { + // exit to the left + flow_inside_loop = 0; + ux_flow_prev(); + return; + } + } else { + // coming from left + h_paging_init(); + } + + view_error_t err = h_review_update_data(); + switch(err) { + case view_no_error: + case view_no_data: + break; + case view_error_detected: + default: + view_error_show(); + break; + } + + ux_flow_next(); +} + +void h_review_loop_inside() { + flow_inside_loop = 1; +} + +void h_review_loop_end() { + if (flow_inside_loop) { + // coming from left + h_paging_increase(); + view_error_t err = h_review_update_data(); + + switch(err) { + case view_no_error: + ux_layout_bnnn_paging_reset(); + break; + case view_no_data: { + flow_inside_loop = 0; + ux_flow_next(); + return; + } + case view_error_detected: + default: + view_error_show(); + break; + } + } else { + // coming from right + h_paging_decrease(); + view_error_t err = h_review_update_data(); + + switch(err) { + case view_no_error: + case view_no_data: + break; + case view_error_detected: + default: + view_error_show(); + break; + } + } + + // move to prev flow but trick paging to show first page + CUR_FLOW.prev_index = CUR_FLOW.index-2; + CUR_FLOW.index--; + ux_flow_relayout(); +} + +void splitValueField() { + uint16_t vlen = strlen(viewdata.value); + if (vlen == 0 ) { + strcpy(viewdata.value, " "); + } +} + +void h_expert_toggle() { + app_mode_set_expert(!app_mode_expert()); + ux_flow_init(0, ux_idle_flow, &ux_idle_flow_2_step); +} + +void h_expert_update() { + strcpy(viewdata.value, "disabled"); + if (app_mode_expert()) { + strcpy(viewdata.value, "enabled"); + } +} + +////////////////////////// +////////////////////////// +////////////////////////// +////////////////////////// +////////////////////////// + +void view_idle_show_impl(uint8_t item_idx) { + if(G_ux.stack_count == 0) { + ux_stack_push(); + } + ux_flow_init(0, ux_idle_flow, NULL); +} + +void view_address_show_impl() { + ux_layout_bnnn_paging_reset(); + if(G_ux.stack_count == 0) { + ux_stack_push(); + } + if (app_mode_expert()) { + ux_flow_init(0, ux_addr_flow_with_path, NULL); + } else { + ux_flow_init(0, ux_addr_flow_no_path, NULL); + } +} + +void view_error_show_impl() { + ux_layout_bnnn_paging_reset(); + if(G_ux.stack_count == 0) { + ux_stack_push(); + } + ux_flow_init(0, ux_error_flow, NULL); +} + +void view_sign_show_impl(){ + h_paging_init(); + h_paging_decrease(); + //// + flow_inside_loop = 0; + if(G_ux.stack_count == 0) { + ux_stack_push(); + } + ux_flow_init(0, ux_sign_flow, NULL); +} + +#endif diff --git a/deps/ledger-zxlib/dockerized_build.mk b/deps/ledger-zxlib/dockerized_build.mk new file mode 100644 index 00000000..3ac50101 --- /dev/null +++ b/deps/ledger-zxlib/dockerized_build.mk @@ -0,0 +1,208 @@ +#******************************************************************************* +#* (c) 2019 Zondax GmbH +#* +#* Licensed under the Apache License, Version 2.0 (the "License"); +#* you may not use this file except in compliance with the License. +#* You may obtain a copy of the License at +#* +#* http://www.apache.org/licenses/LICENSE-2.0 +#* +#* Unless required by applicable law or agreed to in writing, software +#* distributed under the License is distributed on an "AS IS" BASIS, +#* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +#* See the License for the specific language governing permissions and +#* limitations under the License. +#******************************************************************************** + +.PHONY: all deps build clean load delete check_python show_info_recovery_mode + +TESTS_ZEMU_DIR?=$(CURDIR)/tests_zemu +TESTS_ZEMU_JS_PACKAGE?= +TESTS_ZEMU_JS_DIR?= + +LEDGER_SRC=$(CURDIR)/app +DOCKER_APP_SRC=/project +DOCKER_APP_BIN=$(DOCKER_APP_SRC)/app/bin/app.elf + +DOCKER_BOLOS_SDK=/project/deps/nanos-secure-sdk +DOCKER_BOLOS_SDKX=/project/deps/nano2-sdk + +SCP_PUBKEY=049bc79d139c70c83a4b19e8922e5ee3e0080bb14a2e8b0752aa42cda90a1463f689b0fa68c1c0246845c2074787b649d0d8a6c0b97d4607065eee3057bdf16b83 +SCP_PRIVKEY=ff701d781f43ce106f72dc26a46b6a83e053b5d07bb3d4ceab79c91ca822a66b + +INTERACTIVE:=$(shell [ -t 0 ] && echo 1) +USERID:=$(shell id -u) +$(info USERID : $(USERID)) +$(info TESTS_ZEMU_DIR : $(TESTS_ZEMU_DIR)) +$(info TESTS_ZEMU_JS_DIR : $(TESTS_ZEMU_JS_DIR)) +$(info TESTS_ZEMU_JS_PACKAGE : $(TESTS_ZEMU_JS_PACKAGE)) + +ifeq ($(USERID),1001) +# TODO: Use podman inside circleci machines? +DOCKER_IMAGE=zondax/builder-bolos-1001:latest +else +DOCKER_IMAGE=zondax/builder-bolos:latest +endif + +ifdef INTERACTIVE +INTERACTIVE_SETTING:="-i" +TTY_SETTING:="-t" +else +INTERACTIVE_SETTING:= +TTY_SETTING:= +endif + +define run_docker + docker run $(TTY_SETTING) $(INTERACTIVE_SETTING) --rm \ + -e SCP_PRIVKEY=$(SCP_PRIVKEY) \ + -e BOLOS_SDK=$(1) \ + -e BOLOS_ENV=/opt/bolos \ + -u $(USERID) \ + -v $(shell pwd):/project \ + $(DOCKER_IMAGE) \ + "COIN=$(COIN) $(2)" +endef + +all: build + +.PHONY: check_python +check_python: + @python -c 'import sys; sys.exit(3-sys.version_info.major)' || (echo "The python command does not point to Python 3"; exit 1) + +.PHONY: deps +deps: check_python + @echo "Install dependencies" + $(CURDIR)/deps/ledger-zxlib/scripts/install_deps.sh + +.PHONY: pull +pull: + docker pull $(DOCKER_IMAGE) + +.PHONY: build_rust +build_rust: + $(call run_docker,$(DOCKER_BOLOS_SDK),make -C $(DOCKER_APP_SRC) rust) + +.PHONY: build +build: build_rust + $(info Replacing app icon) + @cp $(LEDGER_SRC)/nanos_icon.gif $(LEDGER_SRC)/glyphs/icon_app.gif + $(info calling make inside docker) + $(call run_docker,$(DOCKER_BOLOS_SDK),make -j `nproc` -C $(DOCKER_APP_SRC)) + +.PHONY: buildX +buildX: build_rust + @cp $(LEDGER_SRC)/nanos_icon.gif $(LEDGER_SRC)/glyphs/icon_app.gif + @convert $(LEDGER_SRC)/nanos_icon.gif -crop 14x14+1+1 +repage -negate $(LEDGER_SRC)/nanox_icon.gif + $(call run_docker,$(DOCKER_BOLOS_SDKX),make -j `nproc` -C $(DOCKER_APP_SRC)) + +.PHONY: clean +clean: + $(call run_docker,$(DOCKER_BOLOS_SDK),make -C $(DOCKER_APP_SRC) clean) + +.PHONY: listvariants +listvariants: + $(call run_docker,$(DOCKER_BOLOS_SDK),make -C $(DOCKER_APP_SRC) listvariants) + +.PHONY: shell +shell: + $(call run_docker,$(DOCKER_BOLOS_SDK) -t,bash) + +.PHONY: load +load: + ${LEDGER_SRC}/pkg/zxtool.sh load + +.PHONY: delete +delete: + ${LEDGER_SRC}/pkg/zxtool.sh delete + +.PHONY: show_info_recovery_mode +show_info_recovery_mode: + @echo "This command requires a Ledger Nano S in recovery mode. To go into recovery mode, follow:" + @echo " 1. Settings -> Device -> Reset all and confirm" + @echo " 2. Unplug device, press and hold the right button, plug-in again" + @echo " 3. Navigate to the main menu" + @echo "If everything was correct, no PIN needs to be entered." + +# This target will initialize the device with the integration testing mnemonic +.PHONY: dev_init +dev_init: show_info_recovery_mode + @echo "Initializing device with test mnemonic! WARNING TAKES 2 MINUTES AND REQUIRES RECOVERY MODE" + @python -m ledgerblue.hostOnboard --apdu --id 0 --prefix "" --passphrase "" --pin 5555 --words "equip will roof matter pink blind book anxiety banner elbow sun young" + +# This target will initialize the device with the secondary integration testing mnemonic (Bob) +.PHONY: dev_init_secondary +dev_init_secondary: check_python show_info_recovery_mode + @echo "Initializing device with secondary test mnemonic! WARNING TAKES 2 MINUTES AND REQUIRES RECOVERY MODE" + @python -m ledgerblue.hostOnboard --apdu --id 0 --prefix "" --passphrase "" --pin 5555 --words "elite vote proof agree february step sibling sand grocery axis false cup" + +# This target will setup a custom developer certificate +.PHONY: dev_ca +dev_ca: check_python + @python -m ledgerblue.setupCustomCA --targetId 0x31100004 --public $(SCP_PUBKEY) --name zondax + +# This target will setup a custom developer certificate +.PHONY: dev_caX +dev_caX: check_python + @python -m ledgerblue.setupCustomCA --targetId 0x33000004 --public $(SCP_PUBKEY) --name zondax + +.PHONY: dev_ca_delete +dev_ca_delete: check_python + @python -m ledgerblue.resetCustomCA --targetId 0x31100004 + +# This target will setup a custom developer certificate +.PHONY: dev_ca2 +dev_ca2: check_python + @python -m ledgerblue.setupCustomCA --targetId 0x33000004 --public $(SCP_PUBKEY) --name zondax + +.PHONY: dev_ca_delete2 +dev_ca_delete2: check_python + @python -m ledgerblue.resetCustomCA --targetId 0x33000004 + +########################## ZEMU Section ############################### + +.PHONY: zemu_install_js_link +ifeq ($(TESTS_ZEMU_JS_DIR),) +zemu_install_js_link: + @echo "No local package defined" +else +zemu_install_js_link: + # First unlink everything + cd $(TESTS_ZEMU_JS_DIR) && yarn unlink || true + cd $(TESTS_ZEMU_DIR) && yarn unlink $(TESTS_ZEMU_JS_PACKAGE) || true + # Now build and link + cd $(TESTS_ZEMU_JS_DIR) && yarn install && yarn build || true + cd $(TESTS_ZEMU_JS_DIR) && yarn link || true + cd $(TESTS_ZEMU_DIR) && yarn link $(TESTS_ZEMU_JS_PACKAGE) || true +endif + +.PHONY: zemu_install +zemu_install: zemu_install_js_link + # and now install everything + cd $(TESTS_ZEMU_DIR) && yarn install + +.PHONY: zemu +zemu: + cd $(TESTS_ZEMU_DIR)/tools && node debug.mjs + +.PHONY: zemu_val +zemu_val: + cd $(TESTS_ZEMU_DIR)/tools && node debug_val.mjs + +.PHONY: zemu_debug +zemu_debug: + cd $(TESTS_ZEMU_DIR)/tools && node debug.mjs debug + +########################## TEST Section ############################### + +.PHONY: zemu_test +zemu_test: + cd $(TESTS_ZEMU_DIR) && yarn test + +.PHONY: rust_test +rust_test: + cd app/rust && cargo test + +.PHONY: cpp_test +cpp_test: + mkdir -p build && cd build && cmake -DDISABLE_DOCKER_BUILDS=ON -DCMAKE_BUILD_TYPE=Debug .. && make + cd build && GTEST_COLOR=1 ASAN_OPTIONS=detect_leaks=0 ctest -VV diff --git a/deps/ledger-zxlib/include/apdu_codes.h b/deps/ledger-zxlib/include/apdu_codes.h index 2e545069..e1c22f5a 100644 --- a/deps/ledger-zxlib/include/apdu_codes.h +++ b/deps/ledger-zxlib/include/apdu_codes.h @@ -1,5 +1,5 @@ /******************************************************************************* -* (c) 2018 ZondaX GmbH +* (c) 2018 Zondax GmbH * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -15,7 +15,9 @@ ********************************************************************************/ #pragma once + #include "inttypes.h" +#include "zxmacros.h" // Based on ISO7816 @@ -41,8 +43,7 @@ #define APDU_CODE_SIGN_VERIFY_ERROR 0x6F01 -inline void set_code(uint8_t *buffer, uint8_t offset, uint16_t value) -{ - *(buffer + offset) = (uint8_t)(value >> 8); - *(buffer + offset + 1) = (uint8_t)(value & 0xFF); +__Z_INLINE void set_code(uint8_t *buffer, uint8_t offset, uint16_t value) { + *(buffer + offset) = (uint8_t) (value >> 8); + *(buffer + offset + 1) = (uint8_t) (value & 0xFF); } diff --git a/deps/ledger-zxlib/include/bech32.h b/deps/ledger-zxlib/include/bech32.h index ead5a2f6..bd9fd148 100644 --- a/deps/ledger-zxlib/include/bech32.h +++ b/deps/ledger-zxlib/include/bech32.h @@ -1,5 +1,5 @@ /******************************************************************************* -* (c) 2019 ZondaX GmbH +* (c) 2019 Zondax GmbH * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -19,13 +19,20 @@ extern "C" { #endif +#include "zxerror.h" + +#define MAX_INPUT_SIZE 64 + // the following function encodes directly from bytes // it will internally convert from 8 to 5 bits and return a // zero-terminated string in output -void bech32EncodeFromBytes(char *output, - const char *hrp, - const uint8_t *data, - size_t data_len); + +zxerr_t bech32EncodeFromBytes(char *out, + size_t out_len, + const char *hrp, + const uint8_t *in, + size_t in_len, + uint8_t pad); #ifdef __cplusplus } diff --git a/deps/ledger-zxlib/include/bignum.h b/deps/ledger-zxlib/include/bignum.h new file mode 100644 index 00000000..9bf31f8f --- /dev/null +++ b/deps/ledger-zxlib/include/bignum.h @@ -0,0 +1,35 @@ +/******************************************************************************* +* (c) 2019 Zondax GmbH +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +********************************************************************************/ + +#pragma once + +#ifdef __cplusplus +extern "C" { +#endif + +#include +#include + +bool_t bignumLittleEndian_bcdprint(char *outBuffer, uint16_t outBufferLen, const uint8_t *inBCD, uint16_t inBCDLen); +void bignumLittleEndian_to_bcd(uint8_t *bcdOut, uint16_t bcdOutLen, const uint8_t *binValue, uint16_t binValueLen); + +bool_t bignumBigEndian_bcdprint(char *outBuffer, uint16_t outBufferLen, const uint8_t *bcdIn, uint16_t bcdInLen); +void bignumBigEndian_to_bcd(uint8_t *bcdOut, uint16_t bcdOutLen, const uint8_t *binValue, uint16_t binValueLen); + + +#ifdef __cplusplus +} +#endif diff --git a/deps/ledger-zxlib/include/bittools.h b/deps/ledger-zxlib/include/bittools.h index d2cfa448..1903878d 100644 --- a/deps/ledger-zxlib/include/bittools.h +++ b/deps/ledger-zxlib/include/bittools.h @@ -27,10 +27,10 @@ extern "C" { int convert_bits(uint8_t *out, size_t *outlen, - int outbits, + int outBits, const uint8_t *in, - size_t inlen, - int inbits, int pad); + size_t inLen, + int inBits, int pad); #ifdef __cplusplus } diff --git a/deps/ledger-zxlib/include/buffering.h b/deps/ledger-zxlib/include/buffering.h index 63eef3a2..ec6e80cf 100644 --- a/deps/ledger-zxlib/include/buffering.h +++ b/deps/ledger-zxlib/include/buffering.h @@ -1,6 +1,6 @@ /******************************************************************************* * (c) 2016 Ledger -* (c) 2018 ZondaX GmbH +* (c) 2018 Zondax GmbH * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/deps/ledger-zxlib/include/hexutils.h b/deps/ledger-zxlib/include/hexutils.h new file mode 100644 index 00000000..3cbaad8e --- /dev/null +++ b/deps/ledger-zxlib/include/hexutils.h @@ -0,0 +1,30 @@ +/******************************************************************************* +* (c) 2018 Zondax GmbH +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +********************************************************************************/ + +#pragma once + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +size_t parseHexString(uint8_t *out, uint16_t outLen, const char *input); + +#ifdef __cplusplus +} +#endif diff --git a/deps/ledger-zxlib/include/sigutils.h b/deps/ledger-zxlib/include/sigutils.h new file mode 100644 index 00000000..862f809a --- /dev/null +++ b/deps/ledger-zxlib/include/sigutils.h @@ -0,0 +1,41 @@ +/******************************************************************************* +* (c) 2020 Zondax GmbH +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +********************************************************************************/ + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +typedef enum { + no_error = 0, + invalid_derPrefix, + invalid_payloadLen, + invalid_rmaker, + invalid_rLen, + invalid_smarker, + invalid_sLen, +} err_convert_e; + +err_convert_e convertDERtoRSV(const uint8_t *inSignatureDER, + unsigned int inInfo, + uint8_t *outR, + uint8_t *outS, + uint8_t *outV); + +#ifdef __cplusplus +} +#endif diff --git a/deps/ledger-zxlib/include/utf8.h b/deps/ledger-zxlib/include/utf8.h index db48edbb..4a0ca694 100644 --- a/deps/ledger-zxlib/include/utf8.h +++ b/deps/ledger-zxlib/include/utf8.h @@ -1166,7 +1166,7 @@ utf8_int32_t utf8lwrcodepoint(utf8_int32_t cp) { case 0x03f9: cp = 0x03f2; break; case 0x03f7: cp = 0x03f8; break; case 0x03fa: cp = 0x03fb; break; - }; + } } return cp; @@ -1242,7 +1242,7 @@ utf8_int32_t utf8uprcodepoint(utf8_int32_t cp) { case 0x03f2: cp = 0x03f9; break; case 0x03f8: cp = 0x03f7; break; case 0x03fb: cp = 0x03fa; break; - }; + } } return cp; @@ -1259,4 +1259,4 @@ utf8_int32_t utf8uprcodepoint(utf8_int32_t cp) { #pragma clang diagnostic pop #endif -#endif // SHEREDOM_UTF8_H_INCLUDED \ No newline at end of file +#endif // SHEREDOM_UTF8_H_INCLUDED diff --git a/deps/ledger-zxlib/include/view_templates.h b/deps/ledger-zxlib/include/view_templates.h index 5d6db0d2..0b5c6bcd 100644 --- a/deps/ledger-zxlib/include/view_templates.h +++ b/deps/ledger-zxlib/include/view_templates.h @@ -1,6 +1,6 @@ /******************************************************************************* * (c) 2016 Ledger -* (c) 2018 ZondaX GmbH +* (c) 2018 Zondax GmbH * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -49,12 +49,6 @@ 0 /* icon_id */ \ }, \ NULL, /* text */ \ - 0, /* touch_area_brim */ \ - 0, /* overfgcolor */ \ - 0, /* overbgcolor */ \ - NULL, /* tap */ \ - NULL, /* out */ \ - NULL, /* over */ \ } #define UI_LabelLine(id, x, y, w, h, fgcolor, bgcolor, text) \ @@ -75,12 +69,6 @@ 0 /* icon_id */ \ }, \ text, /* text */ \ - 0, /* touch_area_brim */ \ - 0, /* overfgcolor */ \ - 0, /* overbgcolor */ \ - NULL, /* tap */ \ - NULL, /* out */ \ - NULL, /* over */ \ } #define UI_LabelLineScrolling(id, x, y, w, h, fgcolor, bgcolor, text) \ @@ -101,12 +89,6 @@ 50 /* icon_id / scroll speed */ \ }, \ text, /* text */ \ - 0, /* touch_area_brim */ \ - 0, /* overfgcolor */ \ - 0, /* overbgcolor */ \ - NULL, /* tap */ \ - NULL, /* out */ \ - NULL, /* over */ \ } #if defined(TARGET_NANOX) @@ -171,12 +153,6 @@ icon /* icon_id */ \ }, \ NULL, /* text */ \ - 0, /* touch_area_brim */ \ - 0, /* overfgcolor */ \ - 0, /* overbgcolor */ \ - NULL, /* tap */ \ - NULL, /* out */ \ - NULL, /* over */ \ } #define UI_BACKGROUND_LEFT_RIGHT_ICONS \ diff --git a/deps/ledger-zxlib/include/zxerror.h b/deps/ledger-zxlib/include/zxerror.h new file mode 100644 index 00000000..e3edde76 --- /dev/null +++ b/deps/ledger-zxlib/include/zxerror.h @@ -0,0 +1,32 @@ +/******************************************************************************* +* (c) 2018 Zondax GmbH +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +********************************************************************************/ + +#pragma once + +#ifdef __cplusplus +extern "C" { +#endif + +typedef enum { + zxerr_ok, + zxerr_buffer_too_small, + zxerr_out_of_bounds, + zxerr_encoding_failed, +} zxerr_t; + +#ifdef __cplusplus +} +#endif diff --git a/deps/ledger-zxlib/include/zxformat.h b/deps/ledger-zxlib/include/zxformat.h new file mode 100644 index 00000000..00fec0c7 --- /dev/null +++ b/deps/ledger-zxlib/include/zxformat.h @@ -0,0 +1,273 @@ +/******************************************************************************* +* (c) 2018 Zondax GmbH +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +********************************************************************************/ +#pragma once + +#ifdef __cplusplus +extern "C" { +#endif + +#define NUM_TO_STR(TYPE) __Z_INLINE const char * TYPE##_to_str(char *data, int dataLen, TYPE##_t number) { \ + if (dataLen < 2) return "Buffer too small"; \ + MEMZERO(data, dataLen); \ + char *p = data; \ + if (number < 0) { *(p++) = '-'; data++; } \ + else if (number == 0) { *(p++) = '0'; } \ + TYPE##_t tmp; \ + while (number != 0) { \ + if (p - data >= (dataLen - 1)) { return "Buffer too small"; } \ + tmp = number % 10; \ + tmp = tmp < 0 ? -tmp : tmp; \ + *(p++) = (char) ('0' + tmp); \ + number /= 10u; \ + } \ + while (p > data) { \ + p--; \ + char z = *data; *data = *p; *p = z; \ + data++; \ + } \ + return NULL; \ +} + +NUM_TO_STR(int64) + +NUM_TO_STR(uint64) + +__Z_INLINE void bip32_to_str(char *s, uint32_t max, const uint32_t *path, uint8_t pathLen) { + MEMZERO(s, max); + + if (pathLen == 0) { + snprintf(s, max, "EMPTY PATH"); + return; + } + + if (pathLen > 5) { + snprintf(s, max, "ERROR"); + return; + } + + uint32_t offset = 0; + for (uint16_t i = 0; i < pathLen; i++) { + size_t written = 0; + + // Warning: overcomplicated because Ledger's snprintf does not return number of written bytes + + snprintf(s + offset, max - offset, "%d", path[i] & 0x7FFFFFFFu); + written = strlen(s + offset); + if (written == 0 || written >= max - offset) { + snprintf(s, max, "ERROR"); + return; + } + offset += written; + + if ((path[i] & 0x80000000u) != 0) { + snprintf(s + offset, max - offset, "'"); + written = strlen(s + offset); + if (written == 0 || written >= max - offset) { + snprintf(s, max, "ERROR"); + return; + } + offset += written; + } + + if (i != pathLen - 1) { + snprintf(s + offset, max - offset, "/"); + written = strlen(s + offset); + if (written == 0 || written >= max - offset) { + snprintf(s, max, "ERROR"); + return; + } + offset += written; + } + } +} + +__Z_INLINE void bip44_to_str(char *s, uint32_t max, const uint32_t path[5]) { + bip32_to_str(s, max, path, 5); +} + +__Z_INLINE int8_t str_to_int8(const char *start, const char *end, char *error) { + int sign = 1; + if (*start == '-') { + sign = -1; + start++; + } + + int64_t value = 0; + int multiplier = 1; + for (const char *s = end - 1; s >= start; s--) { + int delta = (*s - '0'); + if (delta >= 0 && delta <= 9) { + value += (delta * multiplier); + multiplier *= 10; + } else { + if (error != NULL) { + *error = 1; + return 0; + } + } + } + + value *= sign; + if (value >= INT8_MIN && value <= INT8_MAX) { + return (int8_t) value; + } + if (error != NULL) { + *error = 1; + } + return 0; +} + +__Z_INLINE int64_t str_to_int64(const char *start, const char *end, char *error) { + int sign = 1; + if (*start == '-') { + sign = -1; + start++; + } + + int64_t value = 0; + uint64_t multiplier = 1; + for (const char *s = end - 1; s >= start; s--) { + int delta = (*s - '0'); + if (delta >= 0 && delta <= 9) { + value += (delta * multiplier); + multiplier *= 10; + } else { + if (error != NULL) { + *error = 1; + return 0; + } + } + } + + return value * sign; +} + +__Z_INLINE uint8_t fpstr_to_str(char *out, uint16_t outLen, const char *number, uint8_t decimals) { + MEMZERO(out, outLen); + size_t digits = strlen(number); + + if (decimals == 0) { + if (digits == 0) { + snprintf(out, outLen, "0"); + return 0; + } else if (outLen < digits) { + snprintf(out, outLen, "ERR"); + return 1; + } + strcpy(out, number); + return 0; + } + + if ((outLen < decimals + 2) || + (outLen < digits + 1)) { + snprintf(out, outLen, "ERR"); + return 1; + } + + if (digits <= decimals) { + // First part + strcpy(out, "0."); + out += 2; + MEMSET(out, '0', decimals - digits); + out += decimals - digits; + } else { + const size_t shift = digits - decimals; + strcpy(out, number); + number += shift; + out += shift; + *out++ = '.'; + } + + strcpy(out, number); + return 0; +} + +__Z_INLINE void fpuint64_to_str(char *out, uint16_t outLen, const uint64_t value, uint8_t decimals) { + char buffer[30]; + MEMZERO(buffer, sizeof(buffer)); + int64_to_str(buffer, sizeof(buffer), value); + fpstr_to_str(out, outLen, buffer, decimals); +} + +__Z_INLINE uint64_t uint64_from_BEarray(const uint8_t data[8]) { + uint64_t result = 0; + for (uint8_t i = 0; i < 8u; i++) { + result <<= 8u; + result += data[i]; + } + return result; +} + +__Z_INLINE uint32_t array_to_hexstr(char *dst, uint16_t dstLen, const uint8_t *src, uint8_t count) { + + if (dstLen < (count * 2 + 1)) { + return 0; + } + + const char hexchars[] = "0123456789abcdef"; + for (uint8_t i = 0; i < count; i++, src++) { + *dst++ = hexchars[*src >> 4u]; + *dst++ = hexchars[*src & 0x0Fu]; + } + *dst = 0; // terminate string + + return count * 2; +} + +__Z_INLINE void pageStringExt(char *outValue, uint16_t outValueLen, + const char *inValue, uint16_t inValueLen, + uint8_t pageIdx, uint8_t *pageCount) { + MEMZERO(outValue, outValueLen); + *pageCount = 0; + + outValueLen--; // leave space for NULL termination + if (outValueLen == 0) { + return; + } + + if (inValueLen == 0) { + return; + } + + *pageCount = (inValueLen / outValueLen); + const uint16_t lastChunkLen = (inValueLen % outValueLen); + + if (lastChunkLen > 0) { + (*pageCount)++; + } + + if (pageIdx < *pageCount) { + if (lastChunkLen > 0 && pageIdx == *pageCount - 1) { + MEMCPY(outValue, inValue + (pageIdx * outValueLen), lastChunkLen); + } else { + MEMCPY(outValue, inValue + (pageIdx * outValueLen), outValueLen); + } + } +} + +__Z_INLINE void pageString(char *outValue, uint16_t outValueLen, + const char *inValue, + uint8_t pageIdx, uint8_t *pageCount) { + pageStringExt(outValue, outValueLen, inValue, strlen(inValue), pageIdx, pageCount); +} + +size_t asciify(char *utf8_in); + +size_t asciify_ext(const char *utf8_in, char *ascii_only_out); + +#ifdef __cplusplus +} +#endif diff --git a/deps/ledger-zxlib/include/zxmacros.h b/deps/ledger-zxlib/include/zxmacros.h index 9a9170a2..b500231c 100644 --- a/deps/ledger-zxlib/include/zxmacros.h +++ b/deps/ledger-zxlib/include/zxmacros.h @@ -1,5 +1,5 @@ /******************************************************************************* -* (c) 2018 ZondaX GmbH +* (c) 2018 Zondax GmbH * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -13,13 +13,26 @@ * See the License for the specific language governing permissions and * limitations under the License. ********************************************************************************/ - #pragma once +#pragma clang diagnostic push +#pragma ide diagnostic ignored "modernize-use-nullptr" +#pragma ide diagnostic ignored "OCUnusedGlobalDeclarationInspection" +#pragma ide diagnostic ignored "OCUnusedMacroInspection" +#pragma ide diagnostic ignored "modernize-deprecated-headers" + #ifdef __cplusplus extern "C" { #endif +#include "string.h" +#ifndef __APPLE__ +extern void explicit_bzero(void *__s, size_t __n) __THROW __nonnull ((1)); +#endif +#define __Z_INLINE inline __attribute__((always_inline)) static + +void handle_stack_overflow(); + #if defined(LEDGER_SPECIFIC) #include "bolos_target.h" #endif @@ -32,10 +45,24 @@ extern "C" { #define NV_VOL #endif +#ifndef PIC +#define PIC(x) (x) +#endif + #define NV_ALIGN __attribute__ ((aligned(64))) #if defined (TARGET_NANOS) || defined(TARGET_NANOX) +__Z_INLINE void debug_log(char *buf) +{ + asm volatile ( + "movs r0, #0x04\n" + "movs r1, %0\n" + "svc 0xab\n" + :: "r"(buf) : "r0", "r1" + ); +} + #include "bolos_target.h" #include "os.h" #include "cx.h" @@ -46,6 +73,10 @@ extern "C" { #include "os_io_seproxyhal.h" #endif +#define CHECK_APP_CANARY() { if (app_stack_canary != APP_STACK_CANARY_MAGIC) handle_stack_overflow(); } +#define APP_STACK_CANARY_MAGIC 0xDEAD0031 +extern unsigned int app_stack_canary; + #define WAIT_EVENT() io_seproxyhal_spi_recv(G_io_seproxyhal_spi_buffer, sizeof(G_io_seproxyhal_spi_buffer), 0) #define UX_WAIT() \ @@ -63,32 +94,34 @@ extern "C" { #define MEMMOVE os_memmove #define MEMSET os_memset #define MEMCPY os_memcpy +#define MEMCMP os_memcmp #define MEMCPY_NV nvm_write +#define MEMZERO explicit_bzero -void debug_printf(void* buffer); +#else -#undef LOG -#undef LOGSTACK -#define LOG(str) debug_printf(str) -extern unsigned int app_stack_canary; -void __logstack(); -#define LOGSTACK() __logstack() +#define CHECK_APP_CANARY() {} -#else -#include #define MEMMOVE memmove #define MEMSET memset #define MEMCPY memcpy +#define MEMCMP memcmp #define MEMCPY_NV memcpy -#define LOG(str) -#define LOGSTACK() + +#define CX_ECCINFO_PARITY_ODD 1u +#define CX_ECCINFO_xGTn 2u + +#ifndef __APPLE__ +#define MEMZERO explicit_bzero +#else +__Z_INLINE void __memzero(void *buffer, size_t s) { memset(buffer, 0, s); } +#define MEMZERO __memzero +#endif #endif #include #include -#include - -#define __Z_INLINE inline __attribute__((always_inline)) static +#include #define SET_NV(DST, TYPE, VAL) { \ TYPE nvset_tmp=(VAL); \ @@ -104,144 +137,18 @@ void __logstack(); #define NtoHL(x) (x) #endif -__Z_INLINE void array_to_hexstr(char *dst, const uint8_t *src, uint8_t count) { - const char hexchars[] = "0123456789ABCDEF"; - for (uint8_t i = 0; i < count; i++, src++) { - *dst++ = hexchars[*src >> 4]; - *dst++ = hexchars[*src & 0x0F]; - } - *dst = 0; // terminate string -} - -__Z_INLINE const char *int64_to_str(char *data, int size, int64_t number) { - char temp[] = "-9223372036854775808"; - - char *ptr = temp; - int64_t num = number; - int sign = 1; - if (number < 0) { - sign = -1; - } - while (num != 0) { - *ptr++ = '0' + (num % 10) * sign; - num /= 10; - } - if (number < 0) { - *ptr++ = '-'; - } else if (number == 0) { - *ptr++ = '0'; - } - int distance = (ptr - temp) + 1; - if (size < distance) { - return "Size too small"; - } - int index = 0; - while (--ptr >= temp) { - data[index++] = *ptr; - } - data[index] = '\0'; - return NULL; -} - -__Z_INLINE int8_t str_to_int8(const char *start, const char *end, char *error) { - - int sign = 1; - if (*start == '-') { - sign = -1; - start++; - } - - int64_t value = 0; - int multiplier = 1; - for (const char *s = end - 1; s >= start; s--) { - int delta = (*s - '0'); - if (delta >= 0 && delta <= 9) { - value += (delta * multiplier); - multiplier *= 10; - } else { - if (error != NULL) { - *error = 1; - return 0; - } - } - } - - value *= sign; - if (value >= INT8_MIN && value <= INT8_MAX) { - return (int8_t) value; - } - if (error != NULL) { - *error = 1; - } - return 0; -} - -__Z_INLINE int64_t str_to_int64(const char *start, const char *end, char *error) { - - int sign = 1; - if (*start == '-') { - sign = -1; - start++; - } - - int64_t value = 0; - uint64_t multiplier = 1; - for (const char *s = end - 1; s >= start; s--) { - int delta = (*s - '0'); - if (delta >= 0 && delta <= 9) { - value += (delta * multiplier); - multiplier *= 10; - } else { - if (error != NULL) { - *error = 1; - return 0; - } - } - } - - return value * sign; -} - -__Z_INLINE void fpuint64_to_str(char *dst, const uint64_t value, uint8_t decimals) { - char buffer[30]; - - int64_to_str(buffer, 30, value); - size_t digits = strlen(buffer); - - if (digits <= decimals) { - *dst++ = '0'; - *dst++ = '.'; - for (uint16_t i = 0; i < decimals - digits; i++, dst++) - *dst = '0'; - strcpy(dst, buffer); - } else { - strcpy(dst, buffer); - const size_t shift = digits - decimals; - dst = dst + shift; - *dst++ = '.'; - - char *p = buffer + shift; - strcpy(dst, p); - } -} +#define sizeof_field(type, member) sizeof(((type *)0)->member) +#define array_length(array) (sizeof(array) / sizeof(array[0])) -__Z_INLINE uint64_t uint64_from_BEarray(const uint8_t data[8]) { - uint64_t result = 0; - for (int i = 0; i < 8; i++) { - result <<= 8; - result += data[i]; - } - return result; +__Z_INLINE void strncpy_s(char *dst, const char *src, size_t dstSize) { + MEMZERO(dst, dstSize); + strncpy(dst, src, dstSize - 1); } -size_t asciify(char *utf8_in); - -size_t asciify_ext(const char *utf8_in, char *ascii_only_out); - -#ifndef PIC -#define PIC(x) (x) -#endif - #ifdef __cplusplus } #endif + +#pragma clang diagnostic pop + +#include "zxformat.h" diff --git a/deps/ledger-zxlib/include/zxtypes.h b/deps/ledger-zxlib/include/zxtypes.h new file mode 100644 index 00000000..9aae6770 --- /dev/null +++ b/deps/ledger-zxlib/include/zxtypes.h @@ -0,0 +1,30 @@ +/******************************************************************************* +* (c) 2018 Zondax GmbH +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +********************************************************************************/ + +#pragma once + +#ifdef __cplusplus +extern "C" { +#endif + +typedef enum { + bool_false = 0, + bool_true = 1, +} bool_t; + +#ifdef __cplusplus +} +#endif diff --git a/deps/ledger-zxlib/include/zxversion.h b/deps/ledger-zxlib/include/zxversion.h new file mode 100644 index 00000000..0c7467eb --- /dev/null +++ b/deps/ledger-zxlib/include/zxversion.h @@ -0,0 +1,20 @@ +/******************************************************************************* +* (c) 2018 Zondax GmbH +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +********************************************************************************/ +#pragma once + +#define ZXLIB_MAJOR 2 +#define ZXLIB_MINOR 3 +#define ZXLIB_PATCH 2 diff --git a/deps/ledger-zxlib/scripts/install_deps.sh b/deps/ledger-zxlib/scripts/install_deps.sh new file mode 100755 index 00000000..46b37651 --- /dev/null +++ b/deps/ledger-zxlib/scripts/install_deps.sh @@ -0,0 +1,34 @@ +#!/usr/bin/env bash +#******************************************************************************* +#* (c) 2018 Zondax GmbH +#* +#* Licensed under the Apache License, Version 2.0 (the "License"); +#* you may not use this file except in compliance with the License. +#* You may obtain a copy of the License at +#* +#* http://www.apache.org/licenses/LICENSE-2.0 +#* +#* Unless required by applicable law or agreed to in writing, software +#* distributed under the License is distributed on an "AS IS" BASIS, +#* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +#* See the License for the specific language governing permissions and +#* limitations under the License. +#******************************************************************************** + +os_string="$(uname -s)" +case "${os_string}" in + Linux*) + sudo apt-get install libusb-1.0.0 libudev-dev + pip install -U setuptools + pip install -U --no-cache ledgerblue ecpy + pip install -U conan + ;; + Darwin*) + brew install libusb + pip install -U ledgerblue ecpy + pip install -U conan + ;; + *) + echo "OS not recognized" + ;; +esac diff --git a/deps/ledger-zxlib/scripts/template.sh b/deps/ledger-zxlib/scripts/template.sh new file mode 100755 index 00000000..8f0e7206 --- /dev/null +++ b/deps/ledger-zxlib/scripts/template.sh @@ -0,0 +1,60 @@ +#!/usr/bin/env bash +#******************************************************************************* +# (c) 2018 Zondax GmbH +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +#******************************************************************************* + +if [ -z "$APPNAME" ] +then + echo "This script has not been configured correctly" + exit 1 +fi + +# check python 3 has been installed +if ! command -v python3 &>/dev/null; then + echo Python 3 is not installed + exit +fi + +python3 -m ledgerblue.loadApp -h &>/dev/null; +if [ $? -ne 0 ]; then + echo + echo "ERR: ledgerblue pip package not found." + echo "please install using 'pip install ledgerblue'" + echo + exit +fi + +TMP_HEX_DIR=$(mktemp -d -t ci-XXXXXXXXXX) +mkdir -p ${TMP_HEX_DIR}/bin +BIN_HEX_FILE=${TMP_HEX_DIR}/bin/app.hex +echo -e "${APPHEX}" > ${BIN_HEX_FILE} + +case "$1" in + 'load') + cd "$TMP_HEX_DIR" || exit + python3 -m ledgerblue.loadApp --appFlags 0x200 --delete ${LOAD_PARAMS} --path ${APPPATH} --path "44'/1'" + ;; + 'delete') + python3 -m ledgerblue.deleteApp ${DELETE_PARAMS} + ;; + 'version') + echo "v${APPVERSION}" + ;; + *) + echo "Zondax Installer [$APPNAME-$APPVERSION] [Warning: use only for test/demo apps]" + echo " load - Load $APPNAME app" + echo " delete - Delete $APPNAME app" + echo " version - Show $APPNAME app version" +esac diff --git a/deps/ledger-zxlib/src/bech32.c b/deps/ledger-zxlib/src/bech32.c index c450c4f2..21fc639b 100644 --- a/deps/ledger-zxlib/src/bech32.c +++ b/deps/ledger-zxlib/src/bech32.c @@ -1,5 +1,5 @@ /******************************************************************************* -* (c) 2019 ZondaX GmbH +* (c) 2019 Zondax GmbH * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -16,22 +16,43 @@ #include #include +#include #include "bech32.h" #include "segwit_addr.h" #include "bittools.h" -void bech32EncodeFromBytes(char *output, - const char *hrp, - const uint8_t *data, - size_t data_len) { - output[0] = 0; - if (data_len > 128) { - return; +zxerr_t bech32EncodeFromBytes(char *out, + size_t out_len, + const char *hrp, + const uint8_t *in, + size_t in_len, + uint8_t pad) { + MEMZERO(out, out_len); + + if (in_len > MAX_INPUT_SIZE) { + return zxerr_out_of_bounds; + } + + size_t hrplen = strlen(hrp); + // We set a lower bound to ensure this is safe + if (out_len < hrplen + (in_len * 2) + 7) { + return zxerr_buffer_too_small; } - uint8_t tmp_data[128]; + // Overestimate required size *2==(8/4) instead of *(8/5) + uint8_t tmp_data[MAX_INPUT_SIZE * 2]; size_t tmp_size = 0; + MEMZERO(tmp_data, sizeof(tmp_data)); + + convert_bits(tmp_data, &tmp_size, 5, in, in_len, 8, pad); + if (tmp_size >= out_len) { + return zxerr_out_of_bounds; + } + + int err = bech32_encode(out, hrp, tmp_data, tmp_size); + if (err == 0) { + return zxerr_encoding_failed; + } - convert_bits(tmp_data, &tmp_size, 5, data, data_len, 8, 0); - bech32_encode(output, hrp, tmp_data, tmp_size); + return zxerr_ok; } diff --git a/deps/ledger-zxlib/src/bignum.c b/deps/ledger-zxlib/src/bignum.c new file mode 100644 index 00000000..59cb0459 --- /dev/null +++ b/deps/ledger-zxlib/src/bignum.c @@ -0,0 +1,148 @@ +/******************************************************************************* +* (c) 2019 Zondax GmbH +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +********************************************************************************/ +#include "zxtypes.h" +#include "bignum.h" + +bool_t bignumLittleEndian_bcdprint(char *outBuffer, uint16_t outBufferLen, + const uint8_t *inBCD, uint16_t inBCDLen) { + static const char hexchars[] = "0123456789ABCDEF"; + uint8_t started = 0; + MEMZERO(outBuffer, outBufferLen); + + if (outBufferLen < 4) { + return bool_false; + } + + if (inBCDLen * 2 > outBufferLen) { + strcpy(outBuffer, "ERR"); + return bool_false; + } + + for (uint8_t i = 0; i < inBCDLen; i++, inBCD++) { + if (started || *inBCD != 0) { + if (started || (*inBCD >> 4u) != 0) { + *outBuffer = hexchars[*inBCD >> 4u]; + outBuffer++; + }; + *outBuffer = hexchars[*inBCD & 0x0Fu]; + outBuffer++; + started = 1; + } + } + + if (!started) { + strcpy(outBuffer, "0"); + } + + return bool_true; +} + +void bignumLittleEndian_to_bcd(uint8_t *bcdOut, uint16_t bcdOutLen, + const uint8_t *binValue, uint16_t binValueLen) { + MEMZERO(bcdOut, bcdOutLen); + + uint8_t carry = 0; + for (uint16_t bitIdx = 0; bitIdx < binValueLen * 8; bitIdx++) { + // Fix bcd + for (uint16_t j = 0; j < bcdOutLen; j++) { + if ((bcdOut[j] & 0x0Fu) > 0x04u) { + bcdOut[j] += 0x03u; + } + if ((bcdOut[j] & 0xF0u) > 0x40u) { + bcdOut[j] += 0x30u; + } + } + + // get bit + const uint16_t byteIdx = bitIdx >> 3u; + const uint8_t mask = 0x80u >> (bitIdx & 0x7u); + carry = (binValue[binValueLen - byteIdx - 1] & mask) > 0; + + // Shift bcd + for (uint16_t j = 0; j < bcdOutLen; j++) { + uint8_t carry2 = bcdOut[bcdOutLen - j - 1] > 127u; + bcdOut[bcdOutLen - j - 1] <<= 1u; + bcdOut[bcdOutLen - j - 1] += carry; + carry = carry2; + } + } +} + +bool_t bignumBigEndian_bcdprint(char *outBuffer, uint16_t outBufferLen, + const uint8_t *bcdIn, uint16_t bcdInLen) { + static const char hexchars[] = "0123456789ABCDEF"; + uint8_t started = 0; + MEMZERO(outBuffer, outBufferLen); + + if (outBufferLen < 4) { + return bool_false; + } + + if (bcdInLen * 2 > outBufferLen) { + strcpy(outBuffer, "ERR"); + return bool_false; + } + + for (uint8_t i = 0; i < bcdInLen; i++) { + uint8_t v = bcdIn[bcdInLen - i - 1]; + if (started || v != 0) { + if (started || (v >> 4u) != 0) { + *outBuffer = hexchars[v >> 4u]; + outBuffer++; + }; + *outBuffer = hexchars[v & 0x0Fu]; + outBuffer++; + started = 1; + } + } + + if (!started) { + strcpy(outBuffer, "0"); + } + + return bool_true; +} + +void bignumBigEndian_to_bcd(uint8_t *bcdOut, uint16_t bcdOutLen, + const uint8_t *binValue, uint16_t binValueLen) { + MEMZERO(bcdOut, bcdOutLen); + + uint8_t carry = 0; + for (uint16_t bitIdx = 0; bitIdx < binValueLen * 8; bitIdx++) { + // Fix bcd + for (uint16_t j = 0; j < bcdOutLen; j++) { + if ((bcdOut[j] & 0x0Fu) > 0x04u) { + bcdOut[j] += 0x03u; + } + if ((bcdOut[j] & 0xF0u) > 0x40u) { + bcdOut[j] += 0x30u; + } + } + + // get bit + const uint16_t byteIdx = bitIdx >> 3u; + const uint8_t mask = 0x80u >> (bitIdx & 0x7u); + carry = (binValue[byteIdx] & mask) > 0; + + // Shift bcd + for (uint16_t j = 0; j < bcdOutLen; j++) { + uint8_t carry2 = bcdOut[j] > 127u; + bcdOut[j] <<= 1u; + bcdOut[j] += carry; + carry = carry2; + } + } +} diff --git a/deps/ledger-zxlib/src/buffering.c b/deps/ledger-zxlib/src/buffering.c index 985accf3..97d56fcd 100644 --- a/deps/ledger-zxlib/src/buffering.c +++ b/deps/ledger-zxlib/src/buffering.c @@ -1,5 +1,5 @@ /******************************************************************************* -* (c) 2018 ZondaX GmbH +* (c) 2018 Zondax GmbH * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/deps/ledger-zxlib/src/hexutils.c b/deps/ledger-zxlib/src/hexutils.c new file mode 100644 index 00000000..bc426072 --- /dev/null +++ b/deps/ledger-zxlib/src/hexutils.c @@ -0,0 +1,57 @@ +/******************************************************************************* +* (c) 2018 Zondax GmbH +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +********************************************************************************/ + +#include +#include +#include "hexutils.h" + +uint8_t hex2dec(char c, char *out) { + c = (char) tolower((int) c); + + if (!isxdigit((int) c)) { + return -1; + } + + if (isdigit((int) c)) { + *out = (char) (c - '0'); + return 0; + } + + *out = (char) (c - 'a' + 10); + return 0; +} + +size_t parseHexString(uint8_t *out, uint16_t outLen, const char *input) { + size_t len = strnlen(input, outLen * 2 + 1); + if ( (len / 2) > outLen) { + return 0; + } + if (len % 2 == 1) { + return 0; + } + + for (size_t i = 0; i < len; i += 2) { + char tmp1, tmp2; + if (hex2dec(input[i], &tmp1)) + return 0; + if (hex2dec(input[i + 1], &tmp2)) + return 0; + + out[i >> 1u] = (tmp1 << 4u) + tmp2; + } + + return (len / 2); +} diff --git a/deps/ledger-zxlib/src/segwit_addr.c b/deps/ledger-zxlib/src/segwit_addr.c index 406c4dc8..d3e99ab4 100644 --- a/deps/ledger-zxlib/src/segwit_addr.c +++ b/deps/ledger-zxlib/src/segwit_addr.c @@ -140,23 +140,23 @@ int bech32_decode(char* hrp, uint8_t *data, size_t *data_len, const char *input) return chk == 1; } -int convert_bits(uint8_t* out, size_t* outlen, int outbits, const uint8_t* in, size_t inlen, int inbits, int pad) { +int convert_bits(uint8_t* out, size_t* outlen, int outBits, const uint8_t* in, size_t inLen, int inBits, int pad) { uint32_t val = 0; int bits = 0; - uint32_t maxv = (((uint32_t)1) << outbits) - 1; - while (inlen--) { - val = (val << inbits) | *(in++); - bits += inbits; - while (bits >= outbits) { - bits -= outbits; + uint32_t maxv = (((uint32_t)1) << outBits) - 1; + while (inLen--) { + val = (val << inBits) | *(in++); + bits += inBits; + while (bits >= outBits) { + bits -= outBits; out[(*outlen)++] = (val >> bits) & maxv; } } if (pad) { if (bits) { - out[(*outlen)++] = (val << (outbits - bits)) & maxv; + out[(*outlen)++] = (val << (outBits - bits)) & maxv; } - } else if (((val << (outbits - bits)) & maxv) || bits >= inbits) { + } else if (((val << (outBits - bits)) & maxv) || bits >= inBits) { return 0; } return 1; diff --git a/deps/ledger-zxlib/src/sigutils.c b/deps/ledger-zxlib/src/sigutils.c new file mode 100644 index 00000000..222a40dd --- /dev/null +++ b/deps/ledger-zxlib/src/sigutils.c @@ -0,0 +1,98 @@ +/******************************************************************************* +* (c) 2020 Zondax GmbH +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +********************************************************************************/ + +#include +#include + +err_convert_e convertDERtoRSV(const uint8_t *inSignatureDER, + unsigned int inInfo, + uint8_t *outR, + uint8_t *outS, + uint8_t *outV) { + + // https://github.com/libbitcoin/libbitcoin-system/wiki/ECDSA-and-DER-Signatures#serialised-der-signature-sequence + // 0 [1 byte] - DER Prefix + // 1 [1 byte] - Payload len + // 2 [1 byte] - R Marker. Always 02 + // 3 [1 byte] - R Len RLEN + // ROFFSET ... [.?. byte] - R ROFFSET + // ROFFSET+RLEN [1 byte] - S Marker. Always 02 + // ROFFSET+RLEN+1 [1 byte] - S Length SLEN + // ROFFSET+RLEN+2 [.?. byte] - S SOFFSET + // Prepare response + // R [32] + // S [32] + // V [1] + + const uint8_t derPrefix = *(inSignatureDER); + if (derPrefix != 0x30) { + return invalid_derPrefix; + } + + const uint8_t payloadLen = *(inSignatureDER + 1); + const uint8_t minPayloadLen = 2 + 32 + 2 + 32; + const uint8_t maxPayloadLen = 2 + 33 + 2 + 33; + if (payloadLen < minPayloadLen || payloadLen > maxPayloadLen) { + return invalid_payloadLen; + } + + const uint8_t rMarker = *(inSignatureDER + 2); + if (rMarker != 0x02) { + return invalid_rmaker; + } + + const uint8_t rLen = *(inSignatureDER + 3); + if (rLen > 33 || rLen < 32) { + return invalid_rLen; + } + + const uint8_t sMarker = *(inSignatureDER + 4 + rLen); + if (sMarker != 0x02) { + return invalid_smarker; + } + + const uint8_t sLen = *(inSignatureDER + 4 + rLen + 1); + if (sLen > 33 || sLen < 32) { + return invalid_sLen; + } + + // Get data fields + const uint8_t *rPtr = inSignatureDER + 4; + // Correct field pointers + if (rLen == 33) { + rPtr++; // get only 32 bytes + } + + const uint8_t *sPtr = inSignatureDER + 4 + rLen + 2; + if (sLen == 33) { + sPtr++; // get only 32 bytes + } + + // Prepare V + *outV = 0; + if (inInfo & CX_ECCINFO_PARITY_ODD) { + *outV += 1; + } + if (inInfo & CX_ECCINFO_xGTn) { + *outV += 2; + } + + // Copy things + MEMCPY(outR, rPtr, 32); + MEMCPY(outS, sPtr, 32); + + return no_error; +} diff --git a/deps/ledger-zxlib/src/zxmacros.c b/deps/ledger-zxlib/src/zxmacros.c index 65fb22c4..7c6ca13e 100644 --- a/deps/ledger-zxlib/src/zxmacros.c +++ b/deps/ledger-zxlib/src/zxmacros.c @@ -1,5 +1,5 @@ /******************************************************************************* -* (c) 2018 ZondaX GmbH +* (c) 2018 Zondax GmbH * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -16,24 +16,6 @@ #include "zxmacros.h" #include "utf8.h" -#ifdef LEDGER_SPECIFIC -#include -#include "stdint.h" -void __logstack() -{ - uint8_t st; - uint32_t tmp1 = (uint32_t)&st - (uint32_t)&app_stack_canary; - uint32_t tmp2 = 0x20002800 - (uint32_t)&st; - char buffer[30]; - snprintf(buffer, 40, "%d / %d", tmp1, tmp2); - LOG(buffer); -} -#else - -void __logstack() {} - -#endif - size_t asciify(char *utf8_in_ascii_out) { return asciify_ext(utf8_in_ascii_out, utf8_in_ascii_out); @@ -47,7 +29,7 @@ size_t asciify_ext(const char *utf8_in, char *ascii_only_out) { while (*((char *) p) && utf8valid(p) == 0) { utf8_int32_t tmp_codepoint = 0; p = utf8codepoint(p, &tmp_codepoint); - *q = (tmp_codepoint >= 32 && tmp_codepoint <= 0x7F)? tmp_codepoint : '.'; + *q = (tmp_codepoint >= 32 && tmp_codepoint <= 0x7F) ? tmp_codepoint : '.'; q++; } @@ -55,3 +37,11 @@ size_t asciify_ext(const char *utf8_in, char *ascii_only_out) { *q = 0; return q - ascii_only_out; } + +void handle_stack_overflow() { +#if defined (TARGET_NANOS) || defined(TARGET_NANOX) + io_seproxyhal_se_reset(); +#else + while(1); +#endif +} diff --git a/deps/ledger-zxlib/templates/Makefile.root b/deps/ledger-zxlib/templates/Makefile.root new file mode 100644 index 00000000..a3fcae5f --- /dev/null +++ b/deps/ledger-zxlib/templates/Makefile.root @@ -0,0 +1,29 @@ +#******************************************************************************* +#* (c) 2019 Zondax GmbH +#* +#* Licensed under the Apache License, Version 2.0 (the "License"); +#* you may not use this file except in compliance with the License. +#* You may obtain a copy of the License at +#* +#* http://www.apache.org/licenses/LICENSE-2.0 +#* +#* Unless required by applicable law or agreed to in writing, software +#* distributed under the License is distributed on an "AS IS" BASIS, +#* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +#* See the License for the specific language governing permissions and +#* limitations under the License. +#******************************************************************************** + +# We use BOLOS_SDK to determine the develoment environment that is being used +# BOLOS_SDK IS DEFINED We use the plain Makefile for Ledger +# BOLOS_SDK NOT DEFINED We use a containerized build approach + +ifeq ($(BOLOS_SDK),) +include $(CURDIR)/deps/ledger-zxlib/cmake/dockerized_build.mk +else +default: + $(MAKE) -C app +%: + $(info "Calling app Makefile for target $@") + $(MAKE) -C app $@ +endif diff --git a/deps/ledger-zxlib/tests/asciify.cpp b/deps/ledger-zxlib/tests/asciify.cpp index 832d5ceb..eed67925 100644 --- a/deps/ledger-zxlib/tests/asciify.cpp +++ b/deps/ledger-zxlib/tests/asciify.cpp @@ -1,5 +1,5 @@ /******************************************************************************* -* (c) 2018 ZondaX GmbH +* (c) 2018 Zondax GmbH * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/deps/ledger-zxlib/tests/bech32.cpp b/deps/ledger-zxlib/tests/bech32.cpp index 9fc4e5b7..4e791419 100644 --- a/deps/ledger-zxlib/tests/bech32.cpp +++ b/deps/ledger-zxlib/tests/bech32.cpp @@ -1,5 +1,5 @@ /******************************************************************************* -* (c) 2019 ZondaX GmbH +* (c) 2019 Zondax GmbH * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -16,6 +16,7 @@ #include #include #include +#include namespace { TEST(BECH32, hex_to_address) { @@ -25,12 +26,58 @@ namespace { uint8_t data1[] = {1, 3, 5}; uint8_t data2[] = {1, 3, 5, 7, 9, 11, 13}; - bech32EncodeFromBytes(addr_out, hrp, data1, sizeof(data1)); + auto err = bech32EncodeFromBytes(addr_out, sizeof(addr_out), hrp, data1, sizeof(data1), 0); + ASSERT_EQ(err, zxerr_ok); std::cout << addr_out << std::endl; ASSERT_STREQ("zx1qypse825ac", addr_out); - bech32EncodeFromBytes(addr_out, hrp, data2, sizeof(data2)); + err = bech32EncodeFromBytes(addr_out, sizeof(addr_out), hrp, data2, sizeof(data2), 0); + ASSERT_EQ(err, zxerr_ok); std::cout << addr_out << std::endl; ASSERT_STREQ("zx1qyps2pcfpvx20dk22", addr_out); + + /// + err = bech32EncodeFromBytes(addr_out, sizeof(addr_out), hrp, data1, sizeof(data1), 1); + ASSERT_EQ(err, zxerr_ok); + std::cout << addr_out << std::endl; + ASSERT_STREQ("zx1qyps2ucfnzd", addr_out); + + err = bech32EncodeFromBytes(addr_out, sizeof(addr_out), hrp, data2, sizeof(data2), 1); + ASSERT_EQ(err, zxerr_ok); + std::cout << addr_out << std::endl; + ASSERT_STREQ("zx1qyps2pcfpvxshamanz", addr_out); + } + + TEST(BECH32, huge_input) { + char addr_out[200]; + const char *hrp = "zx"; + + auto data = std::vector(1000, 0x55); + + auto err = bech32EncodeFromBytes(addr_out, sizeof(addr_out), hrp, data.data(), data.size(),0); + ASSERT_EQ(err, zxerr_out_of_bounds); + + std::cout << addr_out << std::endl; + } + + TEST(BECH32, small_output) { + char addr_out[1000]; + const char *hrp = "zx"; + + auto data = std::vector(32, 0x55); + + MEMZERO(addr_out, sizeof(addr_out)); + + // declare size to be smaller + const size_t declared_size = 52; + + auto err = bech32EncodeFromBytes(addr_out, declared_size, hrp, data.data(), data.size(), 0); + ASSERT_EQ(err, zxerr_buffer_too_small); + + for (int i = declared_size; i < sizeof(addr_out); i++) { + ASSERT_EQ(addr_out[i], 0); + } + + std::cout << addr_out << std::endl; } } diff --git a/deps/ledger-zxlib/tests/bip44path.cpp b/deps/ledger-zxlib/tests/bip44path.cpp new file mode 100644 index 00000000..c4cb7a45 --- /dev/null +++ b/deps/ledger-zxlib/tests/bip44path.cpp @@ -0,0 +1,88 @@ +/******************************************************************************* +* (c) 2018 Zondax GmbH +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +********************************************************************************/ +#include +#include + +namespace { + TEST(MACROS, bip32empty) { + char buffer[100]; + uint32_t path[] = {44, 60, 0, 0, 1}; + bip32_to_str(buffer, sizeof(buffer), path, 0); + EXPECT_EQ("EMPTY PATH", std::string(buffer)); + } + + TEST(MACROS, bip32tooManyChildren) { + char buffer[100]; + uint32_t path[] = {44, 60, 0, 0, 1}; + bip32_to_str(buffer, sizeof(buffer), path, 200); + EXPECT_EQ("ERROR", std::string(buffer)); + } + + TEST(MACROS, bip32notEnoughSpaceInBuffer1) { + char buffer[6]; + uint32_t path[] = {1234, 60, 0, 0, 1}; + bip32_to_str(buffer, sizeof(buffer), path, 5); + EXPECT_EQ("ERROR", std::string(buffer)); + } + + TEST(MACROS, bip32notEnoughSpaceInBuffer2) { + char buffer[9]; + uint32_t path[] = {1, 1, 1, 1, 1}; + bip32_to_str(buffer, sizeof(buffer), path, 5); + EXPECT_EQ("ERROR", std::string(buffer)); + } + + TEST(MACROS, bip32notEnoughSpaceInBuffer3) { + char buffer[10]; + uint32_t path[] = {1, 1, 1, 1, 0x80000001 }; + bip32_to_str(buffer, sizeof(buffer), path, 5); + EXPECT_EQ("ERROR", std::string(buffer)); + } + + TEST(MACROS, bip32justEnoughSpaceInBuffer3) { + char buffer[10]; + uint32_t path[] = {1, 1, 1, 1, 1}; + bip32_to_str(buffer, sizeof(buffer), path, 5); + EXPECT_EQ("1/1/1/1/1", std::string(buffer)); + } + + TEST(MACROS, bip44path1) { + uint32_t path[] = {44, 60, 0, 0, 1}; + + char buffer[100]; + bip44_to_str(buffer, sizeof(buffer), path); + + EXPECT_EQ("44/60/0/0/1", std::string(buffer)); + } + + TEST(MACROS, bip44path2) { + uint32_t path[] = {0x8000002c, 60, 0, 0, 1}; + + char buffer[100]; + bip44_to_str(buffer, sizeof(buffer), path); + + EXPECT_EQ("44'/60/0/0/1", std::string(buffer)); + } + + TEST(MACROS, bip44path3) { + uint32_t path[] = {0x8000002c, 60, 0, 0, 0x80000001}; + + char buffer[100]; + bip44_to_str(buffer, sizeof(buffer), path); + + EXPECT_EQ("44'/60/0/0/1'", std::string(buffer)); + } +} diff --git a/deps/ledger-zxlib/tests/buffering_tests.cpp b/deps/ledger-zxlib/tests/buffering_tests.cpp index d3948196..4af9fd00 100644 --- a/deps/ledger-zxlib/tests/buffering_tests.cpp +++ b/deps/ledger-zxlib/tests/buffering_tests.cpp @@ -1,5 +1,5 @@ /******************************************************************************* -* (c) 2018 ZondaX GmbH +* (c) 2018 Zondax GmbH * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -20,7 +20,6 @@ namespace { TEST(Buffering, SmallBuffer) { - uint8_t ram_buffer[100]; uint8_t flash_buffer[1000]; @@ -43,7 +42,6 @@ namespace { } TEST(Buffering, BigBuffer) { - uint8_t ram_buffer[100]; uint8_t flash_buffer[1000]; @@ -66,7 +64,6 @@ namespace { } TEST(Buffering, SmallBufferMultipleTimesWithinRam) { - uint8_t ram_buffer[100]; uint8_t flash_buffer[1000]; @@ -93,7 +90,6 @@ namespace { } TEST(Buffering, SmallBufferMultipleTimesToFlash) { - uint8_t ram_buffer[100]; uint8_t flash_buffer[1000]; @@ -120,7 +116,6 @@ namespace { } TEST(Buffering, SmallBufferMultipleTimes_CheckData) { - uint8_t ram_buffer[100]; uint8_t flash_buffer[1000]; @@ -154,7 +149,6 @@ namespace { } TEST(Buffering, Reset) { - uint8_t ram_buffer[100]; uint8_t flash_buffer[1000]; @@ -174,11 +168,9 @@ namespace { EXPECT_TRUE(buffering_get_ram_buffer()->in_use) << "After reset RAM should be enabled by default"; EXPECT_FALSE(buffering_get_flash_buffer()->in_use) << "After reset RAM should be enabled by default"; - } TEST(Buffering, NotEnoughRoomInFlash) { - uint8_t ram_buffer[100]; uint8_t flash_buffer[1000]; @@ -193,7 +185,6 @@ namespace { } TEST(Buffering, NoFlashOnlyRAM) { - uint8_t ram_buffer[100]; buffering_init(ram_buffer, diff --git a/deps/ledger-zxlib/tests/doubledabble.cpp b/deps/ledger-zxlib/tests/doubledabble.cpp new file mode 100644 index 00000000..10133a8f --- /dev/null +++ b/deps/ledger-zxlib/tests/doubledabble.cpp @@ -0,0 +1,164 @@ +/******************************************************************************* +* (c) 2019 Zondax GmbH +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +********************************************************************************/ + +#include "gmock/gmock.h" + +#include +#include "bignum.h" + +using ::testing::TestWithParam; +using ::testing::Values; + +typedef struct { + std::string hex; + std::string expectedOutput; +} bignum_testcase_t; + +class BignumLittleEndianTests : public ::testing::TestWithParam { +}; + +class BignumBigEndianTests : public ::testing::TestWithParam { +}; + +INSTANTIATE_TEST_CASE_P + +( + BignumTestCases, BignumLittleEndianTests, testing::Values( + bignum_testcase_t{"00", "0"}, + bignum_testcase_t{"01", "1"}, + bignum_testcase_t{"0001", "256"}, + bignum_testcase_t{"03E8", "59395"}, + bignum_testcase_t{"E803", "1000"}, + bignum_testcase_t{"10", "16"}, + bignum_testcase_t{"FF01", "511"}, + bignum_testcase_t{"0102", "513"}, + bignum_testcase_t{"FFFF01", "131071"}, + bignum_testcase_t{"a08601", "100000"}, + bignum_testcase_t{"40420f", "1000000"}, + bignum_testcase_t{"809698", "10000000"}, + bignum_testcase_t{"002d3101", "20000000"}, + bignum_testcase_t{"00e1f505", "100000000"}, + bignum_testcase_t{"00407a10f35a", "100000000000000"}, + bignum_testcase_t{"d2029649", "1234567890"}, + bignum_testcase_t{"d20a3fce96f1cf8c9cb4378c37a4873f17621ebce404f5aa13", + "123456789012345678901234567890123456789012345678901234567890"} +)); + +// Check that bignums are printed properly (parametric tests) +TEST_P(BignumLittleEndianTests, print) { + auto testcase = GetParam(); + + uint8_t inBuffer[100]; + auto inBufferLen = parseHexString(inBuffer, sizeof(inBuffer), testcase.hex.c_str()); + + uint8_t bcdOut[100]; + uint16_t bcdOutLen = sizeof(bcdOut); + + bignumLittleEndian_to_bcd(bcdOut, bcdOutLen, inBuffer, inBufferLen); + + char bufferUI[300]; + bignumLittleEndian_bcdprint(bufferUI, sizeof(bufferUI), bcdOut, bcdOutLen); + EXPECT_THAT(std::string(bufferUI), testing::Eq(testcase.expectedOutput)); +} + +// Check that bignums are printed properly (range tests) +TEST(BignumLittleEndianTests, range) { + uint8_t inBuffer[100]; + + for (uint64_t i = 0; i < 10000; i++) { + std::stringstream s; + uint64_t tmp = i; + while (tmp != 0) { + s << std::setfill('0') << std::setw(2) << std::hex << tmp % 256; + tmp /= 256; + } + auto inBufferLen = parseHexString(inBuffer, sizeof(inBuffer), s.str().c_str()); + + uint8_t bcdOut[100]; + uint16_t bcdOutLen = sizeof(bcdOut); + bignumLittleEndian_to_bcd(bcdOut, bcdOutLen, inBuffer, inBufferLen); + char bufferUI[300]; + bignumLittleEndian_bcdprint(bufferUI, sizeof(bufferUI), bcdOut, bcdOutLen); + + std::stringstream expected; + expected << i; + EXPECT_THAT(std::string(bufferUI), testing::Eq(expected.str())) << s.str(); + } +} + +INSTANTIATE_TEST_CASE_P + +( + BignumTestCases, BignumBigEndianTests, testing::Values( + bignum_testcase_t{"00", "0"}, + bignum_testcase_t{"01", "1"}, + bignum_testcase_t{"0001", "1"}, + bignum_testcase_t{"000001", "1"}, + bignum_testcase_t{"03E8", "1000"}, + bignum_testcase_t{"E803", "59395"}, + bignum_testcase_t{"10", "16"}, + bignum_testcase_t{"FF01", "65281"}, + bignum_testcase_t{"01FF", "511"}, + bignum_testcase_t{"0102", "258"}, + bignum_testcase_t{"FFFF01", "16776961"}, + bignum_testcase_t{"a08601", "10520065"}, + bignum_testcase_t{"40420f", "4211215"}, + bignum_testcase_t{"809698", "8427160"}, + bignum_testcase_t{"002d3101", "2961665"}, + bignum_testcase_t{"00e1f505", "14808325"}, + bignum_testcase_t{"00407a10f35a", "276925838170"}, + bignum_testcase_t{"d2029649", "3523384905"}, + bignum_testcase_t{"d20a3fce96f1cf8c9cb4378c37a4873f17621ebce404f5aa13", + "1318442675213289749221432902819395197389189473307425559128595"} +)); + +// Check that bignums are printed properly (parametric tests) +TEST_P(BignumBigEndianTests, print) { + auto testcase = GetParam(); + + uint8_t inBuffer[100]; + auto inBufferLen = parseHexString(inBuffer, sizeof(inBuffer), testcase.hex.c_str()); + + uint8_t bcdOut[100]; + uint16_t bcdOutLen = sizeof(bcdOut); + bignumBigEndian_to_bcd(bcdOut, bcdOutLen, inBuffer, inBufferLen); + + char bufferUI[300]; + bignumBigEndian_bcdprint(bufferUI, sizeof(bufferUI), bcdOut, bcdOutLen); + EXPECT_THAT(std::string(bufferUI), testing::Eq(testcase.expectedOutput)); +} + +// Check that bignums are printed properly (range tests) +TEST(BignumBigEndianTests, range) { + uint8_t inBuffer[100]; + + for (uint64_t i = 0; i < 2500; i += 7) { + std::stringstream s; + s << std::setfill('0') << std::setw(10) << std::hex << i; + std::cout << s.str() << std::endl; + auto inBufferLen = parseHexString(inBuffer, sizeof(inBuffer), s.str().c_str()); + + uint8_t bcdOut[100]; + uint16_t bcdOutLen = sizeof(bcdOut); + bignumBigEndian_to_bcd(bcdOut, bcdOutLen, inBuffer, inBufferLen); + char bufferUI[300]; + bignumBigEndian_bcdprint(bufferUI, sizeof(bufferUI), bcdOut, bcdOutLen); + + std::stringstream expected; + expected << i; + EXPECT_THAT(std::string(bufferUI), testing::Eq(expected.str())) << s.str(); + } +} diff --git a/deps/ledger-zxlib/tests/hexutils.cpp b/deps/ledger-zxlib/tests/hexutils.cpp new file mode 100644 index 00000000..e9d3ae4c --- /dev/null +++ b/deps/ledger-zxlib/tests/hexutils.cpp @@ -0,0 +1,51 @@ +/******************************************************************************* +* (c) 2018 Zondax GmbH +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +********************************************************************************/ + +#include "gmock/gmock.h" + +#include + +#include "hexutils.h" + +TEST(HEXUTILS, parseHexString) { + char s[] = "1234567890"; + uint8_t data[100]; + + auto length = parseHexString(data, sizeof(data), s); + + ASSERT_THAT(length, testing::Eq(5)); + + ASSERT_THAT(data[0], testing::Eq(0x12)); + ASSERT_THAT(data[1], testing::Eq(0x34)); + ASSERT_THAT(data[2], testing::Eq(0x56)); + ASSERT_THAT(data[3], testing::Eq(0x78)); + ASSERT_THAT(data[4], testing::Eq(0x90)); +} + +TEST(HEXUTILS, parseHexString2) { + char s[] = "be333be7ee"; + uint8_t data[100]; + + auto length = parseHexString(data, sizeof(data), s); + + ASSERT_THAT(length, testing::Eq(5)); + + ASSERT_THAT(data[0], testing::Eq(0xbe)); + ASSERT_THAT(data[1], testing::Eq(0x33)); + ASSERT_THAT(data[2], testing::Eq(0x3b)); + ASSERT_THAT(data[3], testing::Eq(0xe7)); + ASSERT_THAT(data[4], testing::Eq(0xee)); +} diff --git a/deps/ledger-zxlib/tests/macros.cpp b/deps/ledger-zxlib/tests/macros.cpp index 4d5cdc2f..75b3042e 100644 --- a/deps/ledger-zxlib/tests/macros.cpp +++ b/deps/ledger-zxlib/tests/macros.cpp @@ -1,5 +1,5 @@ /******************************************************************************* -* (c) 2018 ZondaX GmbH +* (c) 2018 Zondax GmbH * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -17,263 +17,361 @@ #include namespace { -TEST(MACROS, array_to_hexstr) { - uint8_t array1[] = {1, 3, 5}; + TEST(FORMAT, array_to_hexstr) { + uint8_t array1[] = {1, 3, 5}; - char output[20]; - memset(output, 1, 20); + char output[20]; + memset(output, 1, 20); - array_to_hexstr(output, array1, sizeof(array1)); - EXPECT_EQ(memcmp(output, "010305", 2*sizeof(array1)), 0); - EXPECT_EQ(output[2*sizeof(array1)], 0); -} -} + array_to_hexstr(output, sizeof(output), array1, sizeof(array1)); + EXPECT_EQ(memcmp(output, "010305", 2 * sizeof(array1)), 0); + EXPECT_EQ(output[2 * sizeof(array1)], 0); + } -namespace { -TEST(MACROS, fpuint64_to_str) { - char output[100]; - printf("\n"); + TEST(FORMAT, fpuint64_to_str) { + char output[100]; + printf("\n"); - fpuint64_to_str(output, 123, 5); - printf("%10s\n", output); - EXPECT_EQ(std::string(output), "0.00123"); + fpuint64_to_str(output, sizeof(output), 123, 5); + printf("%10s\n", output); + EXPECT_EQ(std::string(output), "0.00123"); - fpuint64_to_str(output, 1234, 5); - printf("%10s\n", output); - EXPECT_EQ(std::string(output), "0.01234"); + fpuint64_to_str(output, sizeof(output), 1234, 5); + printf("%10s\n", output); + EXPECT_EQ(std::string(output), "0.01234"); - fpuint64_to_str(output, 12345, 5); - printf("%10s\n", output); - EXPECT_EQ(std::string(output), "0.12345"); + fpuint64_to_str(output, sizeof(output), 12345, 5); + printf("%10s\n", output); + EXPECT_EQ(std::string(output), "0.12345"); - fpuint64_to_str(output, 123456, 5); - printf("%10s\n", output); - EXPECT_EQ(std::string(output), "1.23456"); + fpuint64_to_str(output, sizeof(output), 123456, 5); + printf("%10s\n", output); + EXPECT_EQ(std::string(output), "1.23456"); - fpuint64_to_str(output, 1234567, 5); - printf("%10s\n", output); - EXPECT_EQ(std::string(output), "12.34567"); -} + fpuint64_to_str(output, sizeof(output), 1234567, 5); + printf("%10s\n", output); + EXPECT_EQ(std::string(output), "12.34567"); + } - TEST(MACROS, fpuint64_to_str_zeros) { + TEST(FORMAT, fpstr_to_str) { char output[100]; printf("\n"); - fpuint64_to_str(output, 0, 9); - printf("%11s\n", output); - EXPECT_EQ(std::string(output), "0.000000000"); + fpstr_to_str(output, sizeof(output), "", 0); + printf("%10s\n", output); + EXPECT_EQ(std::string(output), "0"); - fpuint64_to_str(output, 0, 1); - printf("%11s\n", output); - EXPECT_EQ(std::string(output), "0.0"); + fpstr_to_str(output, sizeof(output), "1", 0); + printf("%10s\n", output); + EXPECT_EQ(std::string(output), "1"); - fpuint64_to_str(output, 1, 1); - printf("%11s\n", output); - EXPECT_EQ(std::string(output), "0.1"); + fpstr_to_str(output, sizeof(output), "123", 0); + printf("%10s\n", output); + EXPECT_EQ(std::string(output), "123"); - fpuint64_to_str(output, 10, 1); - printf("%11s\n", output); - EXPECT_EQ(std::string(output), "1.0"); + fpstr_to_str(output, sizeof(output), "", 5); + printf("%10s\n", output); + EXPECT_EQ(std::string(output), "0.00000"); + + fpstr_to_str(output, sizeof(output), "0", 5); + printf("%10s\n", output); + EXPECT_EQ(std::string(output), "0.00000"); + + fpstr_to_str(output, sizeof(output), "123", 5); + printf("%10s\n", output); + EXPECT_EQ(std::string(output), "0.00123"); + + fpstr_to_str(output, sizeof(output), "1234", 5); + printf("%10s\n", output); + EXPECT_EQ(std::string(output), "0.01234"); + + fpstr_to_str(output, sizeof(output), "12345", 5); + printf("%10s\n", output); + EXPECT_EQ(std::string(output), "0.12345"); + + fpstr_to_str(output, sizeof(output), "123456", 5); + printf("%10s\n", output); + EXPECT_EQ(std::string(output), "1.23456"); + + fpstr_to_str(output, sizeof(output), "1234567", 5); + printf("%10s\n", output); + EXPECT_EQ(std::string(output), "12.34567"); } -TEST(INT64_TO_STR, Zero) { + TEST(FORMAT, fpstr_to_str_BAD_zeros) { + char output[8]; + printf("\n"); - char temp[10]; - const char* error = int64_to_str(temp, sizeof(temp), int64_t(0)); - EXPECT_STREQ(temp, "0"); - EXPECT_TRUE(error == nullptr); -} + fpstr_to_str(output, sizeof(output), "", 5); + printf("%10s\n", output); + EXPECT_EQ(std::string(output), "0.00000"); -TEST(INT64_TO_STR, Positive_1234) { + fpstr_to_str(output, sizeof(output), "", 6); + printf("%10s\n", output); + EXPECT_EQ(std::string(output), "0.000000"); - char temp[10]; - const char* error = int64_to_str(temp, sizeof(temp), int64_t(1234)); - EXPECT_STREQ(temp, "1234"); - EXPECT_TRUE(error == nullptr); -} + fpstr_to_str(output, sizeof(output), "", 7); + printf("%10s\n", output); + EXPECT_EQ(std::string(output), "ERR"); -TEST(INT64_TO_STR, Negative_1234) { + fpstr_to_str(output, sizeof(output), "", 8); + printf("%10s\n", output); + EXPECT_EQ(std::string(output), "ERR"); + } - char temp[10]; - const char* error = int64_to_str(temp, sizeof(temp), int64_t(-1234)); - EXPECT_STREQ(temp, "-1234"); - EXPECT_TRUE(error == nullptr); -} + TEST(FORMAT, fpstr_to_str_BAD_short) { + char output[8]; + printf("\n"); -TEST(INT64_TO_STR, TooSmall_0) { + fpstr_to_str(output, sizeof(output), "123", 5); + printf("%10s\n", output); + EXPECT_EQ(std::string(output), "0.00123"); - char temp[1]; - const char* error = int64_to_str(temp, sizeof(temp), int64_t(0)); - EXPECT_STREQ("Size too small", error); -} + fpstr_to_str(output, sizeof(output), "123", 6); + printf("%10s\n", output); + EXPECT_EQ(std::string(output), "0.000123"); -TEST(INT64_TO_STR, FitsJust) { + fpstr_to_str(output, sizeof(output), "123", 7); + printf("%10s\n", output); + EXPECT_EQ(std::string(output), "ERR"); - char temp[4]; - const char *error = int64_to_str(temp, sizeof(temp), int64_t(999)); - EXPECT_STREQ(temp, "999"); - EXPECT_TRUE(error == nullptr); -} + fpstr_to_str(output, sizeof(output), "123", 8); + printf("%10s\n", output); + EXPECT_EQ(std::string(output), "ERR"); + } -TEST(INT64_TO_STR, TooSmall_10) { + TEST(FORMAT, fpstr_to_str_BAD_long) { + char output[8]; + printf("\n"); - char temp[2]; - const char* error = int64_to_str(temp, sizeof(temp), int64_t(10)); - EXPECT_STREQ("Size too small", error); -} + fpstr_to_str(output, sizeof(output), "123456", 5); + printf("%10s\n", output); + EXPECT_EQ(std::string(output), "1.23456"); -TEST(INT64_TO_STR, Max) { + fpstr_to_str(output, sizeof(output), "123456", 6); + printf("%10s\n", output); + EXPECT_EQ(std::string(output), "0.123456"); - char temp[20]; - const char* error = int64_to_str(temp, sizeof(temp), std::numeric_limits::max()); - EXPECT_STREQ(temp, "9223372036854775807"); - EXPECT_TRUE(error == nullptr); -} + fpstr_to_str(output, sizeof(output), "123456", 7); + printf("%10s\n", output); + EXPECT_EQ(std::string(output), "ERR"); -TEST(INT64_TO_STR, Min) { + fpstr_to_str(output, sizeof(output), "12345", 7); + printf("%10s\n", output); + EXPECT_EQ(std::string(output), "ERR"); - char temp[21]; - const char* error = int64_to_str(temp, sizeof(temp), std::numeric_limits::min()); - EXPECT_STREQ(temp, "-9223372036854775808"); - EXPECT_TRUE(error == nullptr); -} + fpstr_to_str(output, sizeof(output), "12345", 2); + printf("%10s\n", output); + EXPECT_EQ(std::string(output), "123.45"); -TEST(STR_TO_INT8, Min) { + fpstr_to_str(output, sizeof(output), "123456", 2); + printf("%10s\n", output); + EXPECT_EQ(std::string(output), "1234.56"); - char numberStr[] = "-128"; - char error = 0; - int8_t number = str_to_int8(numberStr, numberStr + strlen(numberStr), &error); - EXPECT_EQ(-128, number); - EXPECT_EQ(0, error); -} + fpstr_to_str(output, sizeof(output), "1234567", 2); + printf("%10s\n", output); + EXPECT_EQ(std::string(output), "12345.67"); -TEST(STR_TO_INT8, Max) { + fpstr_to_str(output, sizeof(output), "12345678", 2); + printf("%10s\n", output); + EXPECT_EQ(std::string(output), "ERR"); + } - char numberStr[] = "127"; - char error = 0; - int8_t number = str_to_int8(numberStr, numberStr + strlen(numberStr), &error); - EXPECT_EQ(127, number); - EXPECT_EQ(0, error); -} + TEST(FORMAT, fpuint64_to_str_zeros) { + char output[100]; + printf("\n"); -TEST(STR_TO_INT8, Zero) { + fpuint64_to_str(output, sizeof(output), 0, 9); + printf("%11s\n", output); + EXPECT_EQ(std::string(output), "0.000000000"); - char numberStr[] = "0"; - char error = 0; - int8_t number = str_to_int8(numberStr, numberStr + strlen(numberStr), &error); - EXPECT_EQ(0, number); - EXPECT_EQ(0, error); -} + fpuint64_to_str(output, sizeof(output), 0, 1); + printf("%11s\n", output); + EXPECT_EQ(std::string(output), "0.0"); -TEST(STR_TO_INT8, Hundred) { + fpuint64_to_str(output, sizeof(output), 1, 1); + printf("%11s\n", output); + EXPECT_EQ(std::string(output), "0.1"); - char numberStr[] = "100"; - char error = 0; - int8_t number = str_to_int8(numberStr, numberStr + strlen(numberStr), &error); - EXPECT_EQ(100, number); - EXPECT_EQ(0, error); -} + fpuint64_to_str(output, sizeof(output), 10, 1); + printf("%11s\n", output); + EXPECT_EQ(std::string(output), "1.0"); + } -TEST(STR_TO_INT8, NegHundred) { + TEST(INT64_TO_STR, Zero) { + char temp[10]; + const char *error = int64_to_str(temp, sizeof(temp), int64_t(0)); + EXPECT_STREQ(temp, "0"); + EXPECT_TRUE(error == nullptr); + } - char numberStr[] = "-100"; - char error = 0; - int8_t number = str_to_int8(numberStr, numberStr + strlen(numberStr), &error); - EXPECT_EQ(-100, number); - EXPECT_EQ(0, error); -} + TEST(INT64_TO_STR, Positive_1234) { + char temp[10]; + const char *error = int64_to_str(temp, sizeof(temp), int64_t(1234)); + EXPECT_STREQ(temp, "1234"); + EXPECT_TRUE(error == nullptr); + } -TEST(STR_TO_INT8, OutsideBoundsPositive) { + TEST(INT64_TO_STR, Negative_1234) { + char temp[10]; + const char *error = int64_to_str(temp, sizeof(temp), int64_t(-1234)); + EXPECT_STREQ(temp, "-1234"); + EXPECT_TRUE(error == nullptr); + } - char numberStr[] = "128"; - char error = 0; - int8_t number = str_to_int8(numberStr, numberStr + strlen(numberStr), &error); - EXPECT_EQ(1, error); -} + TEST(INT64_TO_STR, TooSmall_0) { + char temp[1]; + const char *error = int64_to_str(temp, sizeof(temp), int64_t(0)); + EXPECT_STREQ("Buffer too small", error); + } -TEST(STR_TO_INT8, OutsideBoundsNegative) { + TEST(INT64_TO_STR, FitsJust) { + char temp[4]; + const char *error = int64_to_str(temp, sizeof(temp), int64_t(999)); + EXPECT_STREQ(temp, "999"); + EXPECT_TRUE(error == nullptr); + } - char numberStr[] = "-129"; - char error = 0; - int8_t number = str_to_int8(numberStr, numberStr + strlen(numberStr), &error); - EXPECT_EQ(1, error); -} + TEST(INT64_TO_STR, TooSmall_10) { + char temp[2]; + const char *error = int64_to_str(temp, sizeof(temp), int64_t(10)); + EXPECT_STREQ("Buffer too small", error); + } + TEST(INT64_TO_STR, Max) { + char temp[20]; + const char *error = int64_to_str(temp, sizeof(temp), std::numeric_limits::max()); + EXPECT_STREQ(temp, "9223372036854775807"); + EXPECT_TRUE(error == nullptr); + } -TEST(STR_TO_INT8, DummyData_Positive) { + TEST(INT64_TO_STR, Min) { + char temp[21]; + const char *error = int64_to_str(temp, sizeof(temp), std::numeric_limits::min()); + EXPECT_STREQ(temp, "-9223372036854775808"); + EXPECT_TRUE(error == nullptr); + } - char numberStr[] = "100b0"; - char error = 0; - int8_t number = str_to_int8(numberStr, numberStr + strlen(numberStr), &error); - EXPECT_EQ(1, error); -} + TEST(STR_TO_INT8, Min) { + char numberStr[] = "-128"; + char error = 0; + int8_t number = str_to_int8(numberStr, numberStr + strlen(numberStr), &error); + EXPECT_EQ(-128, number); + EXPECT_EQ(0, error); + } -TEST(STR_TO_INT8, DummyData_Negative) { + TEST(STR_TO_INT8, Max) { + char numberStr[] = "127"; + char error = 0; + int8_t number = str_to_int8(numberStr, numberStr + strlen(numberStr), &error); + EXPECT_EQ(127, number); + EXPECT_EQ(0, error); + } - char numberStr[] = "-1002xx"; - char error = 0; - int8_t number = str_to_int8(numberStr, numberStr + strlen(numberStr), &error); - EXPECT_EQ(1, error); -} + TEST(STR_TO_INT8, Zero) { + char numberStr[] = "0"; + char error = 0; + int8_t number = str_to_int8(numberStr, numberStr + strlen(numberStr), &error); + EXPECT_EQ(0, number); + EXPECT_EQ(0, error); + } -TEST(STR_TO_INT64, Min) { + TEST(STR_TO_INT8, Hundred) { + char numberStr[] = "100"; + char error = 0; + int8_t number = str_to_int8(numberStr, numberStr + strlen(numberStr), &error); + EXPECT_EQ(100, number); + EXPECT_EQ(0, error); + } - char numberStr[] = "-9223372036854775808"; - char error = 0; - int64_t number = str_to_int64(numberStr, numberStr + strlen(numberStr), &error); - EXPECT_EQ(-9223372036854775808, number); - EXPECT_EQ(0, error); -} + TEST(STR_TO_INT8, NegHundred) { + char numberStr[] = "-100"; + char error = 0; + int8_t number = str_to_int8(numberStr, numberStr + strlen(numberStr), &error); + EXPECT_EQ(-100, number); + EXPECT_EQ(0, error); + } -TEST(STR_TO_INT64, Max) { + TEST(STR_TO_INT8, OutsideBoundsPositive) { + char numberStr[] = "128"; + char error = 0; + int8_t number = str_to_int8(numberStr, numberStr + strlen(numberStr), &error); + EXPECT_EQ(1, error); + } - char numberStr[] = "9223372036854775807"; - char error = 0; - int64_t number = str_to_int64(numberStr, numberStr + strlen(numberStr), &error); - EXPECT_EQ(9223372036854775807, number); - EXPECT_EQ(0, error); -} + TEST(STR_TO_INT8, OutsideBoundsNegative) { + char numberStr[] = "-129"; + char error = 0; + int8_t number = str_to_int8(numberStr, numberStr + strlen(numberStr), &error); + EXPECT_EQ(1, error); + } -TEST(STR_TO_INT64, Zero) { - char numberStr[] = "0"; - char error = 0; - int64_t number = str_to_int64(numberStr, numberStr + strlen(numberStr), &error); - EXPECT_EQ(0, number); - EXPECT_EQ(0, error); -} + TEST(STR_TO_INT8, DummyData_Positive) { + char numberStr[] = "100b0"; + char error = 0; + int8_t number = str_to_int8(numberStr, numberStr + strlen(numberStr), &error); + EXPECT_EQ(1, error); + } -TEST(STR_TO_INT64, Hundred) { + TEST(STR_TO_INT8, DummyData_Negative) { + char numberStr[] = "-1002xx"; + char error = 0; + int8_t number = str_to_int8(numberStr, numberStr + strlen(numberStr), &error); + EXPECT_EQ(1, error); + } - char numberStr[] = "100"; - char error = 0; - int64_t number = str_to_int64(numberStr, numberStr + strlen(numberStr), &error); - EXPECT_EQ(100, number); - EXPECT_EQ(0, error); -} + TEST(STR_TO_INT64, Min) { + char numberStr[] = "-9223372036854775807"; + char error = 0; + int64_t number = str_to_int64(numberStr, numberStr + strlen(numberStr), &error); + EXPECT_EQ(-9223372036854775807, number); + EXPECT_EQ(0, error); + } -TEST(STR_TO_INT64, NegHundred) { + TEST(STR_TO_INT64, Max) { + char numberStr[] = "9223372036854775807"; + char error = 0; + int64_t number = str_to_int64(numberStr, numberStr + strlen(numberStr), &error); + EXPECT_EQ(9223372036854775807, number); + EXPECT_EQ(0, error); + } - char numberStr[] = "-100"; - char error = 0; - int64_t number = str_to_int64(numberStr, numberStr + strlen(numberStr), &error); - EXPECT_EQ(-100, number); - EXPECT_EQ(0, error); -} + TEST(STR_TO_INT64, Zero) { + char numberStr[] = "0"; + char error = 0; + int64_t number = str_to_int64(numberStr, numberStr + strlen(numberStr), &error); + EXPECT_EQ(0, number); + EXPECT_EQ(0, error); + } -TEST(STR_TO_INT64, DummyData_Positive) { + TEST(STR_TO_INT64, Hundred) { + char numberStr[] = "100"; + char error = 0; + int64_t number = str_to_int64(numberStr, numberStr + strlen(numberStr), &error); + EXPECT_EQ(100, number); + EXPECT_EQ(0, error); + } - char numberStr[] = "100b0"; - char error = 0; - int64_t number = str_to_int64(numberStr, numberStr + strlen(numberStr), &error); - EXPECT_EQ(1, error); -} + TEST(STR_TO_INT64, NegHundred) { + char numberStr[] = "-100"; + char error = 0; + int64_t number = str_to_int64(numberStr, numberStr + strlen(numberStr), &error); + EXPECT_EQ(-100, number); + EXPECT_EQ(0, error); + } -TEST(STR_TO_INT64, DummyData_Negative) { + TEST(STR_TO_INT64, DummyData_Positive) { + char numberStr[] = "100b0"; + char error = 0; + int64_t number = str_to_int64(numberStr, numberStr + strlen(numberStr), &error); + EXPECT_EQ(1, error); + } - char numberStr[] = "-1002xx"; - char error = 0; - int64_t number = str_to_int64(numberStr, numberStr + strlen(numberStr), &error); - EXPECT_EQ(1, error); -} + TEST(STR_TO_INT64, DummyData_Negative) { + char numberStr[] = "-1002xx"; + char error = 0; + int64_t number = str_to_int64(numberStr, numberStr + strlen(numberStr), &error); + EXPECT_EQ(1, error); + } } diff --git a/deps/ledger-zxlib/tests/sigutils.cpp b/deps/ledger-zxlib/tests/sigutils.cpp new file mode 100644 index 00000000..1734112b --- /dev/null +++ b/deps/ledger-zxlib/tests/sigutils.cpp @@ -0,0 +1,46 @@ +/******************************************************************************* +* (c) 2020 Zondax GmbH +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +********************************************************************************/ + +#include "gmock/gmock.h" +#include +#include + +#include "sigutils.h" + +TEST(SIGUTILS, convertBasic) { + char inSignatureDERStr[] = "304402206878b5690514437a2342405029426cc2b25b4a03fc396fef845d656cf62bad2c022018610a8d37e3384245176ab49ddbdbe8da4133f661bf5ea7ad4e3d2b912d856f01"; + auto inSignatureDER = std::vector(71); + + auto length = parseHexString(inSignatureDER.data(), inSignatureDER.size(), inSignatureDERStr); + EXPECT_EQ(length, sizeof(inSignatureDERStr) / 2); + + uint8_t R[32]; + uint8_t S[32]; + uint8_t V; + + auto ret = convertDERtoRSV(inSignatureDER.data(), 0, R, S, &V); + EXPECT_EQ(ret, 0); + + char inSignatureDERStr_R[] = "6878b5690514437a2342405029426cc2b25b4a03fc396fef845d656cf62bad2c"; + char inSignatureDERStr_S[] = "18610a8d37e3384245176ab49ddbdbe8da4133f661bf5ea7ad4e3d2b912d856f"; + auto inSignatureDER_R = std::vector(32); + auto inSignatureDER_S = std::vector(32); + parseHexString(inSignatureDER_R.data(), inSignatureDER_R.size(), inSignatureDERStr_R); + parseHexString(inSignatureDER_S.data(), inSignatureDER_S.size(), inSignatureDERStr_S); + + EXPECT_THAT(R, ::testing::ElementsAreArray(inSignatureDER_R)); + EXPECT_THAT(S, ::testing::ElementsAreArray(inSignatureDER_S)); +} diff --git a/deps/nanos-secure-sdk b/deps/nanos-secure-sdk new file mode 160000 index 00000000..1f270694 --- /dev/null +++ b/deps/nanos-secure-sdk @@ -0,0 +1 @@ +Subproject commit 1f2706941b68d897622f75407a868b60eb2be8d7 diff --git a/docs/APDUSPEC.md b/docs/APDUSPEC.md index 4d81bded..4a33b5a8 100644 --- a/docs/APDUSPEC.md +++ b/docs/APDUSPEC.md @@ -63,33 +63,35 @@ The general structure of commands and responses is as follows: -------------- -### PUBLIC_KEY_SECP256K1 (deprecated) +### INS_GET_ADDR_SECP256K1 #### Command -| Field | Type | Content | Expected | -| ---------- | -------- | ---------------------- | --------- | -| CLA | byte (1) | Application Identifier | 0x55 | -| INS | byte (1) | Instruction ID | 0x01 | -| P1 | byte (1) | Parameter 1 | ignored | -| P2 | byte (1) | Parameter 2 | ignored | -| L | byte (1) | Bytes in payload | (depends) | -| PL | byte (1) | Derivation Path Length | 3<=PL<=10 | -| Path[0] | byte (4) | Derivation Path Data | 44 | -| Path[1] | byte (4) | Derivation Path Data | 118 | -| .. | byte (4) | Derivation Path Data | | -| Path[PL-1] | byte (4) | Derivation Path Data | | +| Field | Type | Content | Expected | +| ---------- | -------------- | ------------------------------ | -------------- | +| CLA | byte (1) | Application Identifier | 0x55 | +| INS | byte (1) | Instruction ID | 0x04 | +| P1 | byte (1) | Display address/path on device | 0x00 No | +| | | | 0x01 Yes | +| P2 | byte (1) | Parameter 2 | ignored | +| L | byte (1) | Bytes in payload | (depends) | +| HRP_LEN | byte(1) | Bech32 HRP Length | 1<=HRP_LEN<=83 | +| HRP | byte (HRP_LEN) | Bech32 HRP | | +| Path[0] | byte (4) | Derivation Path Data | 44 | +| Path[1] | byte (4) | Derivation Path Data | 118 | +| Path[2] | byte (4) | Derivation Path Data | ? | +| Path[3] | byte (4) | Derivation Path Data | ? | +| Path[4] | byte (4) | Derivation Path Data | ? | First three items in the derivation path will be hardened automatically hardened #### Response -| Field | Type | Content | Note | -| ------- | --------- | ----------- | ------------------------ | -| PK | byte (65) | Public Key | | -| SW1-SW2 | byte (2) | Return code | see list of return codes | - --------------- +| Field | Type | Content | Note | +| ------- | --------- | --------------------- | ------------------------ | +| PK | byte (33) | Compressed Public Key | | +| ADDR | byte (65) | Bech 32 addr | | +| SW1-SW2 | byte (2) | Return code | see list of return codes | ### SIGN_SECP256K1 @@ -99,25 +101,25 @@ First three items in the derivation path will be hardened automatically hardened | ----- | -------- | ---------------------- | --------- | | CLA | byte (1) | Application Identifier | 0x55 | | INS | byte (1) | Instruction ID | 0x02 | -| P1 | byte (1) | Packet Current Index | | -| P2 | byte (1) | Packet Total Count | -| | +| P1 | byte (1) | Payload desc | 0 = init | +| | | | 1 = add | +| | | | 2 = last | +| P2 | byte (1) | ---- | not used | | L | byte (1) | Bytes in payload | (depends) | The first packet/chunk includes only the derivation path -All other packets/chunks should contain message to sign +All other packets/chunks should contain message to sign *First Packet* | Field | Type | Content | Expected | | ---------- | -------- | ---------------------- | --------- | -| PL | byte (1) | Derivation Path Length | 3<=PL<=10 | | Path[0] | byte (4) | Derivation Path Data | 44 | | Path[1] | byte (4) | Derivation Path Data | 118 | -| .. | byte (4) | Derivation Path Data | | -| Path[PL-1] | byte (4) | Derivation Path Data | | -| Message | bytes... | Message to Sign | | +| Path[2] | byte (4) | Derivation Path Data | ? | +| Path[3] | byte (4) | Derivation Path Data | ? | +| Path[4] | byte (4) | Derivation Path Data | ? | *Other Chunks/Packets* @@ -133,32 +135,3 @@ All other packets/chunks should contain message to sign | SW1-SW2 | byte (2) | Return code | see list of return codes | -------------- - -### INS_GET_ADDR_SECP256K1 - -#### Command - -| Field | Type | Content | Expected | -| ---------- | -------------- | ---------------------- | -------------- | -| CLA | byte (1) | Application Identifier | 0x55 | -| INS | byte (1) | Instruction ID | 0x04 | -| P1 | byte (1) | Parameter 1 | ignored | -| P2 | byte (1) | Parameter 2 | ignored | -| L | byte (1) | Bytes in payload | (depends) | -| HRP_LEN | byte(1) | Bech32 HRP Length | 1<=HRP_LEN<=83 | -| HRP | byte (HRP_LEN) | Bech32 HRP | | -| PL | byte (1) | Derivation Path Length | 3<=PL<=10 | -| Path[0] | byte (4) | Derivation Path Data | 44 | -| Path[1] | byte (4) | Derivation Path Data | 118 | -| .. | byte (4) | Derivation Path Data | | -| Path[PL-1] | byte (4) | Derivation Path Data | | - -First three items in the derivation path will be hardened automatically hardened - -#### Response - -| Field | Type | Content | Note | -| ------- | --------- | --------------------- | ------------------------ | -| PK | byte (33) | Compressed Public Key | | -| ADDR | byte (65) | Bech 32 addr | | -| SW1-SW2 | byte (2) | Return code | see list of return codes | diff --git a/docs/BUILD.md b/docs/BUILD.md deleted file mode 100644 index c9720494..00000000 --- a/docs/BUILD.md +++ /dev/null @@ -1,5 +0,0 @@ -# Building Cosmos App - Ledger Nano S - -Please refer to the [Ledger-Cosmos](https://github.com/cosmos/ledger-cosmos) for the complete source code, build instructions, etc (unit tests, integration tests, documentation, etc.) - -Up to date instructions are kept [here](https://github.com/cosmos/ledger-cosmos/blob/master/docs/BUILD.md) diff --git a/docs/UISPEC.md b/docs/UISPEC.md deleted file mode 100644 index ad39e0f4..00000000 --- a/docs/UISPEC.md +++ /dev/null @@ -1,207 +0,0 @@ -UI Specification -------------------------- - -## Terminology: - -**Left click** - clicking and releasing left button - -**Right click** - clicking and releasing right button - -**Double click** - clicking and releasing both left and right - -**Left hold** - clicking and holding left button - -**Right hold** - clicking and holding right button - -**Double hold** - clicking and holding both left and right - -## UI pages: -### A. 'Welcome' page - -#### Layout: -Screen 1. (default) -``` -LINE1: -LINE2: Tendermint -LINE3: [tendermint icon] Cosmos TEST! [down icon] -``` - -Note: Test only appears when the app has been compiled in test mode. - -Screen 2. -``` -LINE1: -LINE2: [up icon] About [down icon] -LINE3: -``` - -Screen 3. -``` -LINE1: -LINE2: [up icon] [quit icon] Quit app -LINE3: -``` -#### Interface: -##### Left click -Moves to the previous screen. -##### Right click -Moves to the next screen. -##### Left and Right clicked together on the Screen #3 -Exits the app. - -### B. 'Received Transaction' page - -#### Layout: -Screen 1. (default) -``` -LINE1: -LINE2: View transaction [down icon] -LINE3: -``` -Screen 2. -``` -LINE1: -LINE2: [up icon] Sign transaction [down icon] -LINE3: -``` - -Screen 3. -``` -LINE1: -LINE2: [up icon] [reject icon] Reject -LINE3: -``` - -#### Interface: -##### Left click -Moves to the previous screen. -##### Right click -Moves to the next screen. -##### Double click on the Screen #1 -Switches UI to the 'View Transaction' page -##### Double click on the Screen #2 -- signs transaction -- returns signed transaction to the client app -- switches UI to the 'Welcome' page -##### Double click on the Screen #3 -- rejects transaction -- switches UI to the 'Welcome' page - -### C. 'View Transaction' page - -#### Layout: - -Screen (template) -``` -LINE1: [left icon] SECP256K1 - 0n/0k [right icon] -LINE2: title (json element key) -LINE3: value (json element value) -``` - -Line1 contains the page info - the signature type (here SECP256K1) followed by the page index (n) and the total number of pages (k). - -Line2 contains the title - key element from the json transaction for a particular page. - -Line3 contains the value - value element from the json transaction for a particular page. - -##### Here's the page breakdown for a simple json transaction: - -Screen 1. (default) -``` -LINE1: [left icon] SECP256K1 - 01/08 [right icon] -LINE2: chain_id -LINE3: test-chain-1 -``` - -Screen 2. -``` -LINE1: [left icon] SECP256K1 - 02/09 [right icon] -LINE2: account_number -LINE3: 238 -``` - -Screen 3. -``` -LINE1: [left icon] SECP256K1 - 03/09 [right icon] -LINE2: sequence -LINE3: 1 -``` - -Screen 4. -``` -LINE1: [left icon] SECP256K1 - 04/09 [right icon] -LINE2: fee -LINE3: {"amount":[{"denom":"photon", "amount":5}], "gas":10000} -``` - -Screen 5. -``` -LINE1: [left icon] SECP256K1 - 05/09 [right icon] -LINE2: msgs/0/inputs/address -LINE3: 69FE2314BAC34EF -``` - -Screen 6. -``` -LINE1: [left icon] SECP256K1 - 06/08 [right icon] -LINE2: msgs/0/inputs/coins -LINE3: 69FE2314BAC34EF -``` - -Screen 7. -``` -LINE1: [left icon] SECP256K1 - 07/08 [right icon] -LINE2: msgs/0/outputs/address -LINE3: 69FE2314BAC34EF -``` - -Screen 8. -``` -LINE1: [left icon] SECP256K1 - 08/09 [right icon] -LINE2: msgs/0/outputs/coins -LINE3: 69FE2314BAC34EF -``` - -Screen 9. -``` -LINE1: [left icon] SECP256K1 - 09/09 [right icon] -LINE2: memo -LINE3: for_coffee -``` - -#### Scrolling -Ledger screen can only fit around 20 characters and therefore we need a way for displaying the key and the value strings that are longer. Here we assume that the page info will always fit the screen line. - -Ledger supports the smooth pixel scrolling which is enabled by default for the line #3 (i.e. the line that holds the json value). If a text is longer than (around) 20 characters, then Ledger will automatically start scrolling the line. - -We are currently using 256 bytes long buffer to keep the value string. If the value read from the json transaction is longer than that, then we split it into 256 chunks and display and scroll each chunk individually. - -Here's an example: - -Screen 3. -``` -LINE1: [left icon] SECP256K1 - 03/08 [right icon] -LINE2: fee - 01/02 -LINE3: {"amount":[{"denom":"photon", "a [first 256 bytes of the value] -``` - -Screen 3a. -``` -LINE1: [left icon] SECP256K1 - 03/08 [right icon] -LINE2: fee - 02/02 -LINE3: mount":5}], "gas":10000} [the next 256 bytes of the value] -``` - -We'll use term: 'Value Chunk Preview' to describe this mode. - -#### Interface: -##### Left click -Moves to the previous screen, unless the current screen is #1, in which case it switches back to the 'Received Transaction' page. -##### Right click -Moves to the next screen, unless the current screen is the last one, in which case it switches back to the 'Received Transaction' page. -##### Double click -Switches back to the 'View Transaction' page -##### Left hold -Same as Left click, unless we are in the 'Value Chunk Preview', in which case we step out from the 'Value Chunk Preview' and skip to the previous screen. -##### Right hold -Same as Right click, unless we are in the 'Value Chunk Preview', in which case we step out from the 'Value Chunk Preview' and skip to the next screen. diff --git a/docs/img/clion_debugging.png b/docs/img/clion_debugging.png new file mode 100644 index 00000000..f26ff759 Binary files /dev/null and b/docs/img/clion_debugging.png differ diff --git a/docs/img/cosmos_app1.png b/docs/img/cosmos_app1.png new file mode 100644 index 00000000..d971cef2 Binary files /dev/null and b/docs/img/cosmos_app1.png differ diff --git a/docs/img/cosmos_app2.png b/docs/img/cosmos_app2.png new file mode 100644 index 00000000..10c10ea9 Binary files /dev/null and b/docs/img/cosmos_app2.png differ diff --git a/docs/img/cosmos_app3.png b/docs/img/cosmos_app3.png new file mode 100644 index 00000000..4011eeac Binary files /dev/null and b/docs/img/cosmos_app3.png differ diff --git a/docs/img/tendermint_app.png b/docs/img/tendermint_app.png new file mode 100644 index 00000000..a21a37b0 Binary files /dev/null and b/docs/img/tendermint_app.png differ diff --git a/fuzzing/Makefile b/fuzzing/Makefile new file mode 100644 index 00000000..d4289adb --- /dev/null +++ b/fuzzing/Makefile @@ -0,0 +1,34 @@ +#******************************************************************************* +#* (c) 2019 Zondax GmbH +#* +#* Licensed under the Apache License, Version 2.0 (the "License"); +#* you may not use this file except in compliance with the License. +#* You may obtain a copy of the License at +#* +#* http://www.apache.org/licenses/LICENSE-2.0 +#* +#* Unless required by applicable law or agreed to in writing, software +#* distributed under the License is distributed on an "AS IS" BASIS, +#* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +#* See the License for the specific language governing permissions and +#* limitations under the License. +#******************************************************************************** + +.PHONY: all deps build clean load delete + +MAKEFILE_DIR := $(dir $(abspath $(lastword $(MAKEFILE_LIST)))) + +build: + ${MAKEFILE_DIR}/scripts/build.sh + +run: + ${MAKEFILE_DIR}/scripts/run.sh + +run_slaves: + ${MAKEFILE_DIR}/scripts/run_slaves.sh + +plot: + ${MAKEFILE_DIR}/scripts/plot.sh + +login: + ${MAKEFILE_DIR}/scripts/login.sh diff --git a/fuzzing/fuzzing.md b/fuzzing/fuzzing.md new file mode 100644 index 00000000..7a3782b5 --- /dev/null +++ b/fuzzing/fuzzing.md @@ -0,0 +1,24 @@ +# Fuzzing notes + +# Dependencies + +**Docker CE** + +Please install docker CE. The instructions can be found here: https://docs.docker.com/install/ + +#Run Fuzzers + +There are two options, you install `afl` in your host or you use a docker container. + +If you want to use the docker container. In the `fuzzing` directory: + + - Use `make login` to get a bash session in a preinstalled container. + - Run `make build` to build an instrumented stub + - Run `make run` to start fuzzing + +If you want to run multiple fuzzers: + + - Get another session in the same container: `make login` + - run `make run_slaves` to start 4 more parallel fuzzers + +You may want to configure docker to use more CPUs/cores diff --git a/fuzzing/fuzzingMain.cpp b/fuzzing/fuzzingMain.cpp new file mode 100644 index 00000000..fb0eba60 --- /dev/null +++ b/fuzzing/fuzzingMain.cpp @@ -0,0 +1,60 @@ +/******************************************************************************* +* (c) 2019 Zondax GmbH +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +********************************************************************************/ +#include +#include +#include +#include +#include "../tests/util/common.h" + +#ifndef __AFL_LOOP +#define __AFL_LOOP(x) (0) +#endif + +/// +/// This file is just a fuzzing stub used by afl +/// + +void parse(std::istream &istream) { + parser_context_t ctx; + parser_error_t err; + + std::string input; + istream >> input; + + err = parser_parse(&ctx, (const uint8_t *) input.c_str(), input.length()); + if (err != parser_ok) + return; + + auto output = dumpUI(&ctx, 40, 40); + + for (const auto &line : output) { + std::cout << line << std::endl; + } +} + +int main(int argc, char **argv) { + if (argc > 1) { + std::ifstream fin; + fin.open(argv[1]); + parse(fin); + } else { + while (__AFL_LOOP(1000)) { + parse(std::cin); + } + } + + return 0; +} diff --git a/fuzzing/inputs/input1.txt b/fuzzing/inputs/input1.txt new file mode 100644 index 00000000..3c8a8da4 --- /dev/null +++ b/fuzzing/inputs/input1.txt @@ -0,0 +1 @@ +{"account_number":"0","chain_id":"test-chain-1","fee":{"amount":[{"amount":"5","denom":"photon"}],"gas":"10000"},"memo":"testmemo","msgs":[{"inputs":[{"address":"cosmosaccaddr1d9h8qat5e4ehc5","coins":[{"amount":"10","denom":"atom"}]}],"outputs":[{"address":"cosmosaccaddr1da6hgur4wse3jx32","coins":[{"amount":"10","denom":"atom"}]}]},{"inputs":[{"address":"test2","coins":[{"amount":"20","denom":"bitcoin"}]}],"outputs":[{"address":"test3","coins":[{"amount":"50","denom":"ripple"}]}]}],"sequence":"1"} diff --git a/fuzzing/inputs/input2.txt b/fuzzing/inputs/input2.txt new file mode 100644 index 00000000..3a79d59e --- /dev/null +++ b/fuzzing/inputs/input2.txt @@ -0,0 +1 @@ +{"account_number":"6571","chain_id":"cosmoshub-2","fee":{"amount":[{"amount":"5000","denom":"uatom"}],"gas":"200000"},"memo":"Delegated with Ledger from union.market","msgs":[{"type":"cosmos-sdk/MsgDelegate","value":{"amount":{"amount":"1000000","denom":"uatom"},"delegator_address":"cosmos102hty0jv2s29lyc4u0tv97z9v298e24t3vwtpl","validator_address":"cosmosvaloper1grgelyng2v6v3t8z87wu3sxgt9m5s03xfytvz7"}}],"sequence":"1"} diff --git a/fuzzing/inputs/input3.txt b/fuzzing/inputs/input3.txt new file mode 100644 index 00000000..c2292a15 --- /dev/null +++ b/fuzzing/inputs/input3.txt @@ -0,0 +1 @@ +{"account_number":"108","chain_id":"cosmoshub-2","fee":{"amount":[{"amount":"600","denom":"uatom"}],"gas":"200000"},"memo":"","msgs":[{"type":"cosmos-sdk/MsgWithdrawDelegationReward","value":{"delegator_address":"cosmos14lultfckehtszvzw4ehu0apvsr77afvyhgqhwh","validator_address":"cosmosvaloper1qwl879nx9t6kef4supyazayf7vjhennyh568ys"}},{"type":"cosmos-sdk/MsgWithdrawDelegationReward","value":{"delegator_address":"cosmos14lultfckehtszvzw4ehu0apvsr77afvyhgqhwh","validator_address":"cosmosvaloper1x88j7vp2xnw3zec8ur3g4waxycyz7m0mahdv3p"}},{"type":"cosmos-sdk/MsgWithdrawDelegationReward","value":{"delegator_address":"cosmos14lultfckehtszvzw4ehu0apvsr77afvyhgqhwh","validator_address":"cosmosvaloper1grgelyng2v6v3t8z87wu3sxgt9m5s03xfytvz7"}},{"type":"cosmos-sdk/MsgWithdrawDelegationReward","value":{"delegator_address":"cosmos14lultfckehtszvzw4ehu0apvsr77afvyhgqhwh","validator_address":"cosmosvaloper1ttfytaf43nkytzp8hkfjfgjc693ky4t3y2n2ku"}},{"type":"cosmos-sdk/MsgWithdrawDelegationReward","value":{"delegator_address":"cosmos14lultfckehtszvzw4ehu0apvsr77afvyhgqhwh","validator_address":"cosmosvaloper1wdrypwex63geqswmcy5qynv4w3z3dyef2qmyna"}},{"type":"cosmos-sdk/MsgWithdrawDelegationReward","value":{"delegator_address":"cosmos14lultfckehtszvzw4ehu0apvsr77afvyhgqhwh","validator_address":"cosmosvaloper102ruvpv2srmunfffxavttxnhezln6fnc54at8c"}},{"type":"cosmos-sdk/MsgWithdrawDelegationReward","value":{"delegator_address":"cosmos14lultfckehtszvzw4ehu0apvsr77afvyhgqhwh","validator_address":"cosmosvaloper10e4vsut6suau8tk9m6dnrm0slgd6npe3jx5xpv"}},{"type":"cosmos-sdk/MsgWithdrawDelegationReward","value":{"delegator_address":"cosmos14lultfckehtszvzw4ehu0apvsr77afvyhgqhwh","validator_address":"cosmosvaloper1sxx9mszve0gaedz5ld7qdkjkfv8z992ax69k08"}},{"type":"cosmos-sdk/MsgWithdrawDelegationReward","value":{"delegator_address":"cosmos14lultfckehtszvzw4ehu0apvsr77afvyhgqhwh","validator_address":"cosmosvaloper1ssm0d433seakyak8kcf93yefhknjleeds4y3em"}},{"type":"cosmos-sdk/MsgWithdrawDelegationReward","value":{"delegator_address":"cosmos14lultfckehtszvzw4ehu0apvsr77afvyhgqhwh","validator_address":"cosmosvaloper13sduv92y3xdhy3rpmhakrc3v7t37e7ps9l0kpv"}},{"type":"cosmos-sdk/MsgWithdrawDelegationReward","value":{"delegator_address":"cosmos14lultfckehtszvzw4ehu0apvsr77afvyhgqhwh","validator_address":"cosmosvaloper15urq2dtp9qce4fyc85m6upwm9xul3049e02707"}},{"type":"cosmos-sdk/MsgWithdrawDelegationReward","value":{"delegator_address":"cosmos14lultfckehtszvzw4ehu0apvsr77afvyhgqhwh","validator_address":"cosmosvaloper14kn0kk33szpwus9nh8n87fjel8djx0y070ymmj"}},{"type":"cosmos-sdk/MsgWithdrawDelegationReward","value":{"delegator_address":"cosmos14lultfckehtszvzw4ehu0apvsr77afvyhgqhwh","validator_address":"cosmosvaloper14lultfckehtszvzw4ehu0apvsr77afvyju5zzy"}},{"type":"cosmos-sdk/MsgWithdrawDelegationReward","value":{"delegator_address":"cosmos14lultfckehtszvzw4ehu0apvsr77afvyhgqhwh","validator_address":"cosmosvaloper1k9a0cs97vul8w2vwknlfmpez6prv8klv03lv3d"}},{"type":"cosmos-sdk/MsgWithdrawDelegationReward","value":{"delegator_address":"cosmos14lultfckehtszvzw4ehu0apvsr77afvyhgqhwh","validator_address":"cosmosvaloper1kj0h4kn4z5xvedu2nd9c4a9a559wvpuvu0h6qn"}},{"type":"cosmos-sdk/MsgWithdrawDelegationReward","value":{"delegator_address":"cosmos14lultfckehtszvzw4ehu0apvsr77afvyhgqhwh","validator_address":"cosmosvaloper1hjct6q7npsspsg3dgvzk3sdf89spmlpfdn6m9d"}}],"sequence":"106"} diff --git a/fuzzing/inputs/input4.txt b/fuzzing/inputs/input4.txt new file mode 100644 index 00000000..02b836d4 --- /dev/null +++ b/fuzzing/inputs/input4.txt @@ -0,0 +1 @@ +{"1":[[[[[[[[[[[[{"2":"4"}]]]]]]]]]]]]} diff --git a/fuzzing/inputs/input5.txt b/fuzzing/inputs/input5.txt new file mode 100644 index 00000000..20b331d9 --- /dev/null +++ b/fuzzing/inputs/input5.txt @@ -0,0 +1 @@ +{"account_number":"6571","chain_id":"cosmoshub-2","fee":{"amount":[{"amount":"5000","denom":"uatom"}],"gas":"200000"},"memo":"Delegated with Ledger from union.market","msgs":[{"type":"cosmos-sdk/MsgDelegate","value":{"amount":{"amount":"1000000","denom":"uatom"},"delegator_address":"cosmos102hty0jv2s29lyc4u0tv97z9v298e24t3vwtpl","validator_address":"cosmosvaloper1grgelyng2v6v3t8z87wu3sxgt9m5s03xfytvz7"}}],"sequence":"0"} diff --git a/fuzzing/inputs/input6.txt b/fuzzing/inputs/input6.txt new file mode 100644 index 00000000..300caa88 --- /dev/null +++ b/fuzzing/inputs/input6.txt @@ -0,0 +1 @@ +{"account_number":"2","chain_id":"local-testnet","fee":{"amount":[],"gas":"500000"},"memo":"abc","msgs":[{"description":"test","initial_deposit":[{"amount":"1","denom":"stake"}],"proposal_type":"Text","proposer":"cosmos101234567890abcdefghijklmnopqrstuvwxyz0","title":"test"}],"sequence":"0"} diff --git a/fuzzing/inputs/input7.txt b/fuzzing/inputs/input7.txt new file mode 100644 index 00000000..3a79d59e --- /dev/null +++ b/fuzzing/inputs/input7.txt @@ -0,0 +1 @@ +{"account_number":"6571","chain_id":"cosmoshub-2","fee":{"amount":[{"amount":"5000","denom":"uatom"}],"gas":"200000"},"memo":"Delegated with Ledger from union.market","msgs":[{"type":"cosmos-sdk/MsgDelegate","value":{"amount":{"amount":"1000000","denom":"uatom"},"delegator_address":"cosmos102hty0jv2s29lyc4u0tv97z9v298e24t3vwtpl","validator_address":"cosmosvaloper1grgelyng2v6v3t8z87wu3sxgt9m5s03xfytvz7"}}],"sequence":"1"} diff --git a/fuzzing/inputs/input8.txt b/fuzzing/inputs/input8.txt new file mode 100644 index 00000000..3c8a8da4 --- /dev/null +++ b/fuzzing/inputs/input8.txt @@ -0,0 +1 @@ +{"account_number":"0","chain_id":"test-chain-1","fee":{"amount":[{"amount":"5","denom":"photon"}],"gas":"10000"},"memo":"testmemo","msgs":[{"inputs":[{"address":"cosmosaccaddr1d9h8qat5e4ehc5","coins":[{"amount":"10","denom":"atom"}]}],"outputs":[{"address":"cosmosaccaddr1da6hgur4wse3jx32","coins":[{"amount":"10","denom":"atom"}]}]},{"inputs":[{"address":"test2","coins":[{"amount":"20","denom":"bitcoin"}]}],"outputs":[{"address":"test3","coins":[{"amount":"50","denom":"ripple"}]}]}],"sequence":"1"} diff --git a/fuzzing/scripts/build.sh b/fuzzing/scripts/build.sh new file mode 100755 index 00000000..20616fff --- /dev/null +++ b/fuzzing/scripts/build.sh @@ -0,0 +1,28 @@ +#!/usr/bin/env bash +#******************************************************************************* +#* (c) 2019 Zondax GmbH +#* +#* Licensed under the Apache License, Version 2.0 (the "License"); +#* you may not use this file except in compliance with the License. +#* You may obtain a copy of the License at +#* +#* http://www.apache.org/licenses/LICENSE-2.0 +#* +#* Unless required by applicable law or agreed to in writing, software +#* distributed under the License is distributed on an "AS IS" BASIS, +#* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +#* See the License for the specific language governing permissions and +#* limitations under the License. +#********************************************************************************/ + +SCRIPTDIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null 2>&1 && pwd )" +BUILDDIR=$SCRIPTDIR/../../cmake-build-fuzz + +# Compile fuzzing stub +rm -rf "$BUILDDIR" +mkdir -p "$BUILDDIR/syncdir" +cd "$BUILDDIR" || exit + +cmake -DCMAKE_CXX_COMPILER=afl-clang-fast++ -DCMAKE_C_COMPILER=afl-clang-fast .. +make clean +make fuzzing_stub diff --git a/fuzzing/scripts/login.sh b/fuzzing/scripts/login.sh new file mode 100755 index 00000000..82760fa4 --- /dev/null +++ b/fuzzing/scripts/login.sh @@ -0,0 +1,30 @@ +#!/usr/bin/env bash +#******************************************************************************* +#* (c) 2019 Zondax GmbH +#* +#* Licensed under the Apache License, Version 2.0 (the "License"); +#* you may not use this file except in compliance with the License. +#* You may obtain a copy of the License at +#* +#* http://www.apache.org/licenses/LICENSE-2.0 +#* +#* Unless required by applicable law or agreed to in writing, software +#* distributed under the License is distributed on an "AS IS" BASIS, +#* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +#* See the License for the specific language governing permissions and +#* limitations under the License. +#********************************************************************************/ +CONTAINER_NAME='zx-fuzzer-container' +SCRIPTDIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null 2>&1 && pwd )" +DOCKER_IMAGE=zondax/docker-fuzzing:latest +CID=$(docker ps -q -f status=running -f name=^/${CONTAINER_NAME}$) + +echo "$SCRIPTDIR" + +if [ ! "${CID}" ]; then + docker run -it --rm --name ${CONTAINER_NAME} \ + -u "$(id -u)" -v ${SCRIPTDIR}/../../:/project \ + ${DOCKER_IMAGE} +else + docker exec -it ${CONTAINER_NAME} /bin/bash +fi diff --git a/fuzzing/scripts/plot.sh b/fuzzing/scripts/plot.sh new file mode 100755 index 00000000..f107b9fc --- /dev/null +++ b/fuzzing/scripts/plot.sh @@ -0,0 +1,21 @@ +#!/usr/bin/env bash +#******************************************************************************* +#* (c) 2019 Zondax GmbH +#* +#* Licensed under the Apache License, Version 2.0 (the "License"); +#* you may not use this file except in compliance with the License. +#* You may obtain a copy of the License at +#* +#* http://www.apache.org/licenses/LICENSE-2.0 +#* +#* Unless required by applicable law or agreed to in writing, software +#* distributed under the License is distributed on an "AS IS" BASIS, +#* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +#* See the License for the specific language governing permissions and +#* limitations under the License. +#********************************************************************************/ + +SCRIPTDIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null 2>&1 && pwd )" +BUILDDIR=$SCRIPTDIR/../../cmake-build-fuzz + +afl-plot "$BUILDDIR/syncdir/master" "$BUILDDIR/plots" diff --git a/fuzzing/scripts/run.sh b/fuzzing/scripts/run.sh new file mode 100755 index 00000000..3fabbf52 --- /dev/null +++ b/fuzzing/scripts/run.sh @@ -0,0 +1,9 @@ +#!/usr/bin/env bash + +SCRIPTDIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null 2>&1 && pwd )" +BUILDDIR=$SCRIPTDIR/../../cmake-build-fuzz +INPUTS=$SCRIPTDIR/../inputs + +echo "$BUILDDIR" +AFL_I_DONT_CARE_ABOUT_MISSING_CRASHES=1 afl-fuzz -t 50 -i "$INPUTS" -o "$BUILDDIR/syncdir" -M main -- "$BUILDDIR/fuzzing_stub" + diff --git a/fuzzing/scripts/run_slaves.sh b/fuzzing/scripts/run_slaves.sh new file mode 100755 index 00000000..ab8a2e7f --- /dev/null +++ b/fuzzing/scripts/run_slaves.sh @@ -0,0 +1,10 @@ +#!/usr/bin/env bash + +SCRIPTDIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null 2>&1 && pwd )" +BUILDDIR=$SCRIPTDIR/../../cmake-build-fuzz +INPUTS=$SCRIPTDIR/../inputs + +AFL_I_DONT_CARE_ABOUT_MISSING_CRASHES=1 afl-fuzz -t 50 -i "$INPUTS" -o "$BUILDDIR/syncdir" -S fuzzer2 -- "$BUILDDIR/fuzzing_stub" & +AFL_I_DONT_CARE_ABOUT_MISSING_CRASHES=1 afl-fuzz -t 50 -i "$INPUTS" -o "$BUILDDIR/syncdir" -S fuzzer3 -- "$BUILDDIR/fuzzing_stub" & +AFL_I_DONT_CARE_ABOUT_MISSING_CRASHES=1 afl-fuzz -t 50 -i "$INPUTS" -o "$BUILDDIR/syncdir" -S fuzzer1 -- "$BUILDDIR/fuzzing_stub" & +AFL_I_DONT_CARE_ABOUT_MISSING_CRASHES=1 afl-fuzz -t 50 -i "$INPUTS" -o "$BUILDDIR/syncdir" -S fuzzer4 -- "$BUILDDIR/fuzzing_stu"b & diff --git a/pkgdemo.sh b/pkgdemo.sh deleted file mode 100755 index c061c255..00000000 --- a/pkgdemo.sh +++ /dev/null @@ -1,38 +0,0 @@ -#!/usr/bin/env bash -#******************************************************************************* -#* (c) 2018 ZondaX GmbH -#* -#* Licensed under the Apache License, Version 2.0 (the "License"); -#* you may not use this file except in compliance with the License. -#* You may obtain a copy of the License at -#* -#* http://www.apache.org/licenses/LICENSE-2.0 -#* -#* Unless required by applicable law or agreed to in writing, software -#* distributed under the License is distributed on an "AS IS" BASIS, -#* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -#* See the License for the specific language governing permissions and -#* limitations under the License. -#******************************************************************************** -SCRIPT_DIR=$(cd $(dirname $0) && pwd) - -# Copy hex file -cp ${SCRIPT_DIR}/bin/app.hex ${SCRIPT_DIR}/pkgdemo/app.hex - -APPNAME=$1 -APPVERSION=$2 -ICONNAME=$3 - -TARGET_ID=0x31100003 -DATASIZE=$(cat ${SCRIPT_DIR}/debug/app.map |grep _nvram_data_size | tr -s ' ' | cut -f2 -d' ') -ICONHEX=$(python ${BOLOS_SDK}/icon.py ${ICONNAME} hexbitmaponly) - -cat >${SCRIPT_DIR}/pkgdemo/loaddemo.sh < -#include -#include - -#include -#include - -#include "lib/transaction.h" -#include "lib/tx_display.h" -#include "view.h" -#include "crypto.h" - -#ifdef TESTING_ENABLED -// Generate using always the same private data -// to allow for reproducible results -const uint8_t privateKeyDataTest[] = { - 0x75, 0x56, 0x0e, 0x4d, 0xde, 0xa0, 0x63, 0x05, - 0xc3, 0x6e, 0x2e, 0xb5, 0xf7, 0x2a, 0xca, 0x71, - 0x2d, 0x13, 0x4c, 0xc2, 0xa0, 0x59, 0xbf, 0xe8, - 0x7e, 0x9b, 0x5d, 0x55, 0xbf, 0x81, 0x3b, 0xd4 -}; -#endif - -unsigned char G_io_seproxyhal_spi_buffer[IO_SEPROXYHAL_BUFFER_SIZE_B]; - -unsigned char io_event(unsigned char channel) { - switch (G_io_seproxyhal_spi_buffer[0]) { - case SEPROXYHAL_TAG_FINGER_EVENT: // - UX_FINGER_EVENT(G_io_seproxyhal_spi_buffer); - break; - - case SEPROXYHAL_TAG_BUTTON_PUSH_EVENT: // for Nano S - UX_BUTTON_PUSH_EVENT(G_io_seproxyhal_spi_buffer); - break; - - case SEPROXYHAL_TAG_DISPLAY_PROCESSED_EVENT: - if (!UX_DISPLAYED()) - UX_DISPLAYED_EVENT(); - break; - - case SEPROXYHAL_TAG_TICKER_EVENT: { // - UX_TICKER_EVENT(G_io_seproxyhal_spi_buffer, { - if (UX_ALLOWED) { - UX_REDISPLAY(); - } - }); - break; - } - - // unknown events are acknowledged - default: - UX_DEFAULT_EVENT(); - break; - } - if (!io_seproxyhal_spi_is_status_sent()) { - io_seproxyhal_general_status(); - } - return 1; // DO NOT reset the current APDU transport -} - -unsigned short io_exchange_al(unsigned char channel, unsigned short tx_len) { - switch (channel & ~(IO_FLAGS)) { - case CHANNEL_KEYBOARD: - break; - - // multiplexed io exchange over a SPI channel and TLV encapsulated protocol - case CHANNEL_SPI: - if (tx_len) { - io_seproxyhal_spi_send(G_io_apdu_buffer, tx_len); - - if (channel & IO_RESET_AFTER_REPLIED) { - reset(); - } - return 0; // nothing received from the master so far (it's a tx - // transaction) - } else { - return io_seproxyhal_spi_recv(G_io_apdu_buffer, sizeof(G_io_apdu_buffer), 0); - } - - default: - THROW(INVALID_PARAMETER); - } - return 0; -} - -bool extractBip32(uint8_t *depth, uint32_t path[10], uint32_t rx, uint32_t offset) { - if (rx < offset + 1) { - return 0; - } - - *depth = G_io_apdu_buffer[offset]; - const uint16_t req_offset = 4 * *depth + 1 + offset; - - if (rx < req_offset || *depth > 10) { - return 0; - } - - memcpy(path, G_io_apdu_buffer + offset + 1, *depth * 4); - return 1; -} - -bool validateCosmosPath(uint8_t depth, uint32_t path[10]) { - // Only paths in the form 44'/118'/{account}'/0/{index} are supported - if (bip32_depth != 5) { - return 0; - } - if (path[0] != 0x8000002c || path[1] != 0x80000076 || path[3] != 0) { - return 0; - } - return 1; -} - -bool extractHRP(uint8_t *len, char *hrp, uint32_t rx, uint32_t offset) { - if (rx < offset + 1) { - THROW(APDU_CODE_DATA_INVALID); - } - - *len = G_io_apdu_buffer[offset]; - - if (*len == 0 || *len > MAX_BECH32_HRP_LEN) { - THROW(APDU_CODE_DATA_INVALID); - } - - memcpy(hrp, G_io_apdu_buffer + offset + 1, *len); - hrp[*len] = 0; // zero terminate - return 1; -} - -bool process_chunk(volatile uint32_t *tx, uint32_t rx, bool getBip32) { - int packageIndex = G_io_apdu_buffer[OFFSET_PCK_INDEX]; - int packageCount = G_io_apdu_buffer[OFFSET_PCK_COUNT]; - - uint16_t offset = OFFSET_DATA; - if (rx < offset) { - THROW(APDU_CODE_DATA_INVALID); - } - - if (packageIndex == 1) { - transaction_initialize(); - transaction_reset(); - if (getBip32) { - if (!extractBip32(&bip32_depth, bip32_path, rx, OFFSET_DATA)) { - THROW(APDU_CODE_DATA_INVALID); - } - return packageIndex == packageCount; - } - } - - if (transaction_append(&(G_io_apdu_buffer[offset]), rx - offset) != rx - offset) { - THROW(APDU_CODE_OUTPUT_BUFFER_TOO_SMALL); - } - - return packageIndex == packageCount; -} - -//region View Transaction Handlers - -int16_t tx_getData(char *title, int16_t max_title_length, - char *key, int16_t max_key_length, - char *value, int16_t max_value_length, - int16_t page_index, - int16_t chunk_index, - int16_t *page_count_out, - int16_t *chunk_count_out) { - *page_count_out = tx_display_num_pages(); - *chunk_count_out = 0; - - if (*page_count_out > 0) { - switch (current_sigtype) { - case SECP256K1: - snprintf(title, - max_title_length, - "SECP256K1 %02d/%02d", - page_index + 1, - *page_count_out); - break; - default: - snprintf(title, max_title_length, "INVALID!"); - break; - } - - INIT_QUERY(key, max_key_length, value, max_value_length, chunk_index) - *chunk_count_out = tx_display_get_item(page_index); - - tx_display_make_friendly(); - } - - return *chunk_count_out; -} - -void tx_accept_sign() { - // Generate keys - cx_ecfp_public_key_t publicKey; - cx_ecfp_private_key_t privateKey; - uint8_t privateKeyData[32]; - - unsigned int length = 0; - int result = 0; - switch (current_sigtype) { - case SECP256K1: - os_perso_derive_node_bip32(CX_CURVE_256K1, - bip32_path, bip32_depth, - privateKeyData, NULL); - keys_secp256k1(&publicKey, &privateKey, privateKeyData); - memset(privateKeyData, 0, 32); - result = sign_secp256k1(transaction_get_buffer(), - transaction_get_buffer_length(), - G_io_apdu_buffer, - IO_APDU_BUFFER_SIZE, - &length, - &privateKey); - break; - default: - THROW(APDU_CODE_INS_NOT_SUPPORTED); - break; - } - if (result == 1) { - set_code(G_io_apdu_buffer, length, APDU_CODE_OK); - io_exchange(CHANNEL_APDU | IO_RETURN_AFTER_TX, length + 2); - view_idle(0); - } else { - set_code(G_io_apdu_buffer, length, APDU_CODE_SIGN_VERIFY_ERROR); - io_exchange(CHANNEL_APDU | IO_RETURN_AFTER_TX, length + 2); - view_idle(0); - } -} - -void tx_reject() { - set_code(G_io_apdu_buffer, 0, APDU_CODE_COMMAND_NOT_ALLOWED); - io_exchange(CHANNEL_APDU | IO_RETURN_AFTER_TX, 2); - view_idle(0); -} - -//endregion - -//region View Address Handlers - -int16_t addr_getData(char *title, int16_t max_title_length, - char *key, int16_t max_key_length, - char *value, int16_t max_value_length, - int16_t page_index, - int16_t chunk_index, - int16_t *page_count_out, - int16_t *chunk_count_out) { - - if (page_count_out) - *page_count_out = 1; - if (chunk_count_out) - *chunk_count_out = 1; - - snprintf(title, max_title_length, "Account %d", bip32_path[2] & 0x7FFFFFF); - snprintf(key, max_key_length, "index %d", page_index); - bip32_path[bip32_depth - 1] = page_index; - - // get address from the current bip32_path - get_bech32_addr(value); - return 0; -} - -void addr_accept() { -#if defined(TARGET_NANOS) - print_key("Returning"); - print_value("Address..."); - view_status(); - UX_WAIT(); -#endif - // Send pubkey - uint8_t *pk = G_io_apdu_buffer; - get_pk_compressed(pk); - int pos = PK_COMPRESSED_LEN; - - // Convert pubkey to bech32 address - char *bech32_out = (char *) (G_io_apdu_buffer + pos); - get_bech32_addr(bech32_out); - pos += strlen(bech32_out); - - set_code(G_io_apdu_buffer, pos, APDU_CODE_OK); - io_exchange(CHANNEL_APDU | IO_RETURN_AFTER_TX, pos + 2); - view_idle(0); -} - -void addr_reject() { - set_code(G_io_apdu_buffer, 0, APDU_CODE_COMMAND_NOT_ALLOWED); - io_exchange(CHANNEL_APDU | IO_RETURN_AFTER_TX, 2); - view_idle(0); -} - -//endregion - -void handleApdu(volatile uint32_t *flags, volatile uint32_t *tx, uint32_t rx) { - uint16_t sw = 0; - - BEGIN_TRY - { - TRY - { - if (G_io_apdu_buffer[OFFSET_CLA] != CLA) { - THROW(APDU_CODE_CLA_NOT_SUPPORTED); - } - - if (rx < 5) { - THROW(APDU_CODE_WRONG_LENGTH); - } - - switch (G_io_apdu_buffer[OFFSET_INS]) { - case INS_GET_VERSION: { -#ifdef TESTING_ENABLED - G_io_apdu_buffer[0] = 0xFF; -#else - G_io_apdu_buffer[0] = 0; -#endif - G_io_apdu_buffer[1] = LEDGER_MAJOR_VERSION; - G_io_apdu_buffer[2] = LEDGER_MINOR_VERSION; - G_io_apdu_buffer[3] = LEDGER_PATCH_VERSION; - G_io_apdu_buffer[4] = !IS_UX_ALLOWED; - - *tx += 5; - THROW(APDU_CODE_OK); - break; - } - - // INS_PUBLIC_KEY_SECP256K1 will be deprecated in the near future - case INS_PUBLIC_KEY_SECP256K1: { - if (!extractBip32(&bip32_depth, bip32_path, rx, OFFSET_DATA)) { - THROW(APDU_CODE_DATA_INVALID); - } - - if (!validateCosmosPath(bip32_depth, bip32_path)) { - THROW(APDU_CODE_DATA_INVALID); - } - - cx_ecfp_public_key_t publicKey; - getPubKey(&publicKey); - - os_memmove(G_io_apdu_buffer, publicKey.W, 65); - *tx += 65; - - THROW(APDU_CODE_OK); - break; - } - - case INS_GET_ADDR_SECP256K1: { - // Parse arguments - if (!extractHRP(&bech32_hrp_len, bech32_hrp, rx, OFFSET_DATA)) { - THROW(APDU_CODE_DATA_INVALID); - } - - if (!extractBip32(&bip32_depth, bip32_path, rx, OFFSET_DATA + bech32_hrp_len + 1)) { - THROW(APDU_CODE_DATA_INVALID); - } - - if (!validateCosmosPath(bip32_depth, bip32_path)) { - THROW(APDU_CODE_DATA_INVALID); - } - - view_set_handlers(addr_getData, addr_accept, addr_reject); - view_addr_confirm(0); - - *flags |= IO_ASYNCH_REPLY; - break; - } - - case INS_SIGN_SECP256K1: { - current_sigtype = SECP256K1; - if (!process_chunk(tx, rx, true)) - THROW(APDU_CODE_OK); - - const char *error_msg = transaction_parse(); - if (error_msg != NULL) { - int error_msg_length = strlen(error_msg); - os_memmove(G_io_apdu_buffer, error_msg, error_msg_length); - *tx += (error_msg_length); - THROW(APDU_CODE_BAD_KEY_HANDLE); - } - - tx_display_index_root(); - - view_set_handlers(tx_getData, tx_accept_sign, tx_reject); - view_tx_show(0); - - *flags |= IO_ASYNCH_REPLY; - break; - } - -#ifdef TESTING_ENABLED - case INS_HASH_TEST: { - if (process_chunk(tx, rx, false)) { - uint8_t message_digest[CX_SHA256_SIZE]; - - cx_hash_sha256(transaction_get_buffer(), - transaction_get_buffer_length(), - message_digest, - CX_SHA256_SIZE); - - os_memmove(G_io_apdu_buffer, message_digest, CX_SHA256_SIZE); - *tx += 32; - } - THROW(APDU_CODE_OK); - } - break; - - case INS_PUBLIC_KEY_SECP256K1_TEST: { - // Generate key - cx_ecfp_public_key_t publicKey; - cx_ecfp_private_key_t privateKey; - keys_secp256k1(&publicKey, &privateKey, privateKeyDataTest ); - - os_memmove(G_io_apdu_buffer, publicKey.W, 65); - *tx += 65; - - THROW(APDU_CODE_OK); - } - break; - - case INS_SIGN_SECP256K1_TEST: { - if (process_chunk(tx, rx, false)) { - - unsigned int length = 0; - - // Generate keys - cx_ecfp_public_key_t publicKey; - cx_ecfp_private_key_t privateKey; - keys_secp256k1(&publicKey, &privateKey, privateKeyDataTest ); - - // Skip UI and validation - sign_secp256k1( - transaction_get_buffer(), - transaction_get_buffer_length(), - G_io_apdu_buffer, - IO_APDU_BUFFER_SIZE, - &length, - &privateKey); - - *tx += length; - } - THROW(APDU_CODE_OK); - } - break; -#endif - - default: - THROW(APDU_CODE_INS_NOT_SUPPORTED); - } - } - CATCH(EXCEPTION_IO_RESET) - { - THROW(EXCEPTION_IO_RESET); - } - CATCH_OTHER(e) - { - switch (e & 0xF000) { - case 0x6000: - case APDU_CODE_OK: - sw = e; - break; - default: - sw = 0x6800 | (e & 0x7FF); - break; - } - G_io_apdu_buffer[*tx] = sw >> 8; - G_io_apdu_buffer[*tx + 1] = sw; - *tx += 2; - } - FINALLY - { - } - } - END_TRY; -} - -void handle_generic_apdu(volatile uint32_t *flags, volatile uint32_t *tx, uint32_t rx) { - if (rx > 4 && os_memcmp(G_io_apdu_buffer, "\xE0\x01\x00\x00", 4) == 0) { - // Respond to get device info command - uint8_t *p = G_io_apdu_buffer; - // Target ID 4 bytes - p[0]=(TARGET_ID >> 24) & 0xFF; - p[1]=(TARGET_ID >> 16) & 0xFF; - p[2]=(TARGET_ID >> 8) & 0xFF; - p[3]=(TARGET_ID >> 0) & 0xFF; - p += 4; - // SE Version [length][non-terminated string] - *p = os_version(p + 1, 64); - p = p + 1 + *p; - // Flags [length][flags] - *p = 0; - p++; - // MCU Version [length][non-terminated string] - *p = os_seph_version(p + 1, 64); - p = p + 1 + *p; - - *tx = p - G_io_apdu_buffer; - THROW(APDU_CODE_OK); - } -} - -void app_init() { - io_seproxyhal_init(); - USB_power(0); - USB_power(1); - view_idle(0); -} - -#pragma clang diagnostic push -#pragma clang diagnostic ignored "-Wmissing-noreturn" - -void app_main() { - volatile uint32_t rx = 0, tx = 0, flags = 0; - - for (;;) { - volatile uint16_t sw = 0; - - BEGIN_TRY; - { - TRY; - { - rx = tx; - tx = 0; - rx = io_exchange(CHANNEL_APDU | flags, rx); - flags = 0; - - if (rx == 0) - THROW(APDU_CODE_EMPTY_BUFFER); - - handle_generic_apdu(&flags, &tx, rx); - - handleApdu(&flags, &tx, rx); - } - CATCH_OTHER(e); - { - switch (e & 0xF000) { - case 0x6000: - case 0x9000: - sw = e; - break; - default: - sw = 0x6800 | (e & 0x7FF); - break; - } - G_io_apdu_buffer[tx] = sw >> 8; - G_io_apdu_buffer[tx + 1] = sw; - tx += 2; - } - FINALLY; - {} - } - END_TRY; - } -} - -#pragma clang diagnostic pop diff --git a/src/app_main.h b/src/app_main.h deleted file mode 100644 index 291bc5a9..00000000 --- a/src/app_main.h +++ /dev/null @@ -1,44 +0,0 @@ -/******************************************************************************* -* (c) 2016 Ledger -* (c) 2018 ZondaX GmbH -* -* Licensed under the Apache License, Version 2.0 (the "License"); -* you may not use this file except in compliance with the License. -* You may obtain a copy of the License at -* -* http://www.apache.org/licenses/LICENSE-2.0 -* -* Unless required by applicable law or agreed to in writing, software -* distributed under the License is distributed on an "AS IS" BASIS, -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -* See the License for the specific language governing permissions and -* limitations under the License. -********************************************************************************/ -#pragma once - -#include -#include "apdu_codes.h" - -#define CLA 0x55 - -#define OFFSET_CLA 0 -#define OFFSET_INS 1 //< Instruction offset -#define OFFSET_PCK_INDEX 2 //< Package index offset -#define OFFSET_PCK_COUNT 3 //< Package count offset -#define OFFSET_DATA 5 //< Data offset - -#define INS_GET_VERSION 0 -#define INS_PUBLIC_KEY_SECP256K1 1 // It will be deprecated in the near future -#define INS_SIGN_SECP256K1 2 -//#define INS_SHOW_ADDR_SECP256K1 3 DEPRECATED -#define INS_GET_ADDR_SECP256K1 4 - -#ifdef TESTING_ENABLED -#define INS_HASH_TEST 100 -#define INS_PUBLIC_KEY_SECP256K1_TEST 101 -#define INS_SIGN_SECP256K1_TEST 102 -#endif - -void app_init(); - -void app_main(); diff --git a/src/crypto.c b/src/crypto.c deleted file mode 100644 index 457b2136..00000000 --- a/src/crypto.c +++ /dev/null @@ -1,115 +0,0 @@ -/******************************************************************************* -* (c) 2016 Ledger -* (c) 2018 ZondaX GmbH -* -* Licensed under the Apache License, Version 2.0 (the "License"); -* you may not use this file except in compliance with the License. -* You may obtain a copy of the License at -* -* http://www.apache.org/licenses/LICENSE-2.0 -* -* Unless required by applicable law or agreed to in writing, software -* distributed under the License is distributed on an "AS IS" BASIS, -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -* See the License for the specific language governing permissions and -* limitations under the License. -********************************************************************************/ -#include "crypto.h" - -#include "apdu_codes.h" -#include "zxmacros.h" - -uint8_t bip32_depth; -uint32_t bip32_path[10]; -sigtype_t current_sigtype; - -uint8_t bech32_hrp_len; -char bech32_hrp[MAX_BECH32_HRP_LEN + 1]; - -void keys_secp256k1(cx_ecfp_public_key_t *publicKey, - cx_ecfp_private_key_t *privateKey, - const uint8_t privateKeyData[32]) { - cx_ecfp_init_private_key(CX_CURVE_256K1, privateKeyData, 32, privateKey); - cx_ecfp_init_public_key(CX_CURVE_256K1, NULL, 0, publicKey); - cx_ecfp_generate_pair(CX_CURVE_256K1, publicKey, privateKey, 1); -} - -int sign_secp256k1(const uint8_t *message, - unsigned int message_length, - uint8_t *signature, - unsigned int signature_capacity, - unsigned int *signature_length, - cx_ecfp_private_key_t *privateKey) { - uint8_t message_digest[CX_SHA256_SIZE]; - cx_hash_sha256(message, message_length, message_digest, CX_SHA256_SIZE); - - cx_ecfp_public_key_t publicKey; - cx_ecdsa_init_public_key(CX_CURVE_256K1, NULL, 0, &publicKey); - cx_ecfp_generate_pair(CX_CURVE_256K1, &publicKey, privateKey, 1); - - unsigned int info = 0; - *signature_length = cx_ecdsa_sign( - privateKey, - CX_RND_RFC6979 | CX_LAST, - CX_SHA256, - message_digest, - CX_SHA256_SIZE, - signature, - signature_capacity, - &info); - - os_memset(&privateKey, 0, sizeof(privateKey)); -#ifdef TESTING_ENABLED - return cx_ecdsa_verify( - &publicKey, - CX_LAST, - CX_SHA256, - message_digest, - CX_SHA256_SIZE, - signature, - *signature_length); -#else - return 1; -#endif -} - -void getPubKey(cx_ecfp_public_key_t *publicKey) { - cx_ecfp_private_key_t privateKey; - uint8_t privateKeyData[32]; - - // Generate keys - os_perso_derive_node_bip32(CX_CURVE_256K1, - bip32_path, - bip32_depth, - privateKeyData, NULL); - - keys_secp256k1(publicKey, &privateKey, privateKeyData); - memset(privateKeyData, 0, sizeof(privateKeyData)); - memset(&privateKey, 0, sizeof(privateKey)); -} - -void ripemd160_32(uint8_t *out, uint8_t *in) { - cx_ripemd160_t rip160; - cx_ripemd160_init(&rip160); - cx_hash(&rip160.header, CX_LAST, in, CX_SHA256_SIZE, out, CX_RIPEMD160_SIZE); -} - -void get_pk_compressed(uint8_t *pkc) { - cx_ecfp_public_key_t publicKey; - // Modify the last part of the path - getPubKey(&publicKey); - // "Compress" public key in place - publicKey.W[0] = publicKey.W[64] & 1 ? 0x03 : 0x02; - memcpy(pkc, publicKey.W, PK_COMPRESSED_LEN); -} - -void get_bech32_addr(char *bech32_addr) { - uint8_t tmp[PK_COMPRESSED_LEN]; - get_pk_compressed(tmp); - - uint8_t hashed_pk[CX_RIPEMD160_SIZE]; - cx_hash_sha256(tmp, PK_COMPRESSED_LEN, tmp, CX_SHA256_SIZE); - ripemd160_32(hashed_pk, tmp); - - bech32EncodeFromBytes(bech32_addr, bech32_hrp, hashed_pk, CX_RIPEMD160_SIZE); -} diff --git a/src/crypto.h b/src/crypto.h deleted file mode 100644 index 7188d087..00000000 --- a/src/crypto.h +++ /dev/null @@ -1,62 +0,0 @@ -/******************************************************************************* -* (c) 2016 Ledger -* (c) 2018 ZondaX GmbH -* -* Licensed under the Apache License, Version 2.0 (the "License"); -* you may not use this file except in compliance with the License. -* You may obtain a copy of the License at -* -* http://www.apache.org/licenses/LICENSE-2.0 -* -* Unless required by applicable law or agreed to in writing, software -* distributed under the License is distributed on an "AS IS" BASIS, -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -* See the License for the specific language governing permissions and -* limitations under the License. -********************************************************************************/ -#pragma once -#include -#include "os.h" -#include "cx.h" - -#define MAX_BECH32_HRP_LEN 83 -#define PK_COMPRESSED_LEN 33 - -#define BIP32_LEN_DEFAULT 0 -#define BIP32_0_DEFAULT (0x80000000 | 44) -#define BIP32_1_DEFAULT (0x80000000 | 118) -#define BIP32_2_DEFAULT (0x80000000 | 0) -#define BIP32_3_DEFAULT (0) -#define BIP32_4_DEFAULT (0) - -#define BIP32_ACCOUNT (bip32_path[2] & 0x7FFFFFF) -#define BIP32_INDEX (bip32_path[4] & 0x7FFFFFF) - -typedef enum { - SECP256K1 = 0, - ED25519 = 1 -} sigtype_t; - -extern uint8_t bip32_depth; -extern uint32_t bip32_path[10]; -extern sigtype_t current_sigtype; - -extern char bech32_hrp[MAX_BECH32_HRP_LEN + 1]; -extern uint8_t bech32_hrp_len; - -void keys_secp256k1(cx_ecfp_public_key_t *publicKey, - cx_ecfp_private_key_t *privateKey, - const uint8_t privateKeyData[32]); - -int sign_secp256k1(const uint8_t *message, - unsigned int message_length, - uint8_t *signature, - unsigned int signature_capacity, - unsigned int *signature_length, - cx_ecfp_private_key_t *privateKey); - -void getPubKey(cx_ecfp_public_key_t *publicKey); - -void get_pk_compressed(uint8_t *pkc); - -void get_bech32_addr(char *bech32_addr); diff --git a/src/lib/json_parser.c b/src/lib/json_parser.c deleted file mode 100644 index 5874d887..00000000 --- a/src/lib/json_parser.c +++ /dev/null @@ -1,253 +0,0 @@ -/******************************************************************************* -* (c) ZondaX GmbH -* -* Licensed under the Apache License, Version 2.0 (the "License"); -* you may not use this file except in compliance with the License. -* You may obtain a copy of the License at -* -* http://www.apache.org/licenses/LICENSE-2.0 -* -* Unless required by applicable law or agreed to in writing, software -* distributed under the License is distributed on an "AS IS" BASIS, -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -* See the License for the specific language governing permissions and -* limitations under the License. -********************************************************************************/ - -#include -#include "json_parser.h" - -#if defined(TARGET_NANOS) || defined(TARGET_NANOX) - #include "os.h" - #define EQUALS(_P, _Q, _LEN) (os_memcmp( PIC(_P), PIC(_Q), (_LEN))==0) -#else - #define EQUALS(_P, _Q, _LEN) (memcmp( (_P), (_Q), (_LEN))==0) -#endif - -void reset_parsed_json(parsed_json_t *parser_data) { - memset(parser_data, 0, sizeof(parsed_json_t)); -} - -const char *json_parse(parsed_json_t *parsed_json, const char *transaction) { - return json_parse_s(parsed_json, transaction, strlen(transaction)); - -} - -const char *json_parse_s(parsed_json_t *parsed_json, - const char *transaction, - uint16_t transaction_length) { - jsmn_parser parser; - jsmn_init(&parser); - - reset_parsed_json(parsed_json); - - int num_tokens = jsmn_parse( - &parser, - transaction, - transaction_length, - parsed_json->Tokens, - MAX_NUMBER_OF_TOKENS); - - switch (num_tokens) { - case JSMN_ERROR_NOMEM: - return "NOMEM: JSON string contains too many tokens"; - case JSMN_ERROR_INVAL: - return "Invalid character in JSON string"; - case JSMN_ERROR_PART: - return "JSON string is not complete"; - } - - parsed_json->NumberOfTokens = 0; - parsed_json->IsValid = 0; - - // Parsing error - if (num_tokens <= 0) { - return "Unknown parser error"; - } - - // We cannot support if number of tokens exceeds the limit - if (num_tokens > MAX_NUMBER_OF_TOKENS) { - return "TOK: JSON string contains too many tokens"; - } - - parsed_json->NumberOfTokens = num_tokens; - parsed_json->IsValid = true; - return NULL; -} - -uint16_t array_get_element_count(uint16_t array_token_index, - const parsed_json_t *parsed_transaction) { - if (array_token_index < 0 || array_token_index > parsed_transaction->NumberOfTokens) { - return 0; - } - - jsmntok_t array_token = parsed_transaction->Tokens[array_token_index]; - uint16_t token_index = array_token_index; - uint16_t element_count = 0; - uint16_t prev_element_end = array_token.start; - while (true) { - token_index++; - if (token_index >= parsed_transaction->NumberOfTokens) { - break; - } - jsmntok_t current_token = parsed_transaction->Tokens[token_index]; - if (current_token.start > array_token.end) { - break; - } - if (current_token.start <= prev_element_end) { - continue; - } - prev_element_end = current_token.end; - element_count++; - } - - return element_count; -} - -int16_t array_get_nth_element(uint16_t array_token_index, - uint16_t element_index, - const parsed_json_t *parsed_transaction) { - if (array_token_index < 0 || array_token_index > parsed_transaction->NumberOfTokens) { - return -1; - } - - jsmntok_t array_token = parsed_transaction->Tokens[array_token_index]; - uint16_t token_index = array_token_index; - uint16_t element_count = 0; - uint16_t prev_element_end = array_token.start; - while (true) { - token_index++; - if (token_index >= parsed_transaction->NumberOfTokens) { - break; - } - jsmntok_t current_token = parsed_transaction->Tokens[token_index]; - if (current_token.start > array_token.end) { - break; - } - if (current_token.start <= prev_element_end) { - continue; - } - prev_element_end = current_token.end; - if (element_count == element_index) { - return token_index; - } - element_count++; - } - - return -1; -} - -uint16_t object_get_element_count(uint16_t object_token_index, - const parsed_json_t *parsed_transaction) { - if (object_token_index < 0 || object_token_index > parsed_transaction->NumberOfTokens) { - return 0; - } - - jsmntok_t object_token = parsed_transaction->Tokens[object_token_index]; - uint16_t token_index = object_token_index; - uint16_t element_count = 0; - uint16_t prev_element_end = object_token.start; - token_index++; - while (true) { - if (token_index >= parsed_transaction->NumberOfTokens) { - break; - } - jsmntok_t key_token = parsed_transaction->Tokens[token_index++]; - jsmntok_t value_token = parsed_transaction->Tokens[token_index]; - if (key_token.start > object_token.end) { - break; - } - if (key_token.start <= prev_element_end) { - continue; - } - prev_element_end = value_token.end; - element_count++; - } - - return element_count; -} - -int16_t object_get_nth_key(uint16_t object_token_index, - uint16_t object_element_index, - const parsed_json_t *parsed_transaction) { - if (object_token_index < 0 || object_token_index > parsed_transaction->NumberOfTokens) { - return -1; - } - - jsmntok_t object_token = parsed_transaction->Tokens[object_token_index]; - uint16_t token_index = object_token_index; - uint16_t element_count = 0; - uint16_t prev_element_end = object_token.start; - token_index++; - while (true) { - if (token_index >= parsed_transaction->NumberOfTokens) { - break; - } - jsmntok_t key_token = parsed_transaction->Tokens[token_index++]; - jsmntok_t value_token = parsed_transaction->Tokens[token_index]; - if (key_token.start > object_token.end) { - break; - } - if (key_token.start <= prev_element_end) { - continue; - } - prev_element_end = value_token.end; - if (element_count == object_element_index) { - return token_index - 1; - } - element_count++; - } - - return -1; -} - -int16_t object_get_nth_value(uint16_t object_token_index, - uint16_t object_element_index, - const parsed_json_t *parsed_transaction) { - if (object_token_index < 0 || object_token_index > parsed_transaction->NumberOfTokens) { - return -1; - } - - int key_index = object_get_nth_key(object_token_index, object_element_index, parsed_transaction); - if (key_index != -1) { - return key_index + 1; - } - return -1; -} - -int16_t object_get_value(uint16_t object_token_index, - const char *key_name, - const parsed_json_t *parsed_transaction, - const char *transaction) { - if (object_token_index < 0 || object_token_index > parsed_transaction->NumberOfTokens) { - return -1; - } - - const jsmntok_t object_token = parsed_transaction->Tokens[object_token_index]; - - int token_index = object_token_index; - int prev_element_end = object_token.start; - token_index++; - - while (token_index < parsed_transaction->NumberOfTokens) { - const jsmntok_t key_token = parsed_transaction->Tokens[token_index]; - token_index++; - const jsmntok_t value_token = parsed_transaction->Tokens[token_index]; - - if (key_token.start > object_token.end) { - break; - } - if (key_token.start <= prev_element_end) { - continue; - } - prev_element_end = value_token.end; - - if ( ((uint16_t) strlen(key_name)) == (key_token.end - key_token.start)) { - if (EQUALS(key_name, transaction + key_token.start, key_token.end - key_token.start)) { - return token_index; - } - } - } - - return -1; -} diff --git a/src/lib/transaction.c b/src/lib/transaction.c deleted file mode 100644 index 86235ae6..00000000 --- a/src/lib/transaction.c +++ /dev/null @@ -1,98 +0,0 @@ -/******************************************************************************* -* (c) ZondaX GmbH -* -* Licensed under the Apache License, Version 2.0 (the "License"); -* you may not use this file except in compliance with the License. -* You may obtain a copy of the License at -* -* http://www.apache.org/licenses/LICENSE-2.0 -* -* Unless required by applicable law or agreed to in writing, software -* distributed under the License is distributed on an "AS IS" BASIS, -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -* See the License for the specific language governing permissions and -* limitations under the License. -********************************************************************************/ - -#include "transaction.h" -#include "apdu_codes.h" -#include "buffering.h" -#include "json_parser.h" -#include "tx_validate.h" -#include "tx_parser.h" - -// TODO: Remove this dependency -#include "../view.h" - -#if defined(TARGET_NANOX) - #define RAM_BUFFER_SIZE 8192 - #define FLASH_BUFFER_SIZE 16384 -#elif defined(TARGET_NANOS) - #define RAM_BUFFER_SIZE 416 - #define FLASH_BUFFER_SIZE 8192 -#endif - -// Ram -uint8_t ram_buffer[RAM_BUFFER_SIZE]; - -// Flash -typedef struct { - uint8_t buffer[FLASH_BUFFER_SIZE]; -} storage_t; - -#if defined(TARGET_NANOS) -storage_t N_appdata_impl __attribute__ ((aligned(64))); -#define N_appdata (*(storage_t *)PIC(&N_appdata_impl)) - -#elif defined(TARGET_NANOX) -storage_t const N_appdata_impl __attribute__ ((aligned(64))); -#define N_appdata (*(volatile storage_t *)PIC(&N_appdata_impl)) -#endif - -parsed_json_t parsed_transaction; - -void transaction_initialize() { - buffering_init( - ram_buffer, - sizeof(ram_buffer), - N_appdata.buffer, - sizeof(N_appdata.buffer) - ); -} - -void transaction_reset() { - buffering_reset(); -} - -uint32_t transaction_append(unsigned char *buffer, uint32_t length) { - return buffering_append(buffer, length); -} - -uint32_t transaction_get_buffer_length() { - return buffering_get_buffer()->pos; -} - -uint8_t *transaction_get_buffer() { - return buffering_get_buffer()->data; -} - -const char* transaction_parse() { - const char *transaction_buffer = (const char *) transaction_get_buffer(); - const char* error_msg = json_parse_s(&parsed_transaction, transaction_buffer, transaction_get_buffer_length()); - if (error_msg != NULL) { - return error_msg; - } - error_msg = json_validate(&parsed_transaction, transaction_buffer); - if (error_msg != NULL) { - return error_msg; - } - - parsing_context_t context; - context.tx = transaction_buffer; - context.max_chars_per_key_line = MAX_CHARS_PER_KEY_LINE; - context.max_chars_per_value_line = MAX_CHARS_PER_VALUE_LINE; - context.parsed_tx = &parsed_transaction; - - set_parsing_context(context); - return NULL; -} diff --git a/src/lib/tx_display.c b/src/lib/tx_display.c deleted file mode 100644 index b40dd0c1..00000000 --- a/src/lib/tx_display.c +++ /dev/null @@ -1,256 +0,0 @@ -/******************************************************************************* -* (c) ZondaX GmbH -* -* Licensed under the Apache License, Version 2.0 (the "License"); -* you may not use this file except in compliance with the License. -* You may obtain a copy of the License at -* -* http://www.apache.org/licenses/LICENSE-2.0 -* -* Unless required by applicable law or agreed to in writing, software -* distributed under the License is distributed on an "AS IS" BASIS, -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -* See the License for the specific language governing permissions and -* limitations under the License. -********************************************************************************/ - -#include -#include -#include "tx_parser.h" -#include "tx_display.h" -#include "json_parser.h" - -#if defined(TARGET_NANOS) || defined(TARGET_NANOX) -#include "os.h" -#else -#define PIC(X) X -#endif - -// Required pages -// FIXME: the required root items have been moved to a function due to PIC issues. Refactor and fix -const char *get_required_root_item(uint8_t i) { - switch (i) { - case 0: - return "chain_id"; - case 1: - return "account_number"; - case 2: - return "sequence"; - case 3: - return "fee"; - case 4: - return "memo"; - case 5: - return "msgs"; - default: - return "?"; - } -} - -//const char *root_required_items[NUM_REQUIRED_ROOT_PAGES] = { -// "chain_id", -// "account_number", -// "sequence", -// "fee", -// "memo", -// "msgs" -//}; - -static const uint16_t root_max_level[NUM_REQUIRED_ROOT_PAGES] = { - 2, // "chain_id", - 2, // "account_number", - 2, // "sequence", - 1, // "fee", - 2, // "memo" - 2, // "msgs" -}; - -static const key_subst_t key_substitutions[NUM_KEY_SUBSTITUTIONS] = { - {"chain_id", "Chain ID"}, - {"account_number", "Account"}, - {"sequence", "Sequence"}, - {"memo", "Memo"}, - {"fee/amount", "Fee"}, - {"fee/gas", "Gas"}, - {"msgs/type", "Type"}, - - // FIXME: Are these obsolete?? multisend? - {"msgs/inputs/address", "Source Address"}, - {"msgs/inputs/coins", "Source Coins"}, - {"msgs/outputs/address", "Dest Address"}, - {"msgs/outputs/coins", "Dest Coins"}, - - // MsgSend - {"msgs/value/from_address", "From"}, - {"msgs/value/to_address", "To"}, - {"msgs/value/amount", "Amount"}, - - // MsgDelegate - {"msgs/value/delegator_address", "Delegator"}, - {"msgs/value/validator_address", "Validator"}, - - // MsgUndelegate -// {"msgs/value/delegator_address", "Delegator"}, -// {"msgs/value/validator_address", "Validator"}, - - // MsgBeginRedelegate -// {"msgs/value/delegator_address", "Delegator"}, - {"msgs/value/validator_src_address", "Validator Source"}, - {"msgs/value/validator_dst_address", "Validator Dest"}, - - // MsgSubmitProposal - {"msgs/value/description", "Description"}, - {"msgs/value/initial_deposit/amount", "Deposit Amount"}, - {"msgs/value/initial_deposit/denom", "Deposit Denom"}, - {"msgs/value/proposal_type", "Proposal"}, - {"msgs/value/proposer", "Proposer"}, - {"msgs/value/title", "Title"}, - - // MsgDeposit - {"msgs/value/depositer", "Sender"}, - {"msgs/value/proposal_id", "Proposal ID"}, - {"msgs/value/amount", "Amount"}, - - // MsgVote - {"msgs/value/voter", "Description"}, -// {"msgs/value/proposal_id", "Proposal ID"}, - {"msgs/value/option", "Option"}, - - // MsgWithdrawDelegationReward -// {"msgs/value/delegator_address", "Delegator"}, // duplicated -// {"msgs/value/validator_address", "Validator"}, // duplicated -}; - -static const key_subst_t value_substitutions[NUM_VALUE_SUBSTITUTIONS] = { - {"cosmos-sdk/MsgSend", "Send"}, - {"cosmos-sdk/MsgDelegate", "Delegate"}, - {"cosmos-sdk/MsgUndelegate", "Undelegate"}, - {"cosmos-sdk/MsgBeginRedelegate", "Redelegate"}, - {"cosmos-sdk/MsgSubmitProposal", "Propose"}, - {"cosmos-sdk/MsgDeposit", "Deposit"}, - {"cosmos-sdk/MsgVote", "Vote"}, - {"cosmos-sdk/MsgWithdrawDelegationReward", "Withdraw Reward"}, -}; - -#define STRNCPY_S(DST, SRC, DST_SIZE) \ - strncpy(DST, SRC, DST_SIZE - 1); \ - DST[DST_SIZE - 1] = 0; - -display_cache_t display_cache; - -display_cache_t *tx_display_cache() { - return &display_cache; -} - -void tx_display_index_root() { - if (parsing_context.cache_valid) { - return; - } - - // Clear values - display_cache.num_pages = 0; - memset(display_cache.num_subpages, 0, NUM_REQUIRED_ROOT_PAGES); - memset(display_cache.subroot_start_token, TX_TOKEN_NOT_FOUND, NUM_REQUIRED_ROOT_PAGES); - - // Calculate pages - int8_t found = 0; - for (int8_t idx = 0; idx < NUM_REQUIRED_ROOT_PAGES; idx++) { - const int16_t subroot_token_idx = object_get_value(ROOT_TOKEN_INDEX, - get_required_root_item(idx), - parsing_context.parsed_tx, - parsing_context.tx); - if (subroot_token_idx < 0) { - break; - } - - display_cache.num_subpages[idx] = 0; - display_cache.subroot_start_token[idx] = subroot_token_idx; - - char tmp_key[2]; - char tmp_val[2]; - INIT_QUERY_CONTEXT(tmp_key, sizeof(tmp_key), tmp_val, sizeof(tmp_val), 0, root_max_level[idx]) - STRNCPY_S(tx_ctx.query.out_key, get_required_root_item(idx), tx_ctx.query.out_key_len); - tx_ctx.max_depth = MAX_RECURSION_DEPTH; - tx_ctx.query.item_index = 0; - - found = 0; - while (found >= 0) { - tx_ctx.item_index_current = 0; - found = tx_traverse(subroot_token_idx); - - if (found >= 0) { - display_cache.num_subpages[idx]++; - tx_ctx.query.item_index++; - } - }; - display_cache.num_pages += display_cache.num_subpages[idx]; - - if (display_cache.num_subpages[idx] == 0) { - break; - } - } - - parsing_context.cache_valid = 1; -} - -int16_t tx_display_num_pages() { - tx_display_index_root(); - return display_cache.num_pages; -} - -// This function assumes that the tx_ctx has been set properly -int16_t tx_display_get_item(uint16_t page_index) { - if (!parsing_context.cache_valid) { - return ERR_MUST_INDEX_FIRST; - } - - // TODO: Verify it has been properly set? - tx_ctx.query.out_key[0] = 0; - tx_ctx.query.out_val[0] = 0; - if (page_index < 0 || page_index >= display_cache.num_pages) { - return -1; - } - - tx_ctx.query.item_index = 0; - uint16_t root_index = 0; - for (uint16_t i = 0; i < page_index; i++) { - tx_ctx.query.item_index++; - if (tx_ctx.query.item_index >= display_cache.num_subpages[root_index]) { - tx_ctx.query.item_index = 0; - root_index++; - } - } - - tx_ctx.item_index_current = 0; - tx_ctx.max_level = root_max_level[root_index]; - tx_ctx.max_depth = MAX_RECURSION_DEPTH; - - STRNCPY_S(tx_ctx.query.out_key, get_required_root_item(root_index), tx_ctx.query.out_key_len); - - int16_t ret = tx_traverse(display_cache.subroot_start_token[root_index]); - - return ret; -} - -void tx_display_make_friendly() { - // post process keys - for (int8_t i = 0; i < NUM_KEY_SUBSTITUTIONS; i++) { - if (!strcmp(tx_ctx.query.out_key, key_substitutions[i].str1)) { - STRNCPY_S(tx_ctx.query.out_key, - key_substitutions[i].str2, - tx_ctx.query.out_key_len); - break; - } - } - - for (int8_t i = 0; i < NUM_VALUE_SUBSTITUTIONS; i++) { - if (!strcmp(tx_ctx.query.out_val, value_substitutions[i].str1)) { - STRNCPY_S(tx_ctx.query.out_val, - value_substitutions[i].str2, - tx_ctx.query.out_val_len); - break; - } - } - -} - diff --git a/src/lib/tx_display.h b/src/lib/tx_display.h deleted file mode 100644 index 6beb587c..00000000 --- a/src/lib/tx_display.h +++ /dev/null @@ -1,66 +0,0 @@ -/******************************************************************************* -* (c) ZondaX GmbH -* -* Licensed under the Apache License, Version 2.0 (the "License"); -* you may not use this file except in compliance with the License. -* You may obtain a copy of the License at -* -* http://www.apache.org/licenses/LICENSE-2.0 -* -* Unless required by applicable law or agreed to in writing, software -* distributed under the License is distributed on an "AS IS" BASIS, -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -* See the License for the specific language governing permissions and -* limitations under the License. -********************************************************************************/ - -#pragma once -#include - -#include "json_parser.h" -#include "tx_parser.h" - -#ifdef __cplusplus -extern "C" { -#endif - -#define ERR_MUST_INDEX_FIRST -2 - -#define NUM_REQUIRED_ROOT_PAGES 6 -#define NUM_KEY_SUBSTITUTIONS 29 -#define NUM_VALUE_SUBSTITUTIONS 8 - -typedef struct { - char str1[50]; - char str2[50]; -} key_subst_t; - -typedef struct { - int16_t num_pages; - int16_t subroot_start_token[NUM_REQUIRED_ROOT_PAGES]; - uint8_t num_subpages[NUM_REQUIRED_ROOT_PAGES]; -} display_cache_t; - -// This is only used for testing purposes -display_cache_t *tx_display_cache(); - -// This must be run before accessing items -void tx_display_index_root(); - -/// This is the main function called from ledger that updates key and value strings -/// that are going to be displayed in the UI. -/// This function assumes that the tx_ctx has been properly set -int16_t tx_display_get_item(uint16_t page_index); - -/// Return number of UI pages that we'll have for the current json transaction (only if the tx is valid) -/// \return number of pages (msg pages + 5 required) -int16_t tx_display_num_pages(); - -/// Apply postprocessing rules to key and values -void tx_display_make_friendly(); - -//--------------------------------------------- - -#ifdef __cplusplus -} -#endif diff --git a/src/lib/tx_parser.c b/src/lib/tx_parser.c deleted file mode 100644 index cffd81d5..00000000 --- a/src/lib/tx_parser.c +++ /dev/null @@ -1,172 +0,0 @@ -/******************************************************************************* -* (c) ZondaX GmbH -* -* Licensed under the Apache License, Version 2.0 (the "License"); -* you may not use this file except in compliance with the License. -* You may obtain a copy of the License at -* -* http://www.apache.org/licenses/LICENSE-2.0 -* -* Unless required by applicable law or agreed to in writing, software -* distributed under the License is distributed on an "AS IS" BASIS, -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -* See the License for the specific language governing permissions and -* limitations under the License. -********************************************************************************/ - -#include -#include -#include "tx_parser.h" -#include "json_parser.h" - -#if defined(TARGET_NANOX) || defined(TARGET_NANOS) -#include "os.h" -#define COPYFUNC os_memmove -#else -#define COPYFUNC memcpy -#define __always_inline -#endif - -// Global context to save memory / stack space in recursive calls -parsing_context_t parsing_context; -tx_context_t tx_ctx; - -void set_parsing_context(parsing_context_t context) { - parsing_context = context; - // reset cached values - parsing_context.cache_valid = false; -} - -// strcat but source does not need to be terminated (a chunk from a bigger string is concatenated) -// dst_max is measured in bytes including the space for NULL termination -// src_size does not include NULL termination -__always_inline void strcat_chunk_s(char *dst, uint16_t dst_max, const char *src_chunk, uint16_t src_chunk_size) { - *(dst + dst_max - 1) = 0; // last character terminates with zero in case we go beyond bounds - const uint16_t prev_size = strlen(dst); - - uint16_t space_left = dst_max - prev_size - 1; // -1 because requires termination - - if (src_chunk_size > space_left) { - src_chunk_size = space_left; - } - - if (src_chunk_size > 0) { - // Check bounds - COPYFUNC(dst + prev_size, src_chunk, src_chunk_size); - // terminate - *(dst + prev_size + src_chunk_size) = 0; - } -} - -__always_inline int16_t tx_get_value(const int16_t token_index) { - - const int16_t token_start = parsing_context.parsed_tx->Tokens[token_index].start; - const int16_t token_end = parsing_context.parsed_tx->Tokens[token_index].end; - const int16_t token_len = token_end - token_start; - - int16_t num_chunks = (token_len / (tx_ctx.query.out_val_len - 1)) + 1; - if (token_len > 0 && (token_len % (tx_ctx.query.out_val_len - 1) == 0)) - num_chunks--; - - tx_ctx.query.out_val[0] = '\0'; // flush - if (tx_ctx.query.chunk_index >= num_chunks) { - return TX_TOKEN_NOT_FOUND; - } - - const int16_t chunk_start = token_start + tx_ctx.query.chunk_index * (tx_ctx.query.out_val_len - 1); - int16_t chunk_len = token_end - chunk_start; - - if (chunk_len < 0) { - return TX_TOKEN_NOT_FOUND; - } - - if (chunk_len > tx_ctx.query.out_val_len - 1) { - chunk_len = tx_ctx.query.out_val_len - 1; - } - COPYFUNC(tx_ctx.query.out_val, parsing_context.tx + chunk_start, chunk_len); - tx_ctx.query.out_val[chunk_len] = 0; - - return num_chunks; -} - -///// Update key characters from json transaction read from the token_index element. -__always_inline void append_key_item(int16_t token_index) { - if (*tx_ctx.query.out_key > 0) { - // There is already something there, add separator - strcat_chunk_s(tx_ctx.query.out_key, tx_ctx.query.out_key_len, "/", 1); - } - - const int16_t token_start = parsing_context.parsed_tx->Tokens[token_index].start; - const int16_t token_end = parsing_context.parsed_tx->Tokens[token_index].end; - const char *address_ptr = parsing_context.tx + token_start; - const int16_t new_item_size = token_end - token_start; - - strcat_chunk_s(tx_ctx.query.out_key, tx_ctx.query.out_key_len, address_ptr, new_item_size); -} - -int16_t tx_traverse(int16_t root_token_index) { - const jsmntype_t token_type = parsing_context.parsed_tx->Tokens[root_token_index].type; - - if (tx_ctx.max_level <= 0 || tx_ctx.max_depth <= 0 || - token_type == JSMN_STRING || - token_type == JSMN_PRIMITIVE) { - - // Early bail out - if (tx_ctx.item_index_current == tx_ctx.query.item_index) { - return tx_get_value(root_token_index); - } - tx_ctx.item_index_current++; - return TX_TOKEN_NOT_FOUND; - } - - const int16_t el_count = object_get_element_count(root_token_index, parsing_context.parsed_tx); - int16_t num_chunks = TX_TOKEN_NOT_FOUND; - - switch (token_type) { - case JSMN_OBJECT: { - const int16_t key_len = strlen(tx_ctx.query.out_key); - for (int16_t i = 0; i < el_count; ++i) { - const int16_t key_index = object_get_nth_key(root_token_index, i, parsing_context.parsed_tx); - const int16_t value_index = object_get_nth_value(root_token_index, i, parsing_context.parsed_tx); - - // Skip writing keys if we are actually exploring to count - if (tx_ctx.query.item_index != TX_TOKEN_NOT_FOUND) { - append_key_item(key_index); - } - - // When traversing objects both level and depth should be considered - tx_ctx.max_level--; - tx_ctx.max_depth--; - num_chunks = tx_traverse(value_index); // Traverse the value, extracting subkeys - tx_ctx.max_level++; - tx_ctx.max_depth++; - - if (num_chunks != TX_TOKEN_NOT_FOUND) { - break; - } - - *(tx_ctx.query.out_key + key_len) = 0; - } - break; - } - case JSMN_ARRAY: { - for (int16_t i = 0; i < el_count; ++i) { - const int16_t element_index = array_get_nth_element(root_token_index, i, parsing_context.parsed_tx); - - // When iterating along an array, the level does not change but we need to count the recursion - tx_ctx.max_depth--; - num_chunks = tx_traverse(element_index); - tx_ctx.max_depth++; - - if (num_chunks != TX_TOKEN_NOT_FOUND) { - break; - } - } - break; - } - default: - break; - } - - return num_chunks; -} diff --git a/src/lib/tx_parser.h b/src/lib/tx_parser.h deleted file mode 100644 index 3bf3cead..00000000 --- a/src/lib/tx_parser.h +++ /dev/null @@ -1,86 +0,0 @@ -/******************************************************************************* -* (c) ZondaX GmbH -* -* Licensed under the Apache License, Version 2.0 (the "License"); -* you may not use this file except in compliance with the License. -* You may obtain a copy of the License at -* -* http://www.apache.org/licenses/LICENSE-2.0 -* -* Unless required by applicable law or agreed to in writing, software -* distributed under the License is distributed on an "AS IS" BASIS, -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -* See the License for the specific language governing permissions and -* limitations under the License. -********************************************************************************/ - -#pragma once - -#include "json_parser.h" -#include - -#ifdef __cplusplus -extern "C" { -#endif - -#define TX_TOKEN_NOT_FOUND (-1) - -#define MAX_RECURSION_DEPTH 6 - -typedef struct { - int16_t item_index; // ?? - int16_t chunk_index; // ?? - - char *out_key; // ?? - int16_t out_key_len; // ?? - char *out_val; // ?? - int16_t out_val_len; // ?? -} tx_query_t; - -// used to reduce stack size usage in recursive calls -typedef struct { - tx_query_t query; // ?? - int16_t item_index_current; // ?? - uint8_t max_level; - uint8_t max_depth; -} tx_context_t; - -extern parsing_context_t parsing_context; -extern tx_context_t tx_ctx; - -#define INIT_QUERY_CONTEXT(_KEY, _KEY_LEN, _VAL, _VAL_LEN, _CHUNK_IDX, _MAX_LEVEL) \ - INIT_QUERY(_KEY, _KEY_LEN, _VAL, _VAL_LEN, _CHUNK_IDX) \ - tx_ctx.item_index_current = 0; \ - tx_ctx.max_depth = MAX_RECURSION_DEPTH; \ - tx_ctx.max_level = _MAX_LEVEL; - -#define INIT_QUERY(_KEY, _KEY_LEN, _VAL, _VAL_LEN, _CHUNK_IDX) \ - _KEY[0] = 0; \ - _VAL[0] = 0; \ - tx_ctx.query.out_key=_KEY; \ - tx_ctx.query.out_val=_VAL; \ - tx_ctx.query.out_key_len = _KEY_LEN; \ - tx_ctx.query.out_val_len = _VAL_LEN; \ - tx_ctx.query.item_index= 0; \ - tx_ctx.query.chunk_index = _CHUNK_IDX; - -/// Validate json transaction -/// \param parsed_transacton -/// \param transaction -/// \return -const char *json_validate(parsed_json_t *parsed_transaction, const char *transaction); - -// Traverses transaction data and fills tx_context -// \return -1 if the item was not found or the number of available chunks for this item -int16_t tx_traverse(int16_t root_token_index); - -// Retrieves the value for the corresponding token index. If the value goes beyond val_len, the chunk_idx will be used -int16_t tx_get_value(int16_t token_index); - -void set_parsing_context(parsing_context_t context); - -//--------------------------------------------- - -#ifdef __cplusplus -} -#endif diff --git a/src/lib/tx_validate.c b/src/lib/tx_validate.c deleted file mode 100644 index 8cabe23c..00000000 --- a/src/lib/tx_validate.c +++ /dev/null @@ -1,162 +0,0 @@ -/******************************************************************************* -* (c) ZondaX GmbH -* -* Licensed under the Apache License, Version 2.0 (the "License"); -* you may not use this file except in compliance with the License. -* You may obtain a copy of the License at -* -* http://www.apache.org/licenses/LICENSE-2.0 -* -* Unless required by applicable law or agreed to in writing, software -* distributed under the License is distributed on an "AS IS" BASIS, -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -* See the License for the specific language governing permissions and -* limitations under the License. -********************************************************************************/ - -#include -#include -#include "tx_parser.h" -#include "json_parser.h" - -const char whitespaces[] = { - 0x20,// space ' ' - 0x0c, // form_feed '\f' - 0x0a, // line_feed, '\n' - 0x0d, // carriage_return, '\r' - 0x09, // horizontal_tab, '\t' - 0x0b // vertical_tab, '\v' -}; - -int8_t is_space(char c) { - for (uint16_t i = 0; i < sizeof(whitespaces); i++) { - if (whitespaces[i] == c) { - return 1; - } - } - return 0; -} - -int8_t contains_whitespace(parsed_json_t *parsed_transaction, const char *transaction) { - int start = 0; - const int last_element_index = parsed_transaction->Tokens[0].end; - - // Starting at token 1 because token 0 contains full tx - for (int i = 1; i < parsed_transaction->NumberOfTokens; i++) { - if (parsed_transaction->Tokens[i].type != JSMN_UNDEFINED) { - const int end = parsed_transaction->Tokens[i].start; - for (int j = start; j < end; j++) { - if (is_space(transaction[j]) == 1) { - return 1; - } - } - start = parsed_transaction->Tokens[i].end + 1; - } else { - return 0; - } - } - while (start <= last_element_index && transaction[start] != '\0') { - if (is_space(transaction[start])) { - return 1; - } - start++; - } - return 0; -} - -int8_t is_sorted(int16_t first_index, int16_t second_index, - parsed_json_t *parsed_transaction, - const char *transaction) { -#if DEBUG_SORTING - char first[256]; - char second[256]; - - int size = parsed_tx->Tokens[first_index].end - parsed_tx->Tokens[first_index].start; - strncpy(first, tx + parsed_tx->Tokens[first_index].start, size); - first[size] = '\0'; - size = parsed_tx->Tokens[second_index].end - parsed_tx->Tokens[second_index].start; - strncpy(second, tx + parsed_tx->Tokens[second_index].start, size); - second[size] = '\0'; -#endif - - if (strcmp(transaction + parsed_transaction->Tokens[first_index].start, - transaction + parsed_transaction->Tokens[second_index].start) <= 0) { - return 1; - } - return 0; -} - -int8_t dictionaries_sorted(parsed_json_t *parsed_transaction, - const char *transaction) { - for (int i = 0; i < parsed_transaction->NumberOfTokens; i++) { - if (parsed_transaction->Tokens[i].type == JSMN_OBJECT) { - - const int count = object_get_element_count(i, parsed_transaction); - if (count > 1) { - int prev_token_index = object_get_nth_key(i, 0, parsed_transaction); - for (int j = 1; j < count; j++) { - int next_token_index = object_get_nth_key(i, j, parsed_transaction); - if (!is_sorted(prev_token_index, next_token_index, parsed_transaction, transaction)) { - return 0; - } - prev_token_index = next_token_index; - } - } - } - } - return 1; -} - -const char *json_validate(parsed_json_t *parsed_transaction, const char *transaction) { - if (contains_whitespace(parsed_transaction, transaction) == 1) { - return "JSON Contains whitespace in the corpus"; - } - - if (dictionaries_sorted(parsed_transaction, transaction) != 1) { - return "JSON Dictionaries are not sorted"; - } - - if (object_get_value(0, - "chain_id", - parsed_transaction, - transaction) == -1) { - return "JSON Missing chain_id"; - } - - if (object_get_value(0, - "sequence", - parsed_transaction, - transaction) == -1) { - return "JSON Missing sequence"; - } - - if (object_get_value(0, - "fee", - parsed_transaction, - transaction) == -1) { - return "JSON Missing fee"; - } - - if (object_get_value(0, - "msgs", - parsed_transaction, - transaction) == -1) { - return "JSON Missing msgs"; - } - - if (object_get_value(0, - "account_number", - parsed_transaction, - transaction) == -1) { - return "JSON Missing account_number"; - } - - if (object_get_value(0, - "memo", - parsed_transaction, - transaction) == -1) { - return "JSON Missing memo"; - } - - return NULL; -} diff --git a/src/view.c b/src/view.c deleted file mode 100644 index a1b8b722..00000000 --- a/src/view.c +++ /dev/null @@ -1,453 +0,0 @@ -/******************************************************************************* -* (c) 2016 Ledger -* (c) 2018, 2019 ZondaX GmbH -* -* Licensed under the Apache License, Version 2.0 (the "License"); -* you may not use this file except in compliance with the License. -* You may obtain a copy of the License at -* -* http://www.apache.org/licenses/LICENSE-2.0 -* -* Unless required by applicable law or agreed to in writing, software -* distributed under the License is distributed on an "AS IS" BASIS, -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -* See the License for the specific language governing permissions and -* limitations under the License. -********************************************************************************/ - -#include "view.h" -#include "view_templates.h" -#include "view_expl.h" - -#include "glyphs.h" -#include "bagl.h" -#include "zxmacros.h" -#include "crypto.h" - -#include -#include - -viewctl_delegate_getData ehGetData = NULL; -viewctl_delegate_accept ehAccept = NULL; -viewctl_delegate_reject ehReject = NULL; - -#define MAX_VAL(a, b) ( (a)>(b) ? (a) : (b) ) -#define MIN_VAL(a, b) ( (a)<(b) ? (a) : (b) ) - -#define VIEW_ADDR_MODE_ACCOUNT 0 -#define VIEW_ADDR_MODE_INDEX 1 -#define VIEW_ADDR_MODE_SHOW 2 -#define VIEW_ADDR_MODE_CONFIRM 3 - -#define UIID_ICONACCEPT 0x50 -#define UIID_ICONREJECT 0x51 -#define UIID_ICONLEFT1 0x52 -#define UIID_ICONRIGHT1 0x53 -#define UIID_ICONLEFT2 0x54 -#define UIID_ICONRIGHT2 0x55 - -#define UIID_MARKER1 0x60 -#define UIID_MARKER2 0x61 - -void accept(unsigned int _) { - UNUSED(_); - if (ehAccept != NULL) ehAccept(); -} - -void reject(unsigned int _) { - UNUSED(_); - if (ehReject != NULL) ehReject(); -} - -void show_idle_menu() { - view_idle(0); -} - -void view_tx_menu(unsigned int _); - -void view_addr_choose_show(unsigned int _); - -void view_addr_choose_refresh(); - -void view_addr_choose_update(); - -struct { - // modes - // 0 - select account - // 1 - select index - struct { - unsigned int mode: 4; - unsigned int marker_blink: 1; - } status; - uint32_t account; - uint32_t index; -} view_addr_choose_data; - - -#if defined(TARGET_NANOX) - -#include "ux.h" -ux_state_t G_ux; -bolos_ux_params_t G_ux_params; - -#ifdef TESTING_ENABLED -UX_FLOW_DEF_NOCB(ux_idle_flow_1_step, pbb, { &C_icon_app, "Tendermint", "Cosmos TEST!", }); -#else -UX_FLOW_DEF_NOCB(ux_idle_flow_1_step, pbb, { &C_icon_app, "Tendermint", "Cosmos", }); -#endif -UX_FLOW_DEF_VALID(ux_idle_flow_2_step, pb, view_addr_choose_show(0), { &C_icon_eye, "Show Address",}); -UX_FLOW_DEF_NOCB(ux_idle_flow_3_step, bn, { "Version", APPVERSION, }); -UX_FLOW_DEF_VALID(ux_idle_flow_4_step, pb, os_sched_exit(-1), { &C_icon_dashboard, "Quit",}); -const ux_flow_step_t *const ux_idle_flow [] = { - &ux_idle_flow_1_step, - &ux_idle_flow_2_step, - &ux_idle_flow_3_step, - &ux_idle_flow_4_step, - FLOW_END_STEP, -}; - -UX_FLOW_DEF_VALID(ux_tx_flow_1_step, pbb, view_tx_show(0), { &C_icon_eye, "Review", "Transaction" }); -UX_FLOW_DEF_VALID(ux_tx_flow_2_step, pbb, accept(0), { &C_icon_validate_14, "Sign", "Transaction" }); -UX_FLOW_DEF_VALID(ux_tx_flow_3_step, pbb, reject(0), { &C_icon_crossmark, "Reject", "Transaction" }); -const ux_flow_step_t *const ux_tx_flow [] = { - &ux_tx_flow_1_step, - &ux_tx_flow_2_step, - &ux_tx_flow_3_step, - FLOW_END_STEP, -}; - -UX_FLOW_DEF_NOCB(ux_addr_flow_1_step, bnn, { "Address Request", viewctl.title, viewctl.dataKey}); -UX_FLOW_DEF_NOCB(ux_addr_flow_2_step, bnnn_paging, { .title = "Address", .text = viewctl.dataValue }); -UX_FLOW_DEF_VALID(ux_addr_flow_3_step, pb, accept(0), { &C_icon_validate_14, "Reply", }); -UX_FLOW_DEF_VALID(ux_addr_flow_4_step, pb, reject(0), { &C_icon_crossmark, "Reject", }); -const ux_flow_step_t *const ux_addr_flow [] = { - &ux_addr_flow_1_step, - &ux_addr_flow_2_step, - &ux_addr_flow_3_step, - &ux_addr_flow_4_step, - FLOW_END_STEP, -}; -#else - -// Nano S -ux_state_t ux; - -const ux_menu_entry_t menu_transaction_info[] = { - {NULL, view_tx_show, 0, NULL, "View transaction", NULL, 0, 0}, - {NULL, accept, 0, NULL, "Sign transaction", NULL, 0, 0}, - {NULL, reject, 0, &C_icon_crossmark, "Reject", NULL, 60, 40}, - UX_MENU_END -}; - -const ux_menu_entry_t menu_main[] = { -#ifdef TESTING_ENABLED - {NULL, NULL, 0, &C_icon_app, "Tendermint", "Cosmos TEST!", 33, 12}, -#else - {NULL, NULL, 0, &C_icon_app, "Tendermint", "Cosmos", 33, 12}, -#endif - {NULL, view_addr_choose_show, 0, NULL, "Show Address", NULL, 0, 0}, - {NULL, NULL, 0, NULL, "v"APPVERSION, NULL, 0, 0}, - {NULL, os_sched_exit, 0, &C_icon_dashboard, "Quit app", NULL, 50, 29}, - UX_MENU_END -}; - -const ux_menu_entry_t menu_status[] = { - {NULL, NULL, 0, &C_icon_app, viewctl.dataKey, viewctl.dataValue, 33, 12}, - UX_MENU_END -}; - -#endif - -static const bagl_element_t view_addr_choose[] = { - UI_FillRectangle(0, 0, 0, UI_SCREEN_WIDTH, UI_SCREEN_HEIGHT, 0x000000, 0xFFFFFF), - - UI_LabelLine(UIID_LABEL + 0, 0, 9 + UI_11PX * 0, UI_SCREEN_WIDTH, UI_11PX, UI_WHITE, UI_BLACK, - (const char *) viewctl.title), - UI_LabelLine(UIID_LABEL + 1, 0, 9 + UI_11PX * 1, UI_SCREEN_WIDTH, UI_11PX, UI_WHITE, UI_BLACK, - (const char *) viewctl.dataKey), - -#if defined(TARGET_NANOX) - UI_Icon(UIID_ICONLEFT1, 2, 28, 4, 7, BAGL_GLYPH_ICON_LEFT), - UI_Icon(UIID_ICONRIGHT1, 122, 28, 4, 7, BAGL_GLYPH_ICON_RIGHT), - UI_Icon(UIID_ICONLEFT2, 2, 28, 4, 7, BAGL_GLYPH_ICON_LEFT), - UI_Icon(UIID_ICONRIGHT2, 122, 28, 4, 7, BAGL_GLYPH_ICON_RIGHT), - - UI_Icon(UIID_MARKER1, 0, 0, 7, 7, ((const char*)&C_digit_dot)), - UI_Icon(UIID_MARKER2, 0, 9, 7, 7, ((const char*)&C_digit_dot)), - - UI_LabelLine(UIID_LABEL+2, 0, 9 + UI_11PX * 2, UI_SCREEN_WIDTH, UI_11PX, UI_WHITE, UI_BLACK, (const char *) viewctl.dataValueChunk[0]), - UI_LabelLine(UIID_LABEL+3, 0, 9 + UI_11PX * 3, UI_SCREEN_WIDTH, UI_11PX, UI_WHITE, UI_BLACK, (const char *) viewctl.dataValueChunk[1]), - UI_LabelLine(UIID_LABEL+4, 0, 9 + UI_11PX * 4, UI_SCREEN_WIDTH, UI_11PX, UI_WHITE, UI_BLACK, (const char *) viewctl.dataValueChunk[2]), - UI_LabelLine(UIID_LABEL+5, 0, 9 + UI_11PX * 5, UI_SCREEN_WIDTH, UI_11PX, UI_WHITE, UI_BLACK, (const char *) viewctl.dataValueChunk[3]), -#else - UI_Icon(UIID_ICONREJECT, 0, 0, 7, 7, BAGL_GLYPH_ICON_CROSS), - UI_Icon(UIID_ICONACCEPT, 128 - 7, 0, 7, 7, BAGL_GLYPH_ICON_CHECK), - UI_Icon(UIID_ICONLEFT1, 0, 0, 7, 7, BAGL_GLYPH_ICON_LEFT), - UI_Icon(UIID_ICONRIGHT1, 128 - 7, 0, 7, 7, BAGL_GLYPH_ICON_RIGHT), - UI_Icon(UIID_ICONLEFT2, 0, 9, 7, 7, BAGL_GLYPH_ICON_LEFT), - UI_Icon(UIID_ICONRIGHT2, 128 - 7, 9, 7, 7, BAGL_GLYPH_ICON_RIGHT), - - UI_LabelLineScrolling(UIID_LABELSCROLL, 16, 30, 96, 11, UI_WHITE, UI_BLACK, (const char *) viewctl.dataValue), -#endif -}; - -const bagl_element_t *view_addr_choose_prepro(const bagl_element_t *element) { - switch (element->component.userid) { - case UIID_MARKER1: - if (view_addr_choose_data.status.mode != VIEW_ADDR_MODE_ACCOUNT) - return NULL; - break; - case UIID_MARKER2: - if (view_addr_choose_data.status.mode != VIEW_ADDR_MODE_INDEX) - return NULL; - break; - - case UIID_ICONLEFT1: - case UIID_ICONRIGHT1: - if (view_addr_choose_data.status.mode != VIEW_ADDR_MODE_ACCOUNT) - return NULL; - break; - - case UIID_ICONLEFT2: - case UIID_ICONRIGHT2: - if (view_addr_choose_data.status.mode != VIEW_ADDR_MODE_INDEX) - return NULL; - break; - - case UIID_ICONACCEPT: - if (view_addr_choose_data.status.mode != VIEW_ADDR_MODE_CONFIRM && - view_addr_choose_data.status.mode != VIEW_ADDR_MODE_SHOW) - return NULL; - break; - - case UIID_ICONREJECT: - if (view_addr_choose_data.status.mode != VIEW_ADDR_MODE_CONFIRM) - return NULL; - break; - - case UIID_LABELSCROLL: - UX_CALLBACK_SET_INTERVAL(MAX(3000, 1000 + bagl_label_roundtrip_duration_ms(element, 7))); - break; - } - return element; -} - -uint32_t bip32_field_add(uint32_t field, int16_t value) { - return (field + value) & 0x7FFFFFFF; -} - -static unsigned int view_addr_choose_button(unsigned int button_mask, unsigned int button_mask_counter) { - switch (button_mask) { - case BUTTON_EVT_RELEASED | BUTTON_LEFT | BUTTON_RIGHT: - // Press both to accept / switch mode - switch (view_addr_choose_data.status.mode) { - case VIEW_ADDR_MODE_ACCOUNT: - view_addr_choose_data.status.mode = VIEW_ADDR_MODE_INDEX; - break; - case VIEW_ADDR_MODE_INDEX: - view_addr_choose_data.status.mode = VIEW_ADDR_MODE_SHOW; - break; - default: - return 0; - } - break; - - case BUTTON_EVT_RELEASED | BUTTON_LEFT: - // Press left -> previous element - switch (view_addr_choose_data.status.mode) { - case VIEW_ADDR_MODE_ACCOUNT: - view_addr_choose_data.account = bip32_field_add(view_addr_choose_data.account, -1); - break; - case VIEW_ADDR_MODE_INDEX: - view_addr_choose_data.index = bip32_field_add(view_addr_choose_data.index, -1); - break; - case VIEW_ADDR_MODE_SHOW: - // DO NOTHING -#if defined(TARGET_NANOX) - show_idle_menu(); -#endif - return 0; - case VIEW_ADDR_MODE_CONFIRM: - reject(0); - return 0; - default: - return 0; - } - break; - - case BUTTON_EVT_RELEASED | BUTTON_RIGHT: - // Press right -> next element - switch (view_addr_choose_data.status.mode) { - case VIEW_ADDR_MODE_ACCOUNT: - view_addr_choose_data.account = bip32_field_add(view_addr_choose_data.account, +1); - break; - case VIEW_ADDR_MODE_INDEX: - view_addr_choose_data.index = bip32_field_add(view_addr_choose_data.index, +1); - break; - case VIEW_ADDR_MODE_SHOW: - show_idle_menu(); - return 0; - case VIEW_ADDR_MODE_CONFIRM: - accept(0); - return 0; - default: - return 0; - } - break; - - case BUTTON_EVT_FAST | BUTTON_LEFT: - // Hold left -> previous element (fast) - switch (view_addr_choose_data.status.mode) { - case VIEW_ADDR_MODE_ACCOUNT: - view_addr_choose_data.account = bip32_field_add(view_addr_choose_data.account, -10); - break; - case VIEW_ADDR_MODE_INDEX: - view_addr_choose_data.index = bip32_field_add(view_addr_choose_data.index, -10); - break; - default: - return 0; - } - break; - - case BUTTON_EVT_FAST | BUTTON_RIGHT: - // Press right -> next element (fast) - switch (view_addr_choose_data.status.mode) { - case VIEW_ADDR_MODE_ACCOUNT: - view_addr_choose_data.account = bip32_field_add(view_addr_choose_data.account, +10); - break; - case VIEW_ADDR_MODE_INDEX: - view_addr_choose_data.index = bip32_field_add(view_addr_choose_data.index, +10); - break; - default: - return 0; - } - break; - - default: - return 0; - } - - view_addr_choose_update(); - view_addr_choose_refresh(); - return 0; -} - -//////////////////////////////// -//////////////////////////////// -//////////////////////////////// - -void io_seproxyhal_display(const bagl_element_t *element) { - io_seproxyhal_display_default((bagl_element_t *) element); -} - -void view_init(void) { - UX_INIT(); -} - -void view_idle(unsigned int ignored) { -#if defined(TARGET_NANOS) - UX_MENU_DISPLAY(0, menu_main, NULL); -#elif defined(TARGET_NANOX) - if(G_ux.stack_count == 0) { - ux_stack_push(); - } - ux_flow_init(0, ux_idle_flow, NULL); -#endif -} - -void view_status(unsigned int ignored) { -#if defined(TARGET_NANOS) - UX_MENU_DISPLAY(0, menu_status, NULL); -#endif -} - -void view_tx_show(unsigned int start_page) { - if (ehGetData == NULL) { return; } - viewexpl_start(start_page, - ehGetData, - NULL, - view_tx_menu); - -} - -void view_addr_choose_update() { - print_title("Account %u", view_addr_choose_data.account); - print_key("Index %u", view_addr_choose_data.index); - - print_value("..."); - if (view_addr_choose_data.status.mode == VIEW_ADDR_MODE_SHOW) { - print_value("....?...."); - UX_DISPLAY(view_addr_choose, view_addr_choose_prepro); - UX_WAIT(); - } - - if (view_addr_choose_data.status.mode == VIEW_ADDR_MODE_SHOW || - view_addr_choose_data.status.mode == VIEW_ADDR_MODE_CONFIRM) { - print_value("This is a very long string that needs to be scrolled otherwise it does not fit"); - - bip32_depth = 5; - bip32_path[0] = BIP32_0_DEFAULT; - bip32_path[1] = BIP32_1_DEFAULT; - bip32_path[2] = 0x80000000 | view_addr_choose_data.account; - bip32_path[3] = BIP32_3_DEFAULT; - bip32_path[4] = view_addr_choose_data.index; - get_bech32_addr(viewctl.dataValue); - } - - viewctl_dataValue_split(); -} - -void view_addr_choose_refresh() { - UX_DISPLAY(view_addr_choose, view_addr_choose_prepro); -} - -void view_addr_choose_show(unsigned int _) { - // Initialize show view - view_addr_choose_data.status.mode = VIEW_ADDR_MODE_ACCOUNT; - view_addr_choose_data.account = 0; - view_addr_choose_data.index = 0; - strcpy(bech32_hrp, "cosmos"); - ehAccept = show_idle_menu; - ehReject = NULL; - - // Now show view - view_addr_choose_update(); - view_addr_choose_refresh(); -} - -void view_addr_confirm(unsigned int _) { - view_addr_choose_data.status.mode = VIEW_ADDR_MODE_CONFIRM; - view_addr_choose_data.account = BIP32_ACCOUNT; - view_addr_choose_data.index = BIP32_INDEX; - view_addr_choose_update(); - -#if defined(TARGET_NANOS) - view_addr_choose_refresh(); -#elif defined(TARGET_NANOX) - if(G_ux.stack_count == 0) { - ux_stack_push(); - } - ux_flow_init(0, ux_addr_flow, NULL); -#endif -} - -void view_tx_menu(unsigned int unused) { - UNUSED(unused); - -#if defined(TARGET_NANOS) - UX_MENU_DISPLAY(0, menu_transaction_info, NULL); -#elif defined(TARGET_NANOX) - if(G_ux.stack_count == 0) { - ux_stack_push(); - } - ux_flow_init(0, ux_tx_flow, NULL); -#endif -} - -void view_set_handlers(viewctl_delegate_getData func_getData, - viewctl_delegate_accept func_accept, - viewctl_delegate_reject func_reject) { - ehGetData = func_getData; - ehAccept = func_accept; - ehReject = func_reject; -} diff --git a/src/view_common.c b/src/view_common.c deleted file mode 100644 index 3ba520bb..00000000 --- a/src/view_common.c +++ /dev/null @@ -1,205 +0,0 @@ -/******************************************************************************* -* (c) 2016 Ledger -* (c) 2018, 2019 ZondaX GmbH -* -* Licensed under the Apache License, Version 2.0 (the "License"); -* you may not use this file except in compliance with the License. -* You may obtain a copy of the License at -* -* http://www.apache.org/licenses/LICENSE-2.0 -* -* Unless required by applicable law or agreed to in writing, software -* distributed under the License is distributed on an "AS IS" BASIS, -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -* See the License for the specific language governing permissions and -* limitations under the License. -********************************************************************************/ - -#include "view.h" -#include "view_templates.h" -#include "view_expl.h" - -#include "glyphs.h" -#include "bagl.h" -#include "zxmacros.h" - -#include -#include - -void viewctl_display_page(); - -const char *dblClickInfo = "DBL-CLICK TO VIEW"; - -viewctl_s viewctl; - -viewctl_delegate_getData viewctl_ehGetData = NULL; -viewctl_delegate_ready viewctl_ehReady = NULL; -viewctl_delegate_exit viewctl_ehExit = NULL; -viewctl_delegate_display_ux viewctl_display_ux = NULL; - -//------ External functions - -void viewctl_start(int start_page, - viewctl_delegate_getData func_getData, - viewctl_delegate_ready ehReady, - viewctl_delegate_exit ehExit, - viewctl_delegate_display_ux func_display_ux) { - // set handlers - viewctl_ehGetData = func_getData; - viewctl_ehReady = ehReady; - viewctl_ehExit = ehExit; - viewctl_display_ux = func_display_ux; - - // initialize variables - viewctl.scrolling_mode = PENDING; - viewctl.detailsCurrentPage = start_page; - viewctl.chunksIndex = 0; - viewctl.chunksCount = 1; - - viewctl_display_page(); - if (viewctl_ehReady != NULL) { - viewctl_ehReady(0); - } -} - -//------ Event handlers - -const bagl_element_t *ui_view_info_prepro(const bagl_element_t *element) { - - switch (element->component.userid) { - case 0x01: - UX_CALLBACK_SET_INTERVAL(2000); - break; - case 0x02: - UX_CALLBACK_SET_INTERVAL(MAX(3000, 1000 + bagl_label_roundtrip_duration_ms(element, 7))); - break; - } - return element; -} - -void submenu_left() { - viewctl.chunksIndex--; - viewctl.scrolling_mode = PENDING; - viewctl_display_page(); -} - -void submenu_right() { - viewctl.chunksIndex++; - viewctl.scrolling_mode = PENDING; - viewctl_display_page(); -} - -void menu_left() { - viewctl.scrolling_mode = PENDING; - viewctl.chunksIndex = 0; - viewctl.chunksCount = 1; - if (viewctl.detailsCurrentPage > 0) { - viewctl.detailsCurrentPage--; - viewctl_display_page(); - } else { - viewctl_ehExit(0); - } -} - -void menu_right() { - viewctl.scrolling_mode = PENDING; - viewctl.chunksIndex = 0; - viewctl.chunksCount = 1; - if (viewctl.detailsCurrentPage < viewctl.detailsPageCount - 1) { - viewctl.detailsCurrentPage++; - viewctl_display_page(); - } else { - viewctl_ehExit(0); - } -} - -void viewctl_crop_key() { - int offset = strlen((char *) viewctl.dataKey) - MAX_SCREEN_LINE_WIDTH; - if (offset > 0) { - char *start = (char *) viewctl.dataKey; - for (;;) { - *start = start[offset]; - if (*start++ == '\0') - break; - } - } -} - -void viewctl_dataValue_split() { -#if defined(TARGET_NANOX) - const int dataValueLen = strlen(viewctl.dataValue); - - int offset = 0; - for (int i = 0; i < MAX_SCREEN_NUM_LINES; i++) { - viewctl.dataValueChunk[i][0] = 0; // clean/terminate strings - if (offset < dataValueLen) { - snprintf((char *) viewctl.dataValueChunk[i], MAX_SCREEN_LINE_WIDTH, "%s", viewctl.dataValue + offset); - } - offset += (MAX_SCREEN_LINE_WIDTH - 1); - } -#endif -} - -void viewctl_display_page() { - if (viewctl_ehGetData == NULL) { - return; - } - - strcpy(viewctl.title, "?"); - strcpy(viewctl.dataKey, "?"); - strcpy(viewctl.dataValue, "?"); - - // Read key and value strings from json - viewctl_ehGetData( - (char *) viewctl.title, sizeof(viewctl.title), - (char *) viewctl.dataKey, sizeof(viewctl.dataKey), - (char *) viewctl.dataValue, sizeof(viewctl.dataValue), - viewctl.detailsCurrentPage, viewctl.chunksIndex, - &viewctl.detailsPageCount, &viewctl.chunksCount); - - // fix possible utf8 issues - asciify((char *) viewctl.title); - asciify((char *) viewctl.dataKey); - asciify((char *) viewctl.dataValue); - - if (viewctl.chunksCount > 0) { - // If value is very long, we split it into chunks - // and add chunk index/count information at the end of the key - if (viewctl.chunksCount > 1) { - int position = strlen((char *) viewctl.dataKey); - snprintf((char *) viewctl.dataKey + position, - sizeof(viewctl.dataKey) - position, - " %d/%d", - viewctl.chunksIndex + 1, - viewctl.chunksCount); - } - -#if defined(TARGET_NANOX) - viewctl_dataValue_split(); -#elif defined(TARGET_NANOS) - switch (viewctl.scrolling_mode) { - case KEY_SCROLLING_NO_VALUE: { - viewctl_crop_key(); - viewctl.scrolling_mode = VALUE_SCROLLING; - break; - } - case PENDING: { - viewctl.scrolling_mode = VALUE_SCROLLING; - if (strlen((char *) viewctl.dataKey) > MAX_SCREEN_LINE_WIDTH) { - int value_length = strlen((char *) viewctl.dataValue); - if (value_length > MAX_SCREEN_LINE_WIDTH) { - strcpy((char *) viewctl.dataValue, "DBL-CLICK FOR VALUE"); - viewctl.scrolling_mode = KEY_SCROLLING_NO_VALUE; - } else { - viewctl.scrolling_mode = KEY_SCROLLING_SHORT_VALUE; - } - } - } - default: - break; - } -#endif - } - - viewctl_display_ux(); -} diff --git a/src/view_common.h b/src/view_common.h deleted file mode 100644 index 07e40c46..00000000 --- a/src/view_common.h +++ /dev/null @@ -1,118 +0,0 @@ -/******************************************************************************* -* (c) 2016 Ledger -* (c) 2018, 2019 ZondaX GmbH -* -* Licensed under the Apache License, Version 2.0 (the "License"); -* you may not use this file except in compliance with the License. -* You may obtain a copy of the License at -* -* http://www.apache.org/licenses/LICENSE-2.0 -* -* Unless required by applicable law or agreed to in writing, software -* distributed under the License is distributed on an "AS IS" BASIS, -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -* See the License for the specific language governing permissions and -* limitations under the License. -********************************************************************************/ -#pragma once - -#include - -#include "os.h" -#include "cx.h" - -#define MAX_CHARS_TITLE 32 -#define MAX_CHARS_PER_KEY_LINE 64 -#define MAX_CHARS_PER_VALUE_LINE 192 -#define MAX_SCREEN_LINE_WIDTH 19 - -enum UI_DISPLAY_MODE { - VALUE_SCROLLING, - KEY_SCROLLING_NO_VALUE, - KEY_SCROLLING_SHORT_VALUE, - PENDING -}; - -#if defined(TARGET_NANOX) -#define MAX_SCREEN_NUM_LINES 4 -#endif - -typedef struct { - enum UI_DISPLAY_MODE scrolling_mode; - // Index of the currently displayed page - int16_t detailsCurrentPage; - // Total number of displayable pages - int16_t detailsPageCount; - - // When data goes beyond the limit, it will be split in chunks that - // that spread over several pages - // Index of currently displayed value chunk - int16_t chunksIndex; - // Total number of displayable value chunks - int16_t chunksCount; - - // DATA - char title[MAX_SCREEN_LINE_WIDTH]; - char dataKey[MAX_CHARS_PER_KEY_LINE]; - char dataValue[MAX_CHARS_PER_VALUE_LINE]; - -#if defined(TARGET_NANOX) - char dataValueChunk[MAX_SCREEN_NUM_LINES][MAX_SCREEN_LINE_WIDTH+1]; -#endif -} viewctl_s; - -extern viewctl_s viewctl; - -// Delegate to update contents -typedef int16_t (*viewctl_delegate_getData)( - char *title, int16_t max_title_length, - char *key, int16_t max_key_length, - char *value, int16_t max_value_length, - int16_t page_index, - int16_t chunk_index, - int16_t *page_count_out, - int16_t *chunk_count_out); - -// Delegate to handle exit view event -typedef void (*viewctl_delegate_exit)(unsigned int ignored); - -// Delegate to handle exit view event -typedef void (*viewctl_delegate_ready)(unsigned int ignored); - -// Delegate to handle exit view event -typedef void (*viewctl_delegate_display_ux)(); - -// Delegate to handle an accept event -typedef void (*viewctl_delegate_accept)(); - -// Delegate to handle a reject event -typedef void (*viewctl_delegate_reject)(); - -extern viewctl_delegate_getData viewctl_ehGetData; -extern viewctl_delegate_ready viewctl_ehReady; -extern viewctl_delegate_exit viewctl_ehExit; -extern viewctl_delegate_display_ux viewctl_display_ux; - -void viewctl_start(int start_page, - viewctl_delegate_getData ehUpdate, - viewctl_delegate_ready ehReady, - viewctl_delegate_exit ehExit, - viewctl_delegate_display_ux func_display_ux); - -void viewctl_display_page(); - -void submenu_left(); - -void submenu_right(); - -void menu_left(); - -void menu_right(); - -#define print_title(...) snprintf(viewctl.title, sizeof(viewctl.title), __VA_ARGS__) - -#define print_key(...) snprintf(viewctl.dataKey, sizeof(viewctl.dataKey), __VA_ARGS__) - -#define print_value(...) snprintf(viewctl.dataValue, sizeof(viewctl.dataValue), __VA_ARGS__) - -void viewctl_dataValue_split(); diff --git a/src/view_expl.c b/src/view_expl.c deleted file mode 100644 index ed80d6b0..00000000 --- a/src/view_expl.c +++ /dev/null @@ -1,197 +0,0 @@ -/******************************************************************************* -* (c) 2016 Ledger -* (c) 2018, 2019 ZondaX GmbH -* -* Licensed under the Apache License, Version 2.0 (the "License"); -* you may not use this file except in compliance with the License. -* You may obtain a copy of the License at -* -* http://www.apache.org/licenses/LICENSE-2.0 -* -* Unless required by applicable law or agreed to in writing, software -* distributed under the License is distributed on an "AS IS" BASIS, -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -* See the License for the specific language governing permissions and -* limitations under the License. -********************************************************************************/ - -#include "view.h" -#include "view_templates.h" -#include "view_expl.h" - -#include "glyphs.h" -#include "bagl.h" -#include "zxmacros.h" - -#include -#include - -#if defined(TARGET_NANOX) -static const bagl_element_t viewexpl_bagl[] = { - UI_BACKGROUND_LEFT_RIGHT_ICONS, - UI_LabelLine(UIID_LABEL+0, 0, 9 + UI_11PX * 0, UI_SCREEN_WIDTH, UI_11PX, UI_WHITE, UI_BLACK, (const char *) viewctl.title), - UI_LabelLine(UIID_LABEL+1, 0, 9 + UI_11PX * 1, UI_SCREEN_WIDTH, UI_11PX, UI_WHITE, UI_BLACK, (const char *) viewctl.dataKey), - UI_LabelLine(UIID_LABEL+2, 0, 9 + UI_11PX * 2, UI_SCREEN_WIDTH, UI_11PX, UI_WHITE, UI_BLACK, (const char *) viewctl.dataValueChunk[0]), - UI_LabelLine(UIID_LABEL+3, 0, 9 + UI_11PX * 3, UI_SCREEN_WIDTH, UI_11PX, UI_WHITE, UI_BLACK, (const char *) viewctl.dataValueChunk[1]), - UI_LabelLine(UIID_LABEL+4, 0, 9 + UI_11PX * 4, UI_SCREEN_WIDTH, UI_11PX, UI_WHITE, UI_BLACK, (const char *) viewctl.dataValueChunk[2]), - UI_LabelLine(UIID_LABEL+5, 0, 9 + UI_11PX * 5, UI_SCREEN_WIDTH, UI_11PX, UI_WHITE, UI_BLACK, (const char *) viewctl.dataValueChunk[3]), -}; - -static unsigned int viewexpl_bagl_button( - unsigned int button_mask, - unsigned int button_mask_counter) { - switch (button_mask) { - // Press both left and right to switch to value scrolling - case BUTTON_EVT_RELEASED | BUTTON_LEFT | BUTTON_RIGHT: { - if (viewctl.scrolling_mode == KEY_SCROLLING_NO_VALUE) { - viewctl_display_page(); - } else { - viewctl_ehExit(0); - } - break; - } - - // Press left to progress to the previous element - case BUTTON_EVT_RELEASED | BUTTON_LEFT: { - if (viewctl.chunksIndex > 0) { - submenu_left(); - } else { - menu_left(); - } - break; - } - - // Hold left to progress to the previous element quickly - // It also steps out from the value chunk page view mode - case BUTTON_EVT_FAST | BUTTON_LEFT: { - menu_left(); - break; - } - - // Press right to progress to the next element - case BUTTON_EVT_RELEASED | BUTTON_RIGHT: { - if (viewctl.chunksIndex < viewctl.chunksCount - 1) { - submenu_right(); - } else { - menu_right(); - } - break; - } - - // Hold right to progress to the next element quickly - // It also steps out from the value chunk page view mode - case BUTTON_EVT_FAST | BUTTON_RIGHT: { - menu_right(); - break; - } - } - return 0; -} - -#elif defined(TARGET_NANOS) -static const bagl_element_t viewexpl_bagl_valuescrolling[] = { - UI_BACKGROUND_LEFT_RIGHT_ICONS, - UI_LabelLine(UIID_LABEL + 0, 0, 8, UI_SCREEN_WIDTH, UI_11PX, UI_WHITE, UI_BLACK, (const char *) viewctl.title), - UI_LabelLine(UIID_LABEL + 1, 0, 19, UI_SCREEN_WIDTH, UI_11PX, UI_WHITE, UI_BLACK, (const char *) viewctl.dataKey), - UI_LabelLineScrolling(UIID_LABELSCROLL, 16, 30, 96, UI_11PX, UI_WHITE, UI_BLACK, (const char *) viewctl.dataValue), -}; - -static const bagl_element_t viewexpl_bagl_keyscrolling[] = { - UI_BACKGROUND_LEFT_RIGHT_ICONS, - UI_LabelLine(UIID_LABEL + 0, 0, 8, UI_SCREEN_WIDTH, UI_11PX, UI_WHITE, UI_BLACK, (const char *) viewctl.title), - UI_LabelLine(UIID_LABEL + 1, 0, 30, UI_SCREEN_WIDTH, UI_11PX, UI_WHITE, UI_BLACK, (const char *) viewctl.dataValue), - UI_LabelLineScrolling(UIID_LABELSCROLL, 16, 19, 96, UI_11PX, UI_WHITE, UI_BLACK, (const char *) viewctl.dataKey), -}; - -static unsigned int viewexpl_bagl_keyscrolling_button( - unsigned int button_mask, - unsigned int button_mask_counter) { - switch (button_mask) { - // Press both left and right to switch to value scrolling - case BUTTON_EVT_RELEASED | BUTTON_LEFT | BUTTON_RIGHT: { - if (viewctl.scrolling_mode == KEY_SCROLLING_NO_VALUE) { - viewctl_display_page(); - } else { - viewctl_ehExit(0); - } - break; - } - - // Press left to progress to the previous element - case BUTTON_EVT_RELEASED | BUTTON_LEFT: { - if (viewctl.chunksIndex > 0) { - submenu_left(); - } else { - menu_left(); - } - break; - } - - // Hold left to progress to the previous element quickly - // It also steps out from the value chunk page view mode - case BUTTON_EVT_FAST | BUTTON_LEFT: { - menu_left(); - break; - } - - // Press right to progress to the next element - case BUTTON_EVT_RELEASED | BUTTON_RIGHT: { - if (viewctl.chunksIndex < viewctl.chunksCount - 1) { - submenu_right(); - } else { - menu_right(); - } - break; - } - - // Hold right to progress to the next element quickly - // It also steps out from the value chunk page view mode - case BUTTON_EVT_FAST | BUTTON_RIGHT: { - menu_right(); - break; - } - } - return 0; -} - -static unsigned int viewexpl_bagl_valuescrolling_button( - unsigned int button_mask, - unsigned int button_mask_counter) { - return viewexpl_bagl_keyscrolling_button(button_mask, button_mask_counter); -} - -#endif - -const bagl_element_t *viewexpl_bagl_prepro(const bagl_element_t *element) { - switch (element->component.userid) { - case UIID_ICONLEFT: - UX_CALLBACK_SET_INTERVAL(2000); - break; - case UIID_ICONRIGHT: - UX_CALLBACK_SET_INTERVAL(2000); - break; - case UIID_LABELSCROLL: - UX_CALLBACK_SET_INTERVAL(MAX(3000, 1000 + bagl_label_roundtrip_duration_ms(element, 7))); - break; - } - return element; -} - -void viewexpl_display_ux() { -#if defined(TARGET_NANOX) - UX_DISPLAY(viewexpl_bagl, viewexpl_bagl_prepro); -#else - if (viewctl.scrolling_mode == VALUE_SCROLLING) { - UX_DISPLAY(viewexpl_bagl_valuescrolling, viewexpl_bagl_prepro); - } else { - UX_DISPLAY(viewexpl_bagl_keyscrolling, viewexpl_bagl_prepro); - } -#endif -} - -void viewexpl_start(int start_page, - viewctl_delegate_getData ehUpdate, - viewctl_delegate_ready ehReady, - viewctl_delegate_exit ehExit) { - viewctl_start(start_page, ehUpdate, ehReady, ehExit, viewexpl_display_ux); -} diff --git a/tests/json_parser.cpp b/tests/json_parser.cpp new file mode 100644 index 00000000..99e3ca5a --- /dev/null +++ b/tests/json_parser.cpp @@ -0,0 +1,391 @@ +/******************************************************************************* +* (c) 2018 Zondax GmbH +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +********************************************************************************/ + +#include "gtest/gtest.h" +#include "util/common.h" +#include +#include + +namespace { + TEST(JsonParserTest, Empty) { + parsed_json_t parserData = {false}; + JSON_PARSE(&parserData, ""); + + EXPECT_FALSE(parserData.isValid); + EXPECT_EQ(0, parserData.numberOfTokens); + } + + TEST(JsonParserTest, SinglePrimitive) { + parsed_json_t parserData = {false}; + JSON_PARSE(&parserData, "EMPTY"); + + EXPECT_TRUE(parserData.isValid); + EXPECT_EQ(1, parserData.numberOfTokens); + EXPECT_TRUE(parserData.tokens[0].type == jsmntype_t::JSMN_PRIMITIVE); + } + + TEST(JsonParserTest, KeyValuePrimitives) { + parsed_json_t parserData = {false}; + JSON_PARSE(&parserData, "KEY : VALUE"); + + EXPECT_TRUE(parserData.isValid); + EXPECT_EQ(2, parserData.numberOfTokens); + EXPECT_TRUE(parserData.tokens[0].type == jsmntype_t::JSMN_PRIMITIVE); + EXPECT_TRUE(parserData.tokens[1].type == jsmntype_t::JSMN_PRIMITIVE); + } + + TEST(JsonParserTest, SingleString) { + parsed_json_t parserData = {false}; + JSON_PARSE(&parserData, "\"EMPTY\""); + + EXPECT_TRUE(parserData.isValid); + EXPECT_EQ(1, parserData.numberOfTokens); + EXPECT_TRUE(parserData.tokens[0].type == jsmntype_t::JSMN_STRING); + } + + TEST(JsonParserTest, KeyValueStrings) { + parsed_json_t parserData = {false}; + JSON_PARSE(&parserData, R"("KEY" : "VALUE")"); + + EXPECT_TRUE(parserData.isValid); + EXPECT_EQ(2, parserData.numberOfTokens); + EXPECT_TRUE(parserData.tokens[0].type == jsmntype_t::JSMN_STRING); + EXPECT_TRUE(parserData.tokens[1].type == jsmntype_t::JSMN_STRING); + } + + TEST(JsonParserTest, SimpleArray) { + parsed_json_t parserData = {false}; + JSON_PARSE(&parserData, "LIST : [1, 2, 3, 4]"); + + EXPECT_TRUE(parserData.isValid); + EXPECT_EQ(6, parserData.numberOfTokens); + EXPECT_TRUE(parserData.tokens[0].type == jsmntype_t::JSMN_PRIMITIVE); + EXPECT_TRUE(parserData.tokens[1].type == jsmntype_t::JSMN_ARRAY); + EXPECT_TRUE(parserData.tokens[2].type == jsmntype_t::JSMN_PRIMITIVE); + EXPECT_TRUE(parserData.tokens[3].type == jsmntype_t::JSMN_PRIMITIVE); + EXPECT_TRUE(parserData.tokens[4].type == jsmntype_t::JSMN_PRIMITIVE); + EXPECT_TRUE(parserData.tokens[5].type == jsmntype_t::JSMN_PRIMITIVE); + } + + TEST(JsonParserTest, MixedArray) { + parsed_json_t parserData = {false}; + JSON_PARSE(&parserData, R"(LIST : [1, "Text", 3, "Another text"])"); + + EXPECT_TRUE(parserData.isValid); + EXPECT_EQ(6, parserData.numberOfTokens); + EXPECT_TRUE(parserData.tokens[0].type == jsmntype_t::JSMN_PRIMITIVE); + EXPECT_TRUE(parserData.tokens[1].type == jsmntype_t::JSMN_ARRAY); + EXPECT_TRUE(parserData.tokens[2].type == jsmntype_t::JSMN_PRIMITIVE); + EXPECT_TRUE(parserData.tokens[3].type == jsmntype_t::JSMN_STRING); + EXPECT_TRUE(parserData.tokens[4].type == jsmntype_t::JSMN_PRIMITIVE); + EXPECT_TRUE(parserData.tokens[5].type == jsmntype_t::JSMN_STRING); + } + + TEST(JsonParserTest, SimpleObject) { + parsed_json_t parserData = {false}; + JSON_PARSE(&parserData, "vote : " + "{ " + "\"key\" : \"value\", " + "\"another key\" : { " + "\"inner key\" : \"inner value\", " + "\"total\":123 }" + "}"); + + EXPECT_TRUE(parserData.isValid); + EXPECT_EQ(10, parserData.numberOfTokens); + EXPECT_TRUE(parserData.tokens[0].type == jsmntype_t::JSMN_PRIMITIVE); + EXPECT_TRUE(parserData.tokens[1].type == jsmntype_t::JSMN_OBJECT); + EXPECT_TRUE(parserData.tokens[2].type == jsmntype_t::JSMN_STRING); + EXPECT_TRUE(parserData.tokens[3].type == jsmntype_t::JSMN_STRING); + EXPECT_TRUE(parserData.tokens[4].type == jsmntype_t::JSMN_STRING); + EXPECT_TRUE(parserData.tokens[5].type == jsmntype_t::JSMN_OBJECT); + EXPECT_TRUE(parserData.tokens[6].type == jsmntype_t::JSMN_STRING); + EXPECT_TRUE(parserData.tokens[7].type == jsmntype_t::JSMN_STRING); + EXPECT_TRUE(parserData.tokens[8].type == jsmntype_t::JSMN_STRING); + EXPECT_TRUE(parserData.tokens[9].type == jsmntype_t::JSMN_PRIMITIVE); + } + + TEST(JsonParserTest, ArrayElementCount_objects) { + auto transaction = + R"({"array":[{"amount":5,"denom":"photon"}, {"amount":5,"denom":"photon"}, {"amount":5,"denom":"photon"}]})"; + + parsed_json_t parsed_json; + JSON_PARSE(&parsed_json, transaction); + + uint16_t token; + EXPECT_EQ(array_get_element_count(&parsed_json, 2, &token), parser_ok); + EXPECT_EQ(token, 3) << "Wrong number of array elements"; + } + + TEST(JsonParserTest, ArrayElementCount_primitives) { + auto transaction = R"({"array":[1, 2, 3, 4, 5, 6, 7]})"; + + parsed_json_t parsed_json; + JSON_PARSE(&parsed_json, transaction); + + uint16_t token; + EXPECT_EQ(array_get_element_count(&parsed_json, 2, &token), parser_ok); + EXPECT_EQ(token, 7) << "Wrong number of array elements"; + } + + TEST(JsonParserTest, ArrayElementCount_strings) { + auto transaction = R"({"array":["hello", "there"]})"; + + parsed_json_t parsed_json; + JSON_PARSE(&parsed_json, transaction); + + uint16_t token; + EXPECT_EQ(array_get_element_count(&parsed_json, 2, &token), parser_ok); + EXPECT_EQ(token, 2) << "Wrong number of array elements"; + } + + TEST(JsonParserTest, ArrayElementCount_empty) { + auto transaction = R"({"array":[])"; + + parsed_json_t parsed_json; + JSON_PARSE(&parsed_json, transaction); + + uint16_t token; + EXPECT_EQ(array_get_element_count(&parsed_json, 2, &token), parser_no_data); + } + + TEST(JsonParserTest, ArrayElementGet_objects) { + auto transaction = + R"({"array":[{"amount":5,"denom":"photon"}, {"amount":5,"denom":"photon"}, {"amount":5,"denom":"photon"}]})"; + + parsed_json_t parsed_json; + JSON_PARSE(&parsed_json, transaction); + + uint16_t token_index; + EXPECT_EQ(array_get_nth_element(&parsed_json, 2, 1, &token_index), parser_ok); + EXPECT_EQ(token_index, 8) << "Wrong token index returned"; + EXPECT_EQ(parsed_json.tokens[token_index].type, JSMN_OBJECT) << "Wrong token type returned"; + } + + TEST(JsonParserTest, ArrayElementGet_primitives) { + auto transaction = R"({"array":[1, 2, 3, 4, 5, 6, 7]})"; + + parsed_json_t parsed_json; + JSON_PARSE(&parsed_json, transaction); + + uint16_t token_index; + EXPECT_EQ(array_get_nth_element(&parsed_json, 2, 5, &token_index), parser_ok); + EXPECT_EQ(token_index, 8) << "Wrong token index returned"; + EXPECT_EQ(parsed_json.tokens[token_index].type, JSMN_PRIMITIVE) << "Wrong token type returned"; + } + + TEST(TxValidationTest, ArrayElementGet_strings) { + auto transaction = R"({"array":["hello", "there"]})"; + + parsed_json_t parsed_json; + JSON_PARSE(&parsed_json, transaction); + + uint16_t token_index; + EXPECT_EQ(array_get_nth_element(&parsed_json, 2, 0, &token_index), parser_ok); + EXPECT_EQ(token_index, 3) << "Wrong token index returned"; + EXPECT_EQ(parsed_json.tokens[token_index].type, JSMN_STRING) << "Wrong token type returned"; + } + + TEST(TxValidationTest, ArrayElementGet_empty) { + auto transaction = R"({"array":[])"; + + parsed_json_t parsed_json; + JSON_PARSE(&parsed_json, transaction); + + uint16_t token_index; + EXPECT_EQ(array_get_nth_element(&parsed_json, 2, 0, &token_index), parser_no_data) + << "Token index should be invalid (not found)."; + } + + TEST(TxValidationTest, ArrayElementGet_out_of_bounds_negative) { + auto transaction = R"({"array":["hello", "there"])"; + + parsed_json_t parsed_json; + JSON_PARSE(&parsed_json, transaction); + + uint16_t token_index; + EXPECT_EQ(array_get_nth_element(&parsed_json, 2, -1, &token_index), parser_no_data) + << "Token index should be invalid (not found)."; + } + + TEST(TxValidationTest, ArrayElementGet_out_of_bounds) { + auto transaction = R"({"array":["hello", "there"])"; + + parsed_json_t parsed_json; + JSON_PARSE(&parsed_json, transaction); + + uint16_t token_index; + EXPECT_EQ(array_get_nth_element(&parsed_json, 2, 3, &token_index), parser_no_data) + << "Token index should be invalid (not found)."; + } + + TEST(TxValidationTest, ObjectElementCount_primitives) { + auto transaction = R"({"age":36, "height":185, "year":1981})"; + + parsed_json_t parsed_json; + JSON_PARSE(&parsed_json, transaction); + + uint16_t count; + EXPECT_EQ(object_get_element_count(&parsed_json, 0, &count), parser_ok); + EXPECT_EQ(count, 3) << "Wrong number of object elements"; + } + + TEST(TxValidationTest, ObjectElementCount_string) { + auto transaction = R"({"age":"36", "height":"185", "year":"1981", "month":"july"})"; + + parsed_json_t parsed_json; + JSON_PARSE(&parsed_json, transaction); + + uint16_t count; + EXPECT_EQ(object_get_element_count(&parsed_json, 0, &count), parser_ok); + EXPECT_EQ(count, 4) << "Wrong number of object elements"; + } + + TEST(TxValidationTest, ObjectElementCount_array) { + auto transaction = R"({ "ages":[36, 31, 10, 2], + "heights":[185, 164, 154, 132], + "years":[1981, 1985, 2008, 2016], + "months":["july", "august", "february", "july"]})"; + + parsed_json_t parsed_json; + JSON_PARSE(&parsed_json, transaction); + + uint16_t count; + EXPECT_EQ(object_get_element_count(&parsed_json, 0, &count), parser_ok); + EXPECT_EQ(count, 4) << "Wrong number of object elements"; + } + + TEST(TxValidationTest, ObjectElementCount_object) { + auto transaction = R"({"person1":{"age":36, "height":185, "year":1981}, + "person2":{"age":36, "height":185, "year":1981}, + "person3":{"age":36, "height":185, "year":1981}})"; + + parsed_json_t parsed_json; + JSON_PARSE(&parsed_json, transaction); + + uint16_t count; + EXPECT_EQ(object_get_element_count(&parsed_json, 0, &count), parser_ok); + EXPECT_EQ(count, 3) << "Wrong number of object elements"; + } + + TEST(TxValidationTest, ObjectElementCount_deep) { + auto transaction = R"({"person1":{"age":{"age":36, "height":185, "year":1981}, "height":{"age":36, "height":185, "year":1981}, "year":1981}, + "person2":{"age":{"age":36, "height":185, "year":1981}, "height":{"age":36, "height":185, "year":1981}, "year":1981}, + "person3":{"age":{"age":36, "height":185, "year":1981}, "height":{"age":36, "height":185, "year":1981}, "year":1981}})"; + + parsed_json_t parsed_json; + JSON_PARSE(&parsed_json, transaction); + + uint16_t count; + EXPECT_EQ(object_get_element_count(&parsed_json, 0, &count), parser_ok); + EXPECT_EQ(count, 3) << "Wrong number of object elements"; + } + + TEST(TxValidationTest, ObjectElementGet_primitives) { + auto transaction = R"({"age":36, "height":185, "year":1981})"; + + parsed_json_t parsed_json; + JSON_PARSE(&parsed_json, transaction); + + uint16_t token_index; + EXPECT_EQ(object_get_nth_key(&parsed_json, 0, 0, &token_index), parser_ok); + EXPECT_EQ(token_index, 1) << "Wrong token index"; + EXPECT_EQ(parsed_json.tokens[token_index].type, JSMN_STRING) << "Wrong token type returned"; + EXPECT_EQ(memcmp(transaction + parsed_json.tokens[token_index].start, "age", strlen("age")), 0) + << "Wrong key returned"; + } + + TEST(TxValidationTest, ObjectElementGet_string) { + auto transaction = R"({"age":"36", "height":"185", "year":"1981", "month":"july"})"; + + parsed_json_t parsed_json; + JSON_PARSE(&parsed_json, transaction); + + uint16_t token_index; + EXPECT_EQ(object_get_nth_value(&parsed_json, 0, 3, &token_index), parser_ok); + EXPECT_EQ(token_index, 8) << "Wrong token index"; + EXPECT_EQ(parsed_json.tokens[token_index].type, JSMN_STRING) << "Wrong token type returned"; + EXPECT_EQ(memcmp(transaction + parsed_json.tokens[token_index].start, "july", strlen("july")), 0) + << "Wrong key returned"; + } + + TEST(TxValidationTest, ObjectElementGet_out_of_bounds_negative) { + auto transaction = R"({"age":36, "height":185, "year":1981})"; + + parsed_json_t parsed_json; + JSON_PARSE(&parsed_json, transaction); + + uint16_t token_index; + EXPECT_EQ(object_get_nth_key(&parsed_json, 0, -1, &token_index), parser_no_data) + << "Wrong token index, should be invalid"; + } + + TEST(TxValidationTest, ObjectElementGet_out_of_bounds) { + auto transaction = R"({"age":36, "height":185, "year":1981})"; + + parsed_json_t parsed_json; + JSON_PARSE(&parsed_json, transaction); + + uint16_t token_index; + EXPECT_EQ(object_get_nth_key(&parsed_json, 0, 5, &token_index), parser_no_data) + << "Wrong token index, should be invalid"; + } + + TEST(TxValidationTest, ObjectElementGet_array) { + auto transaction = R"({ "ages":[36, 31, 10, 2], + "heights":[185, 164, 154, 132], + "years":[1981, 1985, 2008, 2016, 2022], + "months":["july", "august", "february", "july"]})"; + + parsed_json_t parsed_json; + JSON_PARSE(&parsed_json, transaction); + + uint16_t token_index; + EXPECT_EQ(object_get_value(&parsed_json, 0, "years", &token_index), parser_ok); + + EXPECT_EQ(token_index, 14) << "Wrong token index"; + EXPECT_EQ(parsed_json.tokens[token_index].type, JSMN_ARRAY) << "Wrong token type returned"; + uint16_t number_elements; + EXPECT_EQ(array_get_element_count(&parsed_json, token_index, &number_elements), parser_ok); + EXPECT_EQ(number_elements, 5) << "Wrong number of array elements"; + } + + TEST(TxValidationTest, ObjectGetValueCorrectFormat) { + auto transaction = + R"({"account_number":"0","chain_id":"test-chain-1","fee":{"amount":[{"amount":"5","denom":"photon"}],"gas":"10000"},"memo":"testmemo","msgs":[{"inputs":[{"address":"cosmosaccaddr1d9h8qat5e4ehc5","coins":[{"amount":"10","denom":"atom"}]}],"outputs":[{"address":"cosmosaccaddr1da6hgur4wse3jx32","coins":[{"amount":"10","denom":"atom"}]}]}],"sequence":"1"})"; + parsed_json_t parsed_json; + JSON_PARSE(&parsed_json, transaction); + + uint16_t token_index; + EXPECT_EQ(object_get_value(&parsed_json, 0, "alt_bytes", &token_index), parser_no_data) + << "Wrong token index"; // alt_bytes should not be found + + EXPECT_EQ(object_get_value(&parsed_json, 0, "account_number", &token_index), parser_ok); + EXPECT_EQ(token_index, 2) << "Wrong token index"; // alt_bytes should not be found + + EXPECT_EQ(object_get_value(&parsed_json, 0, "chain_id", &token_index), parser_ok); + EXPECT_EQ(token_index, 4) << "Wrong token index"; + + EXPECT_EQ(object_get_value(&parsed_json, 0, "fee", &token_index), parser_ok); + EXPECT_EQ(token_index, 6) << "Wrong token index"; + + EXPECT_EQ(object_get_value(&parsed_json, 0, "msgs", &token_index), parser_ok); + EXPECT_EQ(token_index, 19) << "Wrong token index"; + + EXPECT_EQ(object_get_value(&parsed_json, 0, "sequence", &token_index), parser_ok); + EXPECT_EQ(token_index, 46) << "Wrong token index"; + } +} diff --git a/tests/testcases/manual.json b/tests/testcases/manual.json new file mode 100644 index 00000000..32d27a06 --- /dev/null +++ b/tests/testcases/manual.json @@ -0,0 +1,1521 @@ +[ + { + "name": "empty", + "tx": "", + "parsingErr": "No error", + "validationErr": "JSON Missing chain_id", + "expected": [], + "expert": true + }, + { + "name": "minimal", + "tx": { + "account_number": "V1", + "chain_id": "V2", + "fee": { + "amount": [ + { + "amount": "b" + }, + { + "c": "d" + }, + { + "x": "y" + } + ], + "gas": "V3" + }, + "memo": "V4", + "msgs": [], + "sequence": "V5" + }, + "parsingErr": "No error", + "validationErr": "Unexpected field", + "expected": [ + "0 | Chain ID : V2", + "1 | Account : V1", + "2 | Sequence : V5", + "3 | Memo : V4", + "4 | fee/amount : Unexpected field", + "5 | Gas : V3" + ], + "expert": true + }, + { + "name": "multipleMessages", + "tx": { + "account_number": "V1", + "chain_id": "V2", + "fee": { + "amount": [ + { + "amount": "b", + "denom": "d" + } + ], + "gas": "V3" + }, + "memo": "V4", + "msgs": [ + { + "m1": "z1" + }, + { + "m2": "z2" + }, + { + "m3": "z3" + } + ], + "sequence": "V5" + }, + "parsingErr": "No error", + "validationErr": "No error", + "expected": [ + "0 | Chain ID : V2", + "1 | Account : V1", + "2 | Sequence : V5", + "3 | msgs/m1 : z1", + "4 | msgs/m2 : z2", + "5 | msgs/m3 : z3", + "6 | Memo : V4", + "7 | Fee : b d", + "8 | Gas : V3" + ], + "expert": true + }, + { + "name": "completeTransferExpert", + "tx": { + "account_number": "0", + "chain_id": "cosmoshub-3", + "fee": { + "amount": [ + { + "amount": "5", + "denom": "photon" + } + ], + "gas": "10000" + }, + "memo": "testmemo", + "msgs": [ + { + "inputs": [ + { + "address": "cosmosaccaddr1d9h8qat5e4ehc5", + "coins": [ + { + "amount": "10", + "denom": "atom" + } + ] + } + ], + "outputs": [ + { + "address": "cosmosaccaddr1da6hgur4wse3jx32", + "coins": [ + { + "amount": "10", + "denom": "atom" + } + ] + } + ] + } + ], + "sequence": "1" + }, + "parsingErr": "No error", + "validationErr": "No error", + "expected": [ + "0 | Chain ID : cosmoshub-3", + "1 | Account : 0", + "2 | Sequence : 1", + "3 | Source Address : cosmosaccaddr1d9h8qat5e4ehc5", + "4 | Source Coins : 10 atom", + "5 | Dest Address : cosmosaccaddr1da6hgur4wse3jx32", + "6 | Dest Coins : 10 atom", + "7 | Memo : testmemo", + "8 | Fee : 5 photon", + "9 | Gas : 10000" + ], + "expert": true + }, + { + "name": "completeTransfer", + "tx": { + "account_number": "0", + "chain_id": "cosmoshub-3", + "fee": { + "amount": [ + { + "amount": "5", + "denom": "photon" + } + ], + "gas": "10000" + }, + "memo": "testmemo", + "msgs": [ + { + "inputs": [ + { + "address": "cosmosaccaddr1d9h8qat5e4ehc5", + "coins": [ + { + "amount": "10", + "denom": "atom" + } + ] + } + ], + "outputs": [ + { + "address": "cosmosaccaddr1da6hgur4wse3jx32", + "coins": [ + { + "amount": "10", + "denom": "atom" + } + ] + } + ] + } + ], + "sequence": "1" + }, + "parsingErr": "No error", + "validationErr": "No error", + "expected": [ + "0 | Source Address : cosmosaccaddr1d9h8qat5e4ehc5", + "1 | Source Coins : 10 atom", + "2 | Dest Address : cosmosaccaddr1da6hgur4wse3jx32", + "3 | Dest Coins : 10 atom", + "4 | Memo : testmemo", + "5 | Fee : 5 photon" + ], + "expert": false + }, + { + "name": "completeTransferNoMemoExpert", + "tx": { + "account_number": "0", + "chain_id": "test-chain-1", + "fee": { + "amount": [ + { + "amount": "5", + "denom": "photon" + } + ], + "gas": "10000" + }, + "msgs": [ + { + "inputs": [ + { + "address": "cosmosaccaddr1d9h8qat5e4ehc5", + "coins": [ + { + "amount": "10", + "denom": "atom" + } + ] + } + ], + "outputs": [ + { + "address": "cosmosaccaddr1da6hgur4wse3jx32", + "coins": [ + { + "amount": "10", + "denom": "atom" + } + ] + } + ] + } + ], + "sequence": "1" + }, + "parsingErr": "No error", + "validationErr": "JSON Missing memo", + "expected": [ + "0 | Chain ID : test-chain-1", + "1 | Account : 0", + "2 | Sequence : 1", + "3 | Source Address : cosmosaccaddr1d9h8qat5e4ehc5", + "4 | Source Coins : 10 atom", + "5 | Dest Address : cosmosaccaddr1da6hgur4wse3jx32", + "6 | Dest Coins : 10 atom", + "7 | Fee : 5 photon", + "8 | Gas : 10000" + ], + "expert": true + }, + { + "name": "completeTransferNoMemo", + "tx": { + "account_number": "0", + "chain_id": "cosmoshub-3", + "fee": { + "amount": [ + { + "amount": "5", + "denom": "photon" + } + ], + "gas": "10000" + }, + "msgs": [ + { + "inputs": [ + { + "address": "cosmosaccaddr1d9h8qat5e4ehc5", + "coins": [ + { + "amount": "10", + "denom": "atom" + } + ] + } + ], + "outputs": [ + { + "address": "cosmosaccaddr1da6hgur4wse3jx32", + "coins": [ + { + "amount": "10", + "denom": "atom" + } + ] + } + ] + } + ], + "sequence": "1" + }, + "parsingErr": "No error", + "validationErr": "JSON Missing memo", + "expected": [ + "0 | Source Address : cosmosaccaddr1d9h8qat5e4ehc5", + "1 | Source Coins : 10 atom", + "2 | Dest Address : cosmosaccaddr1da6hgur4wse3jx32", + "3 | Dest Coins : 10 atom", + "4 | Fee : 5 photon" + ], + "expert": false + }, + { + "name": "completeTransferEmptyMemoExpert", + "tx": { + "account_number": "0", + "chain_id": "test-chain-1", + "fee": { + "amount": [ + { + "amount": "5", + "denom": "photon" + } + ], + "gas": "10000" + }, + "memo": "", + "msgs": [ + { + "inputs": [ + { + "address": "cosmosaccaddr1d9h8qat5e4ehc5", + "coins": [ + { + "amount": "10", + "denom": "atom" + } + ] + } + ], + "outputs": [ + { + "address": "cosmosaccaddr1da6hgur4wse3jx32", + "coins": [ + { + "amount": "10", + "denom": "atom" + } + ] + } + ] + } + ], + "sequence": "1" + }, + "parsingErr": "No error", + "validationErr": "No error", + "expected": [ + "0 | Chain ID : test-chain-1", + "1 | Account : 0", + "2 | Sequence : 1", + "3 | Source Address : cosmosaccaddr1d9h8qat5e4ehc5", + "4 | Source Coins : 10 atom", + "5 | Dest Address : cosmosaccaddr1da6hgur4wse3jx32", + "6 | Dest Coins : 10 atom", + "7 | Fee : 5 photon", + "8 | Gas : 10000" + ], + "expert": true + }, + { + "name": "completeTransferEmptyMemo", + "tx": { + "account_number": "0", + "chain_id": "cosmoshub-3", + "fee": { + "amount": [ + { + "amount": "5", + "denom": "photon" + } + ], + "gas": "10000" + }, + "memo": "", + "msgs": [ + { + "inputs": [ + { + "address": "cosmosaccaddr1d9h8qat5e4ehc5", + "coins": [ + { + "amount": "10", + "denom": "atom" + } + ] + } + ], + "outputs": [ + { + "address": "cosmosaccaddr1da6hgur4wse3jx32", + "coins": [ + { + "amount": "10", + "denom": "atom" + } + ] + } + ] + } + ], + "sequence": "1" + }, + "parsingErr": "No error", + "validationErr": "No error", + "expected": [ + "0 | Source Address : cosmosaccaddr1d9h8qat5e4ehc5", + "1 | Source Coins : 10 atom", + "2 | Dest Address : cosmosaccaddr1da6hgur4wse3jx32", + "3 | Dest Coins : 10 atom", + "4 | Fee : 5 photon" + ], + "expert": false + }, + { + "name": "multipleTransfersExpert", + "tx": { + "account_number": "0", + "chain_id": "test-chain-1", + "fee": { + "amount": [ + { + "amount": "5", + "denom": "photon" + } + ], + "gas": "10000" + }, + "memo": "testmemo", + "msgs": [ + { + "inputs": [ + { + "address": "cosmosaccaddr1d9h8qat5e4ehc5", + "coins": [ + { + "amount": "10", + "denom": "atom" + } + ] + } + ], + "outputs": [ + { + "address": "cosmosaccaddr1da6hgur4wse3jx32", + "coins": [ + { + "amount": "10", + "denom": "atom" + } + ] + } + ] + }, + { + "inputs": [ + { + "address": "test2", + "coins": [ + { + "amount": "20", + "denom": "bitcoin" + } + ] + } + ], + "outputs": [ + { + "address": "test3", + "coins": [ + { + "amount": "50", + "denom": "ripple" + } + ] + } + ] + } + ], + "sequence": "1" + }, + "parsingErr": "No error", + "validationErr": "No error", + "expected": [ + "0 | Chain ID : test-chain-1", + "1 | Account : 0", + "2 | Sequence : 1", + "3 | Source Address : cosmosaccaddr1d9h8qat5e4ehc5", + "4 | Source Coins : 10 atom", + "5 | Dest Address : cosmosaccaddr1da6hgur4wse3jx32", + "6 | Dest Coins : 10 atom", + "7 | Source Address : test2", + "8 | Source Coins : 20 bitcoin", + "9 | Dest Address : test3", + "10 | Dest Coins : 50 ripple", + "11 | Memo : testmemo", + "12 | Fee : 5 photon", + "13 | Gas : 10000" + ], + "expert": true + }, + { + "name": "multipleTransfers", + "tx": { + "account_number": "0", + "chain_id": "cosmoshub-3", + "fee": { + "amount": [ + { + "amount": "5", + "denom": "photon" + } + ], + "gas": "10000" + }, + "memo": "testmemo", + "msgs": [ + { + "inputs": [ + { + "address": "cosmosaccaddr1d9h8qat5e4ehc5", + "coins": [ + { + "amount": "10", + "denom": "atom" + } + ] + } + ], + "outputs": [ + { + "address": "cosmosaccaddr1da6hgur4wse3jx32", + "coins": [ + { + "amount": "10", + "denom": "atom" + } + ] + } + ] + }, + { + "inputs": [ + { + "address": "test2", + "coins": [ + { + "amount": "20", + "denom": "bitcoin" + } + ] + } + ], + "outputs": [ + { + "address": "test3", + "coins": [ + { + "amount": "50", + "denom": "ripple" + } + ] + } + ] + } + ], + "sequence": "1" + }, + "parsingErr": "No error", + "validationErr": "No error", + "expected": [ + "0 | Source Address : cosmosaccaddr1d9h8qat5e4ehc5", + "1 | Source Coins : 10 atom", + "2 | Dest Address : cosmosaccaddr1da6hgur4wse3jx32", + "3 | Dest Coins : 10 atom", + "4 | Source Address : test2", + "5 | Source Coins : 20 bitcoin", + "6 | Dest Address : test3", + "7 | Dest Coins : 50 ripple", + "8 | Memo : testmemo", + "9 | Fee : 5 photon" + ], + "expert": false + }, + { + "name": "delegationExpert", + "tx": { + "account_number": "6571", + "chain_id": "cosmoshub-3", + "fee": { + "amount": [ + { + "amount": "5000", + "denom": "uatom" + } + ], + "gas": "200000" + }, + "memo": "Zondax.ch", + "msgs": [ + { + "type": "cosmos-sdk/MsgDelegate", + "value": { + "amount": { + "amount": "1000000", + "denom": "uatom" + }, + "delegator_address": "cosmos102hty0jv2s29lyc4u0tv97z9v298e24t3vwtpl", + "validator_address": "cosmosvaloper1grgelyng2v6v3t8z87wu3sxgt9m5s03xfytvz7" + } + } + ], + "sequence": "1" + }, + "parsingErr": "No error", + "validationErr": "No error", + "expected": [ + "0 | Chain ID : cosmoshub-3", + "1 | Account : 6571", + "2 | Sequence : 1", + "3 | Type : Delegate", + "4 | Amount : 1000000 uatom", + "5 | Delegator [1/2] : cosmos102hty0jv2s29lyc4u0tv97z9v298e24t", + "5 | Delegator [2/2] : 3vwtpl", + "6 | Validator [1/2] : cosmosvaloper1grgelyng2v6v3t8z87wu3sxgt", + "6 | Validator [2/2] : 9m5s03xfytvz7", + "7 | Memo : Zondax.ch", + "8 | Fee : 5000 uatom", + "9 | Gas : 200000" + ], + "expert": true + }, + { + "name": "delegationDifferentChain", + "tx": { + "account_number": "6571", + "chain_id": "otherhub", + "fee": { + "amount": [ + { + "amount": "5000", + "denom": "uatom" + } + ], + "gas": "200000" + }, + "memo": "Zondax.ch", + "msgs": [ + { + "type": "cosmos-sdk/MsgDelegate", + "value": { + "amount": { + "amount": "1000000", + "denom": "uatom" + }, + "delegator_address": "cosmos102hty0jv2s29lyc4u0tv97z9v298e24t3vwtpl", + "validator_address": "cosmosvaloper1grgelyng2v6v3t8z87wu3sxgt9m5s03xfytvz7" + } + } + ], + "sequence": "1" + }, + "parsingErr": "No error", + "validationErr": "No error", + "expected": [ + "0 | Chain ID : otherhub", + "1 | Account : 6571", + "2 | Sequence : 1", + "3 | Type : Delegate", + "4 | Amount : 1000000 uatom", + "5 | Delegator [1/2] : cosmos102hty0jv2s29lyc4u0tv97z9v298e24t", + "5 | Delegator [2/2] : 3vwtpl", + "6 | Validator [1/2] : cosmosvaloper1grgelyng2v6v3t8z87wu3sxgt", + "6 | Validator [2/2] : 9m5s03xfytvz7", + "7 | Memo : Zondax.ch", + "8 | Fee : 5000 uatom", + "9 | Gas : 200000" + ], + "expert": false + }, + { + "name": "delegation", + "tx": { + "account_number": "6571", + "chain_id": "cosmoshub-3", + "fee": { + "amount": [ + { + "amount": "5000", + "denom": "uatom" + } + ], + "gas": "200000" + }, + "memo": "Zondax.ch", + "msgs": [ + { + "type": "cosmos-sdk/MsgDelegate", + "value": { + "amount": { + "amount": "1000000", + "denom": "uatom" + }, + "delegator_address": "cosmos102hty0jv2s29lyc4u0tv97z9v298e24t3vwtpl", + "validator_address": "cosmosvaloper1grgelyng2v6v3t8z87wu3sxgt9m5s03xfytvz7" + } + } + ], + "sequence": "1" + }, + "parsingErr": "No error", + "validationErr": "No error", + "expected": [ + "0 | Type : Delegate", + "1 | Amount : 1.000000 ATOM", + "2 | Delegator [1/2] : cosmos102hty0jv2s29lyc4u0tv97z9v298e24t", + "2 | Delegator [2/2] : 3vwtpl", + "3 | Validator [1/2] : cosmosvaloper1grgelyng2v6v3t8z87wu3sxgt", + "3 | Validator [2/2] : 9m5s03xfytvz7", + "4 | Memo : Zondax.ch", + "5 | Fee : 0.005000 ATOM" + ], + "expert": false + }, + { + "name": "proposalExpert", + "tx": { + "account_number": "2", + "chain_id": "local-testnet", + "fee": { + "amount": [], + "gas": "500000" + }, + "memo": "abc", + "msgs": [ + { + "description": "test", + "initial_deposit": [ + { + "amount": "1", + "denom": "stake" + } + ], + "proposal_type": "Text", + "proposer": "cosmos101234567890abcdefghijklmnopqrstuvwxyz0", + "title": "test" + } + ], + "sequence": "0" + }, + "parsingErr": "No error", + "validationErr": "No error", + "expected": [ + "0 | Chain ID : local-testnet", + "1 | Account : 2", + "2 | Sequence : 0", + "3 | msgs/description : test", + "4 | msgs/initial_deposit/amount : 1", + "5 | msgs/initial_deposit/denom : stake", + "6 | msgs/proposal_type : Text", + "7 | msgs/proposer [1/2] : cosmos101234567890abcdefghijklmnopqrstu", + "7 | msgs/proposer [2/2] : vwxyz0", + "8 | msgs/title : test", + "9 | Memo : abc", + "10 | Fee : Empty", + "11 | Gas : 500000" + ], + "expert": true + }, + { + "name": "proposal", + "tx": { + "account_number": "2", + "chain_id": "cosmoshub-3", + "fee": { + "amount": [], + "gas": "500000" + }, + "memo": "abc", + "msgs": [ + { + "description": "test", + "initial_deposit": [ + { + "amount": "1", + "denom": "stake" + } + ], + "proposal_type": "Text", + "proposer": "cosmos101234567890abcdefghijklmnopqrstuvwxyz0", + "title": "test" + } + ], + "sequence": "0" + }, + "parsingErr": "No error", + "validationErr": "No error", + "expected": [ + "0 | msgs/description : test", + "1 | msgs/initial_deposit/amount : 1", + "2 | msgs/initial_deposit/denom : stake", + "3 | msgs/proposal_type : Text", + "4 | msgs/proposer [1/2] : cosmos101234567890abcdefghijklmnopqrstu", + "4 | msgs/proposer [2/2] : vwxyz0", + "5 | msgs/title : test", + "6 | Memo : abc", + "7 | Fee : Empty" + ], + "expert": false + }, + { + "name": "delegation2Expert", + "tx": { + "account_number": "6571", + "chain_id": "cosmoshub-3", + "fee": { + "amount": [ + { + "amount": "5000", + "denom": "uatom" + } + ], + "gas": "200000" + }, + "memo": "Delegated with Ledger from union.market", + "msgs": [ + { + "type": "cosmos-sdk/MsgDelegate", + "value": { + "amount": { + "amount": "1000000", + "denom": "uatom" + }, + "delegator_address": "cosmos102hty0jv2s29lyc4u0tv97z9v298e24t3vwtpl", + "validator_address": "cosmosvaloper1grgelyng2v6v3t8z87wu3sxgt9m5s03xfytvz7" + } + } + ], + "sequence": "0" + }, + "parsingErr": "No error", + "validationErr": "No error", + "expected": [ + "0 | Chain ID : cosmoshub-3", + "1 | Account : 6571", + "2 | Sequence : 0", + "3 | Type : Delegate", + "4 | Amount : 1000000 uatom", + "5 | Delegator [1/2] : cosmos102hty0jv2s29lyc4u0tv97z9v298e24t", + "5 | Delegator [2/2] : 3vwtpl", + "6 | Validator [1/2] : cosmosvaloper1grgelyng2v6v3t8z87wu3sxgt", + "6 | Validator [2/2] : 9m5s03xfytvz7", + "7 | Memo : Delegated with Ledger from union.market", + "8 | Fee : 5000 uatom", + "9 | Gas : 200000" + ], + "expert": true + }, + { + "name": "delegation2", + "tx": { + "account_number": "6571", + "chain_id": "cosmoshub-3", + "fee": { + "amount": [ + { + "amount": "5000", + "denom": "uatom" + } + ], + "gas": "200000" + }, + "memo": "Delegated with Ledger from union.market", + "msgs": [ + { + "type": "cosmos-sdk/MsgDelegate", + "value": { + "amount": { + "amount": "1000000", + "denom": "uatom" + }, + "delegator_address": "cosmos102hty0jv2s29lyc4u0tv97z9v298e24t3vwtpl", + "validator_address": "cosmosvaloper1grgelyng2v6v3t8z87wu3sxgt9m5s03xfytvz7" + } + } + ], + "sequence": "0" + }, + "parsingErr": "No error", + "validationErr": "No error", + "expected": [ + "0 | Type : Delegate", + "1 | Amount : 1.000000 ATOM", + "2 | Delegator [1/2] : cosmos102hty0jv2s29lyc4u0tv97z9v298e24t", + "2 | Delegator [2/2] : 3vwtpl", + "3 | Validator [1/2] : cosmosvaloper1grgelyng2v6v3t8z87wu3sxgt", + "3 | Validator [2/2] : 9m5s03xfytvz7", + "4 | Memo : Delegated with Ledger from union.market", + "5 | Fee : 0.005000 ATOM" + ], + "expert": false + }, + { + "name": "badcase", + "tx": { + "chain_id": "1234567890AB", + "fee": "1234" + }, + "parsingErr": "No error", + "validationErr": "JSON Missing sequence", + "expected": [ + "0 | Chain ID : 1234567890AB", + "1 | fee : 1234" + ], + "expert": true + }, + { + "name": "badcase2", + "tx": { + "account_number": "6571", + "chain_id": "cosmoshub-3", + "fee": { + "amount": [ + { + "amount": "5000", + "denom": "uatom" + } + ], + "gas": "200000" + }, + "memo": "Delegated with Ledger from union.market", + "msgs": [ + { + "type": "cosmos-sdk/MsgDelegate", + "value": { + "amount": { + "amount": "1000000", + "denom": "uatom" + }, + "delegator_address": "cosmos102hty0jv2s29lyc4u0tv97z9v298e24t3vwtpl", + "validator_address": "cosmosvaloper1grgelyng2v6v3t8z87wu3sxgt9m5s03xfytvz7" + } + } + ], + "sequence": "0" + }, + "parsingErr": "No error", + "validationErr": "No error", + "expected": [ + "0 | Chain ID : cosmoshub-3", + "1 | Account : 6571", + "2 | Sequence : 0", + "3 | Type : Delegate", + "4 | Amount : 1000000 uatom", + "5 | Delegator [1/2] : cosmos102hty0jv2s29lyc4u0tv97z9v298e24t", + "5 | Delegator [2/2] : 3vwtpl", + "6 | Validator [1/2] : cosmosvaloper1grgelyng2v6v3t8z87wu3sxgt", + "6 | Validator [2/2] : 9m5s03xfytvz7", + "7 | Memo : Delegated with Ledger from union.market", + "8 | Fee : 5000 uatom", + "9 | Gas : 200000" + ], + "expert": true + }, + { + "name": "oldStackOverflow", + "tx": { + "1": [ + [ + [ + [ + [ + [ + [ + [ + [ + [ + [ + [ + { + "2": "4" + } + ] + ] + ] + ] + ] + ] + ] + ] + ] + ] + ] + ] + }, + "parsingErr": "No error", + "validationErr": "JSON Missing chain_id", + "expected": [ + ], + "expert": true + }, + { + "name": "groupingSmallExpert", + "tx": { + "account_number": "108", + "chain_id": "cosmoshub-3", + "fee": { + "amount": [ + { + "amount": "600", + "denom": "uatom" + } + ], + "gas": "200000" + }, + "memo": "", + "msgs": [ + { + "type": "cosmos-sdk/MsgWithdrawDelegationReward", + "value": { + "delegator_address": "cosmos14lultfckehtszvzw4ehu0apvsr77afvyhgqhwh", + "validator_address": "cosmosvaloper1qwl879nx9t6kef4supyazayf7vjhennyh568ys" + } + }, + { + "type": "cosmos-sdk/MsgWithdrawDelegationReward", + "value": { + "delegator_address": "cosmos14lultfckehtszvzw4ehu0apvsr77afvyhgqhwh", + "validator_address": "cosmosvaloper1x88j7vp2xnw3zec8ur3g4waxycyz7m0mahdv3p" + } + } + ], + "sequence": "106" + }, + "parsingErr": "No error", + "validationErr": "No error", + "expected": [ + "0 | Chain ID : cosmoshub-3", + "1 | Account : 108", + "2 | Sequence : 106", + "3 | Type : Withdraw Reward", + "4 | Delegator [1/2] : cosmos14lultfckehtszvzw4ehu0apvsr77afvy", + "4 | Delegator [2/2] : hgqhwh", + "5 | Validator [1/2] : cosmosvaloper1qwl879nx9t6kef4supyazayf7", + "5 | Validator [2/2] : vjhennyh568ys", + "6 | Delegator [1/2] : cosmos14lultfckehtszvzw4ehu0apvsr77afvy", + "6 | Delegator [2/2] : hgqhwh", + "7 | Validator [1/2] : cosmosvaloper1x88j7vp2xnw3zec8ur3g4waxy", + "7 | Validator [2/2] : cyz7m0mahdv3p", + "8 | Fee : 600 uatom", + "9 | Gas : 200000" + ], + "expert": true + }, + { + "name": "groupingSmall", + "tx": { + "account_number": "108", + "chain_id": "cosmoshub-3", + "fee": { + "amount": [ + { + "amount": "600", + "denom": "uatom" + } + ], + "gas": "200000" + }, + "memo": "", + "msgs": [ + { + "type": "cosmos-sdk/MsgWithdrawDelegationReward", + "value": { + "delegator_address": "cosmos14lultfckehtszvzw4ehu0apvsr77afvyhgqhwh", + "validator_address": "cosmosvaloper1qwl879nx9t6kef4supyazayf7vjhennyh568ys" + } + }, + { + "type": "cosmos-sdk/MsgWithdrawDelegationReward", + "value": { + "delegator_address": "cosmos14lultfckehtszvzw4ehu0apvsr77afvyhgqhwh", + "validator_address": "cosmosvaloper1x88j7vp2xnw3zec8ur3g4waxycyz7m0mahdv3p" + } + } + ], + "sequence": "106" + }, + "parsingErr": "No error", + "validationErr": "No error", + "expected": [ + "0 | Type : Withdraw Reward", + "1 | Delegator [1/2] : cosmos14lultfckehtszvzw4ehu0apvsr77afvy", + "1 | Delegator [2/2] : hgqhwh", + "2 | Validator [1/2] : cosmosvaloper1qwl879nx9t6kef4supyazayf7", + "2 | Validator [2/2] : vjhennyh568ys", + "3 | Validator [1/2] : cosmosvaloper1x88j7vp2xnw3zec8ur3g4waxy", + "3 | Validator [2/2] : cyz7m0mahdv3p", + "4 | Fee : 0.000600 ATOM" + ], + "expert": false + }, + { + "name": "groupingExpert", + "tx": { + "account_number": "108", + "chain_id": "cosmoshub-3", + "fee": { + "amount": [ + { + "amount": "600", + "denom": "uatom" + } + ], + "gas": "200000" + }, + "memo": "", + "msgs": [ + { + "type": "cosmos-sdk/MsgWithdrawDelegationReward", + "value": { + "delegator_address": "cosmos14lultfckehtszvzw4ehu0apvsr77afvyhgqhwh", + "validator_address": "cosmosvaloper1qwl879nx9t6kef4supyazayf7vjhennyh568ys" + } + }, + { + "type": "cosmos-sdk/MsgWithdrawDelegationReward", + "value": { + "delegator_address": "cosmos14lultfckehtszvzw4ehu0apvsr77afvyhgqhwh", + "validator_address": "cosmosvaloper1x88j7vp2xnw3zec8ur3g4waxycyz7m0mahdv3p" + } + }, + { + "type": "cosmos-sdk/MsgWithdrawDelegationReward", + "value": { + "delegator_address": "cosmos14lultfckehtszvzw4ehu0apvsr77afvyhgqhwh", + "validator_address": "cosmosvaloper1grgelyng2v6v3t8z87wu3sxgt9m5s03xfytvz7" + } + }, + { + "type": "cosmos-sdk/MsgWithdrawDelegationReward", + "value": { + "delegator_address": "cosmos14lultfckehtszvzw4ehu0apvsr77afvyhgqhwh", + "validator_address": "cosmosvaloper1ttfytaf43nkytzp8hkfjfgjc693ky4t3y2n2ku" + } + } + ], + "sequence": "106" + }, + "parsingErr": "No error", + "validationErr": "No error", + "expected": [ + "0 | Chain ID : cosmoshub-3", + "1 | Account : 108", + "2 | Sequence : 106", + "3 | Type : Withdraw Reward", + "4 | Delegator [1/2] : cosmos14lultfckehtszvzw4ehu0apvsr77afvy", + "4 | Delegator [2/2] : hgqhwh", + "5 | Validator [1/2] : cosmosvaloper1qwl879nx9t6kef4supyazayf7", + "5 | Validator [2/2] : vjhennyh568ys", + "6 | Delegator [1/2] : cosmos14lultfckehtszvzw4ehu0apvsr77afvy", + "6 | Delegator [2/2] : hgqhwh", + "7 | Validator [1/2] : cosmosvaloper1x88j7vp2xnw3zec8ur3g4waxy", + "7 | Validator [2/2] : cyz7m0mahdv3p", + "8 | Delegator [1/2] : cosmos14lultfckehtszvzw4ehu0apvsr77afvy", + "8 | Delegator [2/2] : hgqhwh", + "9 | Validator [1/2] : cosmosvaloper1grgelyng2v6v3t8z87wu3sxgt", + "9 | Validator [2/2] : 9m5s03xfytvz7", + "10 | Delegator [1/2] : cosmos14lultfckehtszvzw4ehu0apvsr77afvy", + "10 | Delegator [2/2] : hgqhwh", + "11 | Validator [1/2] : cosmosvaloper1ttfytaf43nkytzp8hkfjfgjc6", + "11 | Validator [2/2] : 93ky4t3y2n2ku", + "12 | Fee : 600 uatom", + "13 | Gas : 200000" + ], + "expert": true + }, + { + "name": "grouping_ledger_testcase", + "tx": { + "account_number": "108", + "chain_id": "cosmoshub-3", + "fee": { + "amount": [ + { + "amount": "600", + "denom": "uatom" + } + ], + "gas": "200000" + }, + "memo": "", + "msgs": [ + { + "type": "cosmos-sdk/MsgWithdrawDelegationReward", + "value": { + "delegator_address": "cosmos1kky4yzth6gdrm8ga5zlfwhav33yr7hl87jycah", + "validator_address": "cosmosvaloper1kn3wugetjuy4zetlq6wadchfhvu3x740ae6z6x" + } + }, + { + "type": "cosmos-sdk/MsgWithdrawDelegationReward", + "value": { + "delegator_address": "cosmos1kky4yzth6gdrm8ga5zlfwhav33yr7hl87jycah", + "validator_address": "cosmosvaloper1sjllsnramtg3ewxqwwrwjxfgc4n4ef9u2lcnj0" + } + }, + { + "type": "cosmos-sdk/MsgWithdrawDelegationReward", + "value": { + "delegator_address": "cosmos1kky4yzth6gdrm8ga5zlfwhav33yr7hl87jycah", + "validator_address": "cosmosvaloper1ey69r37gfxvxg62sh4r0ktpuc46pzjrm873ae8" + } + }, + { + "type": "cosmos-sdk/MsgWithdrawDelegationReward", + "value": { + "delegator_address": "cosmos1kky4yzth6gdrm8ga5zlfwhav33yr7hl87jycah", + "validator_address": "cosmosvaloper1648ynlpdw7fqa2axt0w2yp3fk542junl7rsvq6" + } + } + ], + "sequence": "106" + }, + "parsingErr": "No error", + "validationErr": "No error", + "expected": [ + "0 | Chain ID : cosmoshub-3", + "1 | Account : 108", + "2 | Sequence : 106", + "3 | Type : Withdraw Reward", + "4 | Delegator [1/2] : cosmos1kky4yzth6gdrm8ga5zlfwhav33yr7hl8", + "4 | Delegator [2/2] : 7jycah", + "5 | Validator [1/2] : cosmosvaloper1kn3wugetjuy4zetlq6wadchfh", + "5 | Validator [2/2] : vu3x740ae6z6x", + "6 | Delegator [1/2] : cosmos1kky4yzth6gdrm8ga5zlfwhav33yr7hl8", + "6 | Delegator [2/2] : 7jycah", + "7 | Validator [1/2] : cosmosvaloper1sjllsnramtg3ewxqwwrwjxfgc", + "7 | Validator [2/2] : 4n4ef9u2lcnj0", + "8 | Delegator [1/2] : cosmos1kky4yzth6gdrm8ga5zlfwhav33yr7hl8", + "8 | Delegator [2/2] : 7jycah", + "9 | Validator [1/2] : cosmosvaloper1ey69r37gfxvxg62sh4r0ktpuc", + "9 | Validator [2/2] : 46pzjrm873ae8", + "10 | Delegator [1/2] : cosmos1kky4yzth6gdrm8ga5zlfwhav33yr7hl8", + "10 | Delegator [2/2] : 7jycah", + "11 | Validator [1/2] : cosmosvaloper1648ynlpdw7fqa2axt0w2yp3fk", + "11 | Validator [2/2] : 542junl7rsvq6", + "12 | Fee : 600 uatom", + "13 | Gas : 200000" + ], + "expert": true + }, + { + "name": "massive", + "tx": { + "account_number": "108", + "chain_id": "cosmoshub-3", + "fee": { + "amount": [ + { + "amount": "600", + "denom": "uatom" + } + ], + "gas": "200000" + }, + "memo": "", + "msgs": [ + { + "type": "cosmos-sdk/MsgWithdrawDelegationReward", + "value": { + "delegator_address": "cosmos14lultfckehtszvzw4ehu0apvsr77afvyhgqhwh", + "validator_address": "cosmosvaloper1qwl879nx9t6kef4supyazayf7vjhennyh568ys" + } + }, + { + "type": "cosmos-sdk/MsgWithdrawDelegationReward", + "value": { + "delegator_address": "cosmos14lultfckehtszvzw4ehu0apvsr77afvyhgqhwh", + "validator_address": "cosmosvaloper1x88j7vp2xnw3zec8ur3g4waxycyz7m0mahdv3p" + } + }, + { + "type": "cosmos-sdk/MsgWithdrawDelegationReward", + "value": { + "delegator_address": "cosmos14lultfckehtszvzw4ehu0apvsr77afvyhgqhwh", + "validator_address": "cosmosvaloper1grgelyng2v6v3t8z87wu3sxgt9m5s03xfytvz7" + } + }, + { + "type": "cosmos-sdk/MsgWithdrawDelegationReward", + "value": { + "delegator_address": "cosmos14lultfckehtszvzw4ehu0apvsr77afvyhgqhwh", + "validator_address": "cosmosvaloper1ttfytaf43nkytzp8hkfjfgjc693ky4t3y2n2ku" + } + }, + { + "type": "cosmos-sdk/MsgWithdrawDelegationReward", + "value": { + "delegator_address": "cosmos14lultfckehtszvzw4ehu0apvsr77afvyhgqhwh", + "validator_address": "cosmosvaloper1wdrypwex63geqswmcy5qynv4w3z3dyef2qmyna" + } + }, + { + "type": "cosmos-sdk/MsgWithdrawDelegationReward", + "value": { + "delegator_address": "cosmos14lultfckehtszvzw4ehu0apvsr77afvyhgqhwh", + "validator_address": "cosmosvaloper102ruvpv2srmunfffxavttxnhezln6fnc54at8c" + } + }, + { + "type": "cosmos-sdk/MsgWithdrawDelegationReward", + "value": { + "delegator_address": "cosmos14lultfckehtszvzw4ehu0apvsr77afvyhgqhwh", + "validator_address": "cosmosvaloper10e4vsut6suau8tk9m6dnrm0slgd6npe3jx5xpv" + } + }, + { + "type": "cosmos-sdk/MsgWithdrawDelegationReward", + "value": { + "delegator_address": "cosmos14lultfckehtszvzw4ehu0apvsr77afvyhgqhwh", + "validator_address": "cosmosvaloper1sxx9mszve0gaedz5ld7qdkjkfv8z992ax69k08" + } + }, + { + "type": "cosmos-sdk/MsgWithdrawDelegationReward", + "value": { + "delegator_address": "cosmos14lultfckehtszvzw4ehu0apvsr77afvyhgqhwh", + "validator_address": "cosmosvaloper1ssm0d433seakyak8kcf93yefhknjleeds4y3em" + } + }, + { + "type": "cosmos-sdk/MsgWithdrawDelegationReward", + "value": { + "delegator_address": "cosmos14lultfckehtszvzw4ehu0apvsr77afvyhgqhwh", + "validator_address": "cosmosvaloper13sduv92y3xdhy3rpmhakrc3v7t37e7ps9l0kpv" + } + }, + { + "type": "cosmos-sdk/MsgWithdrawDelegationReward", + "value": { + "delegator_address": "cosmos14lultfckehtszvzw4ehu0apvsr77afvyhgqhwh", + "validator_address": "cosmosvaloper15urq2dtp9qce4fyc85m6upwm9xul3049e02707" + } + }, + { + "type": "cosmos-sdk/MsgWithdrawDelegationReward", + "value": { + "delegator_address": "cosmos14lultfckehtszvzw4ehu0apvsr77afvyhgqhwh", + "validator_address": "cosmosvaloper14kn0kk33szpwus9nh8n87fjel8djx0y070ymmj" + } + }, + { + "type": "cosmos-sdk/MsgWithdrawDelegationReward", + "value": { + "delegator_address": "cosmos14lultfckehtszvzw4ehu0apvsr77afvyhgqhwh", + "validator_address": "cosmosvaloper14lultfckehtszvzw4ehu0apvsr77afvyju5zzy" + } + }, + { + "type": "cosmos-sdk/MsgWithdrawDelegationReward", + "value": { + "delegator_address": "cosmos14lultfckehtszvzw4ehu0apvsr77afvyhgqhwh", + "validator_address": "cosmosvaloper1k9a0cs97vul8w2vwknlfmpez6prv8klv03lv3d" + } + }, + { + "type": "cosmos-sdk/MsgWithdrawDelegationReward", + "value": { + "delegator_address": "cosmos14lultfckehtszvzw4ehu0apvsr77afvyhgqhwh", + "validator_address": "cosmosvaloper1kj0h4kn4z5xvedu2nd9c4a9a559wvpuvu0h6qn" + } + }, + { + "type": "cosmos-sdk/MsgWithdrawDelegationReward", + "value": { + "delegator_address": "cosmos14lultfckehtszvzw4ehu0apvsr77afvyhgqhwh", + "validator_address": "cosmosvaloper1hjct6q7npsspsg3dgvzk3sdf89spmlpfdn6m9d" + } + } + ], + "sequence": "106" + }, + "parsingErr": "No error", + "validationErr": "No error", + "expected": [ + "0 | Chain ID : cosmoshub-3", + "1 | Account : 108", + "2 | Sequence : 106", + "3 | Type : Withdraw Reward", + "4 | Delegator [1/2] : cosmos14lultfckehtszvzw4ehu0apvsr77afvy", + "4 | Delegator [2/2] : hgqhwh", + "5 | Validator [1/2] : cosmosvaloper1qwl879nx9t6kef4supyazayf7", + "5 | Validator [2/2] : vjhennyh568ys", + "6 | Delegator [1/2] : cosmos14lultfckehtszvzw4ehu0apvsr77afvy", + "6 | Delegator [2/2] : hgqhwh", + "7 | Validator [1/2] : cosmosvaloper1x88j7vp2xnw3zec8ur3g4waxy", + "7 | Validator [2/2] : cyz7m0mahdv3p", + "8 | Delegator [1/2] : cosmos14lultfckehtszvzw4ehu0apvsr77afvy", + "8 | Delegator [2/2] : hgqhwh", + "9 | Validator [1/2] : cosmosvaloper1grgelyng2v6v3t8z87wu3sxgt", + "9 | Validator [2/2] : 9m5s03xfytvz7", + "10 | Delegator [1/2] : cosmos14lultfckehtszvzw4ehu0apvsr77afvy", + "10 | Delegator [2/2] : hgqhwh", + "11 | Validator [1/2] : cosmosvaloper1ttfytaf43nkytzp8hkfjfgjc6", + "11 | Validator [2/2] : 93ky4t3y2n2ku", + "12 | Delegator [1/2] : cosmos14lultfckehtszvzw4ehu0apvsr77afvy", + "12 | Delegator [2/2] : hgqhwh", + "13 | Validator [1/2] : cosmosvaloper1wdrypwex63geqswmcy5qynv4w", + "13 | Validator [2/2] : 3z3dyef2qmyna", + "14 | Delegator [1/2] : cosmos14lultfckehtszvzw4ehu0apvsr77afvy", + "14 | Delegator [2/2] : hgqhwh", + "15 | Validator [1/2] : cosmosvaloper102ruvpv2srmunfffxavttxnhe", + "15 | Validator [2/2] : zln6fnc54at8c", + "16 | Delegator [1/2] : cosmos14lultfckehtszvzw4ehu0apvsr77afvy", + "16 | Delegator [2/2] : hgqhwh", + "17 | Validator [1/2] : cosmosvaloper10e4vsut6suau8tk9m6dnrm0sl", + "17 | Validator [2/2] : gd6npe3jx5xpv", + "18 | Delegator [1/2] : cosmos14lultfckehtszvzw4ehu0apvsr77afvy", + "18 | Delegator [2/2] : hgqhwh", + "19 | Validator [1/2] : cosmosvaloper1sxx9mszve0gaedz5ld7qdkjkf", + "19 | Validator [2/2] : v8z992ax69k08", + "20 | Delegator [1/2] : cosmos14lultfckehtszvzw4ehu0apvsr77afvy", + "20 | Delegator [2/2] : hgqhwh", + "21 | Validator [1/2] : cosmosvaloper1ssm0d433seakyak8kcf93yefh", + "21 | Validator [2/2] : knjleeds4y3em", + "22 | Delegator [1/2] : cosmos14lultfckehtszvzw4ehu0apvsr77afvy", + "22 | Delegator [2/2] : hgqhwh", + "23 | Validator [1/2] : cosmosvaloper13sduv92y3xdhy3rpmhakrc3v7", + "23 | Validator [2/2] : t37e7ps9l0kpv", + "24 | Delegator [1/2] : cosmos14lultfckehtszvzw4ehu0apvsr77afvy", + "24 | Delegator [2/2] : hgqhwh", + "25 | Validator [1/2] : cosmosvaloper15urq2dtp9qce4fyc85m6upwm9", + "25 | Validator [2/2] : xul3049e02707", + "26 | Delegator [1/2] : cosmos14lultfckehtszvzw4ehu0apvsr77afvy", + "26 | Delegator [2/2] : hgqhwh", + "27 | Validator [1/2] : cosmosvaloper14kn0kk33szpwus9nh8n87fjel", + "27 | Validator [2/2] : 8djx0y070ymmj", + "28 | Delegator [1/2] : cosmos14lultfckehtszvzw4ehu0apvsr77afvy", + "28 | Delegator [2/2] : hgqhwh", + "29 | Validator [1/2] : cosmosvaloper14lultfckehtszvzw4ehu0apvs", + "29 | Validator [2/2] : r77afvyju5zzy", + "30 | Delegator [1/2] : cosmos14lultfckehtszvzw4ehu0apvsr77afvy", + "30 | Delegator [2/2] : hgqhwh", + "31 | Validator [1/2] : cosmosvaloper1k9a0cs97vul8w2vwknlfmpez6", + "31 | Validator [2/2] : prv8klv03lv3d", + "32 | Delegator [1/2] : cosmos14lultfckehtszvzw4ehu0apvsr77afvy", + "32 | Delegator [2/2] : hgqhwh", + "33 | Validator [1/2] : cosmosvaloper1kj0h4kn4z5xvedu2nd9c4a9a5", + "33 | Validator [2/2] : 59wvpuvu0h6qn", + "34 | Delegator [1/2] : cosmos14lultfckehtszvzw4ehu0apvsr77afvy", + "34 | Delegator [2/2] : hgqhwh", + "35 | Validator [1/2] : cosmosvaloper1hjct6q7npsspsg3dgvzk3sdf8", + "35 | Validator [2/2] : 9spmlpfdn6m9d", + "36 | Fee : 600 uatom", + "37 | Gas : 200000" + ], + "expert": true + }, + { + "name": "combined", + "tx": { + "account_number": "108", + "chain_id": "cosmoshub-3", + "fee": { + "amount": [ + { + "amount": "600", + "denom": "uatom" + } + ], + "gas": "200000" + }, + "memo": "", + "msgs": [ + { + "type": "cosmos-sdk/MsgWithdrawDelegationReward", + "value": { + "delegator_address": "cosmos19umvgcvk8cxsvzemy239nj9ngc2ltukantgyp3", + "validator_address": "cosmosvaloper1648ynlpdw7fqa2axt0w2yp3fk542junl7rsvq6" + } + }, + { + "type": "cosmos-sdk/MsgDelegate", + "value": { + "delegator_address": "cosmos19umvgcvk8cxsvzemy239nj9ngc2ltukantgyp3", + "validator_address": "cosmosvaloper1648ynlpdw7fqa2axt0w2yp3fk542junl7rsvq6", + "amount": { + "denom": "uatom", + "amount": "20139397" + } + } + } + ], + "sequence": "106" + }, + "parsingErr": "No error", + "validationErr": "No error", + "expected": [ + "0 | Type : Withdraw Reward", + "1 | Delegator [1/2] : cosmos19umvgcvk8cxsvzemy239nj9ngc2ltuka", + "1 | Delegator [2/2] : ntgyp3", + "2 | Validator [1/2] : cosmosvaloper1648ynlpdw7fqa2axt0w2yp3fk", + "2 | Validator [2/2] : 542junl7rsvq6", + "3 | Type : Delegate", + "4 | Amount : 20.139397 ATOM", + "5 | Validator [1/2] : cosmosvaloper1648ynlpdw7fqa2axt0w2yp3fk", + "5 | Validator [2/2] : 542junl7rsvq6", + "6 | Fee : 0.000600 ATOM" + ], + "expert": false + } +] diff --git a/tests/tx_parse.cpp b/tests/tx_parse.cpp new file mode 100644 index 00000000..973717dd --- /dev/null +++ b/tests/tx_parse.cpp @@ -0,0 +1,166 @@ +/******************************************************************************* +* (c) 2018 Zondax GmbH +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +********************************************************************************/ + +#include "gtest/gtest.h" +#include +#include +#include +#include +#include "util/common.h" + +namespace { + parser_error_t tx_traverse(int16_t root_token_index, uint8_t *numChunks) { + uint16_t ret_value_token_index = 0; + parser_error_t err = tx_traverse_find(root_token_index, &ret_value_token_index); + + if (err != parser_ok){ + return err; + } + + return tx_getToken(ret_value_token_index, + parser_tx_obj.query.out_val, parser_tx_obj.query.out_val_len, + parser_tx_obj.query.page_index, numChunks); + } + + TEST(TxParse, Tx_Traverse) { + auto transaction = R"({"keyA":"123456", "keyB":"abcdefg", "keyC":""})"; + + parser_tx_obj.tx = transaction; + parser_tx_obj.flags.cache_valid = 0; + parser_error_t err = JSON_PARSE(&parser_tx_obj.json, parser_tx_obj.tx); + + ASSERT_EQ(err, parser_ok); + // Check some tokens + ASSERT_EQ(parser_tx_obj.json.numberOfTokens, 7) << "It should contain 7 = 1 (dict) + 6 (key+value)"; + ASSERT_EQ(parser_tx_obj.json.tokens[0].start, 0); + ASSERT_EQ(parser_tx_obj.json.tokens[0].end, 46); + ASSERT_EQ(parser_tx_obj.json.tokens[0].size, 3) << "size should be 3 = 3 key/values contained in the dict"; + ASSERT_EQ(parser_tx_obj.json.tokens[3].start, 19); + ASSERT_EQ(parser_tx_obj.json.tokens[3].end, 23); + + char key[100]; + char val[100]; + uint8_t numChunks; + + // Try second key - first chunk + INIT_QUERY_CONTEXT(key, sizeof(key), val, sizeof(val), 0, 4) + parser_tx_obj.query.item_index = 1; + + err = tx_traverse(0, &numChunks); + EXPECT_EQ(err, parser_ok) << parser_getErrorDescription(err); + EXPECT_EQ(numChunks, 1) << "Incorrect number of chunks"; + EXPECT_EQ_STR(key, "keyB", "Incorrect key") + EXPECT_EQ_STR(val, "abcdefg", "Incorrect value") + + // Try second key - Second chunk + INIT_QUERY_CONTEXT(key, sizeof(key), val, sizeof(val), 1, 4) + parser_tx_obj.query.item_index = 1; + err = tx_traverse(0, &numChunks); + EXPECT_EQ(err, parser_display_page_out_of_range) << parser_getErrorDescription(err); + EXPECT_EQ(numChunks, 1) << "Incorrect number of chunks"; + + // Find first key + INIT_QUERY_CONTEXT(key, sizeof(key), val, sizeof(val), 0, 4) + parser_tx_obj.query.item_index = 0; + err = tx_traverse(0, &numChunks); + EXPECT_EQ(err, parser_ok) << parser_getErrorDescription(err); + EXPECT_EQ(numChunks, 1) << "Incorrect number of chunks"; + EXPECT_EQ_STR(key, "keyA", "Incorrect key") + EXPECT_EQ_STR(val, "123456", "Incorrect value") + + // Try the same again + INIT_QUERY_CONTEXT(key, sizeof(key), val, sizeof(val), 0, 4) + parser_tx_obj.query.item_index = 0; + err = tx_traverse(0, &numChunks); + EXPECT_EQ(err, parser_ok) << parser_getErrorDescription(err); + EXPECT_EQ(numChunks, 1) << "Incorrect number of chunks"; + EXPECT_EQ_STR(key, "keyA", "Incorrect key") + EXPECT_EQ_STR(val, "123456", "Incorrect value") + + // Try last key + INIT_QUERY_CONTEXT(key, sizeof(key), val, sizeof(val), 0, 4) + parser_tx_obj.query.item_index = 2; + err = tx_traverse(0, &numChunks); + EXPECT_EQ(err, parser_ok) << parser_getErrorDescription(err); + EXPECT_EQ(numChunks, 1) << "Incorrect number of chunks"; + EXPECT_EQ_STR(key, "keyC", "Incorrect key") + EXPECT_EQ_STR(val, "", "Incorrect value") + } + + TEST(TxParse, OutOfBoundsSmall) { + auto transaction = R"({"keyA":"123456", "keyB":"abcdefg"})"; + + parser_tx_obj.tx = transaction; + parser_tx_obj.flags.cache_valid = 0; + parser_error_t err = JSON_PARSE(&parser_tx_obj.json, parser_tx_obj.tx); + ASSERT_EQ(err, parser_ok); + + char key[1000]; + char val[1000]; + uint8_t numChunks; + + INIT_QUERY_CONTEXT(key, sizeof(key), val, sizeof(val), 5, 4) + err = tx_traverse(0, &numChunks); + EXPECT_EQ(err, parser_display_page_out_of_range) << "This call should have resulted in a display out of range"; + + // We should find it.. but later tx_display should fail + INIT_QUERY_CONTEXT(key, sizeof(key), val, sizeof(val), 0, 4) + err = tx_traverse(0, &numChunks); + EXPECT_EQ(err, parser_ok); + EXPECT_EQ(numChunks, 1) << "Item not found"; + } + + TEST(TxParse, Count_Minimal) { + auto transaction = R"({"account_number":"0"})"; + + parser_tx_obj.tx = transaction; + parser_tx_obj.flags.cache_valid = 0; + parser_error_t err = JSON_PARSE(&parser_tx_obj.json, parser_tx_obj.tx); + EXPECT_EQ(err, parser_ok); + + uint8_t numItems; + tx_display_numItems(&numItems); + + EXPECT_EQ(1, numItems) << "Wrong number of items"; + } + + TEST(TxParse, Tx_Page_Count) { + auto transaction = R"({"account_number":"0","chain_id":"test-chain-1","fee":{"amount":[{"amount":"5","denom":"photon"}],"gas":"10000"},"memo":"testmemo","msgs":[{"inputs":[{"address":"cosmosaccaddr1d9h8qat5e4ehc5","coins":[{"amount":"10","denom":"atom"}]}],"outputs":[{"address":"cosmosaccaddr1da6hgur4wse3jx32","coins":[{"amount":"10","denom":"atom"}]}]}],"sequence":"1"})"; + + parser_tx_obj.tx = transaction; + parser_tx_obj.flags.cache_valid = 0; + parser_error_t err = JSON_PARSE(&parser_tx_obj.json, parser_tx_obj.tx); + EXPECT_EQ(err, parser_ok); + + uint8_t numItems; + tx_display_numItems(&numItems); + EXPECT_EQ(10, numItems) << "Wrong number of items"; + } + + TEST(TxParse, Page_Count_MultipleMsgs) { + auto transaction = + R"({"account_number":"0","chain_id":"test-chain-1","fee":{"amount":[{"amount":"5","denom":"photon"}],"gas":"10000"},"memo":"testmemo","msgs":[{"inputs":[{"address":"cosmosaccaddr1d9h8qat5e4ehc5","coins":[{"amount":"10","denom":"atom"}]}],"outputs":[{"address":"cosmosaccaddr1da6hgur4wse3jx32","coins":[{"amount":"10","denom":"atom"}]}]},{"inputs":[{"address":"cosmosaccaddr1d9h8qat5e4ehc5","coins":[{"amount":"10","denom":"atom"}]}],"outputs":[{"address":"cosmosaccaddr1da6hgur4wse3jx32","coins":[{"amount":"10","denom":"atom"}]}]},{"inputs":[{"address":"cosmosaccaddr1d9h8qat5e4ehc5","coins":[{"amount":"10","denom":"atom"}]}],"outputs":[{"address":"cosmosaccaddr1da6hgur4wse3jx32","coins":[{"amount":"10","denom":"atom"}]}]},{"inputs":[{"address":"cosmosaccaddr1d9h8qat5e4ehc5","coins":[{"amount":"10","denom":"atom"}]}],"outputs":[{"address":"cosmosaccaddr1da6hgur4wse3jx32","coins":[{"amount":"10","denom":"atom"}]}]}],"sequence":"1"})"; + + parser_tx_obj.tx = transaction; + parser_tx_obj.flags.cache_valid = 0; + parser_error_t err = JSON_PARSE(&parser_tx_obj.json, parser_tx_obj.tx); + EXPECT_EQ(err, parser_ok); + + uint8_t numItems; + tx_display_numItems(&numItems); + EXPECT_EQ(22, numItems) << "Wrong number of items"; + } +} diff --git a/tests/tx_validate.cpp b/tests/tx_validate.cpp new file mode 100644 index 00000000..67692728 --- /dev/null +++ b/tests/tx_validate.cpp @@ -0,0 +1,300 @@ +/******************************************************************************* +* (c) 2018 Zondax GmbH +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +********************************************************************************/ + +#include "gtest/gtest.h" +#include +#include +#include +#include "util/common.h" + +namespace { + TEST(TxValidationTest, CorrectFormat) { + auto transaction = + R"({"account_number":"0","chain_id":"test-chain-1","fee":{"amount":[{"amount":"5","denom":"photon"}],"gas":"10000"},"memo":"testmemo","msgs":[{"inputs":[{"address":"cosmosaccaddr1d9h8qat5e4ehc5","coins":[{"amount":"10","denom":"atom"}]}],"outputs":[{"address":"cosmosaccaddr1da6hgur4wse3jx32","coins":[{"amount":"10","denom":"atom"}]}]}],"sequence":"1"})"; + + parsed_json_t json; + parser_error_t err; + + err = JSON_PARSE(&json, transaction); + ASSERT_EQ(err, parser_ok); + + err = tx_validate(&json); + EXPECT_EQ(err, parser_ok) << "Validation failed, error: " << parser_getErrorDescription(err); + } + + TEST(TxValidationTest, MissingAccountNumber) { + auto transaction = + R"({"chain_id":"test-chain-1","fee":{"amount":[{"amount":"5","denom":"photon"}],"gas":"10000"},"memo":"testmemo","msgs":[{"inputs":[{"address":"cosmosaccaddr1d9h8qat5e4ehc5","coins":[{"amount":"10","denom":"atom"}]}],"outputs":[{"address":"cosmosaccaddr1da6hgur4wse3jx32","coins":[{"amount":"10","denom":"atom"}]}]}],"sequence":"1"})"; + + parsed_json_t json; + parser_error_t err; + + err = JSON_PARSE(&json, transaction); + ASSERT_EQ(err, parser_ok); + + err = tx_validate(&json); + + EXPECT_EQ(err, parser_json_missing_account_number) << "Validation failed, error: " << parser_getErrorDescription(err); + } + + TEST(TxValidationTest, MissingChainId) { + auto transaction = + R"({"account_number":"0","fee":{"amount":[{"amount":"5","denom":"photon"}],"gas":"10000"},"memo":"testmemo","msgs":[{"inputs":[{"address":"cosmosaccaddr1d9h8qat5e4ehc5","coins":[{"amount":"10","denom":"atom"}]}],"outputs":[{"address":"cosmosaccaddr1da6hgur4wse3jx32","coins":[{"amount":"10","denom":"atom"}]}]}],"sequence":"1"})"; + + parsed_json_t json; + parser_error_t err; + + err = JSON_PARSE(&json, transaction); + ASSERT_EQ(err, parser_ok); + + err = tx_validate(&json); + EXPECT_EQ(err, parser_json_missing_chain_id) << "Validation failed, error: " << parser_getErrorDescription(err); + } + + TEST(TxValidationTest, MissingFee) { + auto transaction = + R"({"account_number":"0","chain_id":"test-chain-1","fees":{"amount":[{"amount":"5","denom":"photon"}],"gas":"10000"},"memo":"testmemo","msgs":[{"inputs":[{"address":"cosmosaccaddr1d9h8qat5e4ehc5","coins":[{"amount":"10","denom":"atom"}]}],"outputs":[{"address":"cosmosaccaddr1da6hgur4wse3jx32","coins":[{"amount":"10","denom":"atom"}]}]}],"sequence":"1"})"; + + parsed_json_t json; + parser_error_t err; + + err = JSON_PARSE(&json, transaction); + ASSERT_EQ(err, parser_ok); + + err = tx_validate(&json); + EXPECT_EQ(err, parser_json_missing_fee) << "Validation failed, error: " << parser_getErrorDescription(err); + } + + TEST(TxValidationTest, MissingMsgs) { + auto transaction = + R"({"account_number":"0","chain_id":"test-chain-1","fee":{"amount":[{"amount":"5","denom":"photon"}],"gas":"10000"},"memo":"testmemo","msgsble":[{"inputs":[{"address":"cosmosaccaddr1d9h8qat5e4ehc5","coins":[{"amount":"10","denom":"atom"}]}],"outputs":[{"address":"cosmosaccaddr1da6hgur4wse3jx32","coins":[{"amount":"10","denom":"atom"}]}]}],"sequence":"1"})"; + + parsed_json_t json; + parser_error_t err; + + err = JSON_PARSE(&json, transaction); + ASSERT_EQ(err, parser_ok); + + err = tx_validate(&json); + EXPECT_EQ(err, parser_json_missing_msgs) << "Validation failed, error: " << parser_getErrorDescription(err); + } + + TEST(TxValidationTest, MissingSequence) { + auto transaction = + R"({"account_number":"0","chain_id":"test-chain-1","fee":{"amount":[{"amount":"5","denom":"photon"}],"gas":"10000"},"memo":"testmemo","msgs":[{"inputs":[{"address":"cosmosaccaddr1d9h8qat5e4ehc5","coins":[{"amount":"10","denom":"atom"}]}],"outputs":[{"address":"cosmosaccaddr1da6hgur4wse3jx32","coins":[{"amount":"10","denom":"atom"}]}]}]})"; + + parsed_json_t json; + parser_error_t err; + + err = JSON_PARSE(&json, transaction); + ASSERT_EQ(err, parser_ok); + + err = tx_validate(&json); + EXPECT_EQ(err, parser_json_missing_sequence) << "Validation failed, error: " << parser_getErrorDescription(err); + } + + TEST(TxValidationTest, Spaces_InTheMiddle) { + auto transaction = + R"({"account_number":"0","chain_id":"test-chain-1", "fee":{"amount":[{"amount":"5","denom":"photon"}],"gas":"10000"},"memo":"testmemo","msgs":[{"inputs":[{"address":"cosmosaccaddr1d9h8qat5e4ehc5","coins":[{"amount":"10","denom":"atom"}]}],"outputs":[{"address":"cosmosaccaddr1da6hgur4wse3jx32","coins":[{"amount":"10","denom":"atom"}]}]}],"sequence":"1"})"; + + parsed_json_t json; + parser_error_t err; + + err = JSON_PARSE(&json, transaction); + ASSERT_EQ(err, parser_ok); + + err = tx_validate(&json); + EXPECT_EQ(err, parser_json_contains_whitespace) << "Validation failed, error: " << parser_getErrorDescription(err); + } + + TEST(TxValidationTest, Spaces_AtTheFront) { + auto transaction = + R"({ "account_number":"0","chain_id":"test-chain-1","fee":{"amount":[{"amount":"5","denom":"photon"}],"gas":"10000"},"memo":"testmemo","msgs":[{"inputs":[{"address":"cosmosaccaddr1d9h8qat5e4ehc5","coins":[{"amount":"10","denom":"atom"}]}],"outputs":[{"address":"cosmosaccaddr1da6hgur4wse3jx32","coins":[{"amount":"10","denom":"atom"}]}]}],"sequence":"1"})"; + + parsed_json_t json; + parser_error_t err; + + err = JSON_PARSE(&json, transaction); + ASSERT_EQ(err, parser_ok); + + err = tx_validate(&json); + EXPECT_EQ(err, parser_json_contains_whitespace) << "Validation failed, error: " << parser_getErrorDescription(err); + } + + TEST(TxValidationTest, Spaces_AtTheEnd) { + auto transaction = + R"({"account_number":"0","chain_id":"test-chain-1","fee":{"amount":[{"amount":"5","denom":"photon"}],"gas":"10000"},"memo":"testmemo","msgs":[{"inputs":[{"address":"cosmosaccaddr1d9h8qat5e4ehc5","coins":[{"amount":"10","denom":"atom"}]}],"outputs":[{"address":"cosmosaccaddr1da6hgur4wse3jx32","coins":[{"amount":"10","denom":"atom"}]}]}],"sequence":"1" })"; + + parsed_json_t json; + parser_error_t err; + + err = JSON_PARSE(&json, transaction); + ASSERT_EQ(err, parser_ok); + + err = tx_validate(&json); + EXPECT_EQ(err, parser_json_contains_whitespace) << "Validation failed, error: " << parser_getErrorDescription(err); + } + + TEST(TxValidationTest, Spaces_Lots) { + auto transaction = + R"({"account_number":"0", "chain_id":"test-chain-1", "fee":{"amount": [{"amount":"5","denom":"photon"}],"gas":"10000"},"memo":"testmemo","msgs":[{"inputs":[{"address":"cosmosaccaddr1d9h8qat5e4ehc5","coins":[{"amount":"10","denom":"atom"}]}],"outputs":[{"address":"cosmosaccaddr1da6hgur4wse3jx32","coins":[{"amount":"10","denom":"atom"}]}]}],"sequence":"1"})"; + + parsed_json_t json; + parser_error_t err; + + err = JSON_PARSE(&json, transaction); + ASSERT_EQ(err, parser_ok); + + err = tx_validate(&json); + EXPECT_EQ(err, parser_json_contains_whitespace) << "Validation failed, error: " << parser_getErrorDescription(err); + } + + TEST(TxValidationTest, AllowSpacesInString) { + auto transaction = + R"({"account_number":"0","chain_id":" test-chain-1 ","fee":{"amount":[{"amount":"5","denom":" photon"}],"gas":"10000"},"memo":"testmemo","msgs":[{"inputs":[{"address":"cosmosaccaddr1d9h8qat5e4ehc5","coins":[{"amount":"10","denom":"atom"}]}],"outputs":[{"address":"cosmosaccaddr1da6hgur4wse3jx32","coins":[{"amount":"10","denom":"atom"}]}]}],"sequence":"1"})"; + + parsed_json_t json; + parser_error_t err; + + err = JSON_PARSE(&json, transaction); + ASSERT_EQ(err, parser_ok); + + err = tx_validate(&json); + EXPECT_EQ(err, parser_ok) << "Validation failed, error: " << parser_getErrorDescription(err); + } + + TEST(TxValidationTest, SortedDictionary) { + auto transaction = + R"({"account_number":"0","chain_id":"test-chain-1","fee":{"amount":[{"amount":"5","denom":"photon"}],"gas":"10000"},"memo":"testmemo","msgs":[{"inputs":[{"address":"cosmosaccaddr1d9h8qat5e4ehc5","coins":[{"amount":"10","denom":"atom"}]}],"outputs":[{"address":"cosmosaccaddr1da6hgur4wse3jx32","coins":[{"amount":"10","denom":"atom"}]}]}],"sequence":"1"})"; + + parsed_json_t json; + parser_error_t err; + + err = JSON_PARSE(&json, transaction); + ASSERT_EQ(err, parser_ok); + + err = tx_validate(&json); + EXPECT_EQ(err, parser_ok) << "Validation failed, error: " << parser_getErrorDescription(err); + } + + TEST(TxValidationTest, NotSortedDictionary_FirstElement) { + auto transaction = + R"({"chain_id":"test-chain-1","account_number":"0","fee":{"amount":[{"amount":"5","denom":"photon"}],"gas":"10000"},"memo":"testmemo","msgs":[{"inputs":[{"address":"cosmosaccaddr1d9h8qat5e4ehc5","coins":[{"amount":"10","denom":"atom"}]}],"outputs":[{"address":"cosmosaccaddr1da6hgur4wse3jx32","coins":[{"amount":"10","denom":"atom"}]}]}],"sequence":"1"})"; + + parsed_json_t json; + parser_error_t err; + + err = JSON_PARSE(&json, transaction); + ASSERT_EQ(err, parser_ok); + + err = tx_validate(&json); + EXPECT_EQ(err, parser_json_is_not_sorted) << "Validation failed, error: " << parser_getErrorDescription(err); + } + + TEST(TxValidationTest, NotSortedDictionary_MiddleElement) { + auto transaction = + R"({"account_number":"0","chain_id":"test-chain-1","memo":"testmemo","fee":{"amount":[{"amount":"5","denom":"photon"}],"gas":"10000"},"msgs":[{"inputs":[{"address":"cosmosaccaddr1d9h8qat5e4ehc5","coins":[{"amount":"10","denom":"atom"}]}],"outputs":[{"address":"cosmosaccaddr1da6hgur4wse3jx32","coins":[{"amount":"10","denom":"atom"}]}]}],"sequence":"1"})"; + + parsed_json_t json; + parser_error_t err; + + err = JSON_PARSE(&json, transaction); + ASSERT_EQ(err, parser_ok); + + err = tx_validate(&json); + EXPECT_EQ(err, parser_json_is_not_sorted) << "Validation failed, error: " << parser_getErrorDescription(err); + } + + TEST(TxValidationTest, NotSortedDictionary_LastElement) { + auto transaction = + R"({"account_number":"0","chain_id":"test-chain-1","fee":{"amount":[{"amount":"5","denom":"photon"}],"gas":"10000"},"memo":"testmemo","sequence":"1","msgs":[{"inputs":[{"address":"cosmosaccaddr1d9h8qat5e4ehc5","coins":[{"amount":"10","denom":"atom"}]}],"outputs":[{"address":"cosmosaccaddr1da6hgur4wse3jx32","coins":[{"amount":"10","denom":"atom"}]}]}]})"; + + parsed_json_t json; + parser_error_t err; + + err = JSON_PARSE(&json, transaction); + ASSERT_EQ(err, parser_ok); + + err = tx_validate(&json); + EXPECT_EQ(err, parser_json_is_not_sorted) << "Validation failed, error: " << parser_getErrorDescription(err); + } + +// This json has been taken directly from goclient which uses cosmos to serialize a simple tx +// This test is currently failing the validation. +// We are reviewing the validation code and cosmos serialization to find the culprit. + + TEST(TxValidationTest, CosmosExample) { + auto transaction = + R"({"account_number":"0","chain_id":"test-chain-1","fee":{"amount":[{"amount":"5","denom":"photon"}],"gas":"10000"},"memo":"testmemo","msgs":[{"inputs":[{"address":"cosmosaccaddr1d9h8qat5e4ehc5","coins":[{"amount":"10","denom":"atom"}]}],"outputs":[{"address":"cosmosaccaddr1da6hgur4wse3jx32","coins":[{"amount":"10","denom":"atom"}]}]}],"sequence":"1"})"; + + parsed_json_t json; + parser_error_t err; + + err = JSON_PARSE(&json, transaction); + ASSERT_EQ(err, parser_ok); + + err = tx_validate(&json); + EXPECT_EQ(err, parser_ok) << "Validation failed, error: " << parser_getErrorDescription(err); + } + + TEST(TxValidationTest, GaiaCLIissue) { + auto transaction = R"({"account_number":"811","chain_id":"cosmoshub-1","fee":{"amount":[],"gas":"5000000"},"memo":"","msgs":[{"type":"cosmos-sdk/MsgDelegate","value":{"delegator_address":"cosmos13vfzpfmg6jgzfk4rke9glzpngrzucjtanq9awx","validator_address":"cosmosvaloper10e4vsut6suau8tk9m6dnrm0slgd6npe3jx5xpv","value":{"amount":"8000000000","denom":"uatom"}}},{"type":"cosmos-sdk/MsgDelegate","value":{"delegator_address":"cosmos13vfzpfmg6jgzfk4rke9glzpngrzucjtanq9awx","validator_address":"cosmosvaloper14kn0kk33szpwus9nh8n87fjel8djx0y070ymmj","value":{"amount":"8000000000","denom":"uatom"}}},{"type":"cosmos-sdk/MsgDelegate","value":{"delegator_address":"cosmos13vfzpfmg6jgzfk4rke9glzpngrzucjtanq9awx","validator_address":"cosmosvaloper14kn0kk33szpwus9nh8n87fjel8djx0y070ymmj","value":{"amount":"8000000000","denom":"uatom"}}},{"type":"cosmos-sdk/MsgDelegate","value":{"delegator_address":"cosmos13vfzpfmg6jgzfk4rke9glzpngrzucjtanq9awx","validator_address":"cosmosvaloper14kn0kk33szpwus9nh8n87fjel8djx0y070ymmj","value":{"amount":"8000000000","denom":"uatom"}}}],"sequence":"1"})"; + + parsed_json_t json; + parser_error_t err; + + err = JSON_PARSE(&json, transaction); + ASSERT_EQ(err, parser_ok); + + err = tx_validate(&json); + EXPECT_EQ(err, parser_ok) << "Validation failed, error: " << parser_getErrorDescription(err); + } + + TEST(TxValidationTest, GaiaCLIissueBigTX) { + auto transaction = R"({"account_number":"811","chain_id":"cosmoshub-1","fee":{"amount":[],"gas":"5000000"},"memo":"","msgs":[{"type":"cosmos-sdk/MsgDelegate","value":{"delegator_address":"cosmos13vfzpfmg6jgzfk4rke9glzpngrzucjtanq9awx","validator_address":"cosmosvaloper10e4vsut6suau8tk9m6dnrm0slgd6npe3jx5xpv","value":{"amount":"8000000000","denom":"uatom"}}},{"type":"cosmos-sdk/MsgDelegate","value":{"delegator_address":"cosmos13vfzpfmg6jgzfk4rke9glzpngrzucjtanq9awx", + "validator_address":"cosmosvaloper10e4vsut6suau8tk9m6dnrm0slgd6npe3jx5xpv","value":{"amount":"8000000000","denom":"uatom"}}},{"type":"cosmos-sdk/MsgDelegate","value":{"delegator_address":"cosmos13vfzpfmg6jgzfk4rke9glzpngrzucjtanq9awx","validator_address":"cosmosvaloper10e4vsut6suau8tk9m6dnrm0slgd6npe3jx5xpv","value":{"amount":"8000000000","denom":"uatom"}}},{"type":"cosmos-sdk/MsgDelegate","value":{"delegator_address":"cosmos13vfzpfmg6jgzfk4rke9glzpngrzucjtanq9awx","validator_address":"cosmosvaloper10e4vsut6suau8tk9m6dnrm0slgd6npe3jx5xpv","value":{"amount":"8000000000","denom":"uatom"}}},{"type":"cosmos-sdk/MsgDelegate","value":{"delegator_address":"cosmos13vfzpfmg6jgzfk4rke9glzpngrzucjtanq9awx", + "validator_address":"cosmosvaloper10e4vsut6suau8tk9m6dnrm0slgd6npe3jx5xpv","value":{"amount":"8000000000","denom":"uatom"}}},{"type":"cosmos-sdk/MsgDelegate","value":{"delegator_address":"cosmos13vfzpfmg6jgzfk4rke9glzpngrzucjtanq9awx","validator_address":"cosmosvaloper10e4vsut6suau8tk9m6dnrm0slgd6npe3jx5xpv","value":{"amount":"8000000000","denom":"uatom"}}},{"type":"cosmos-sdk/MsgDelegate","value":{"delegator_address":"cosmos13vfzpfmg6jgzfk4rke9glzpngrzucjtanq9awx","validator_address":"cosmosvaloper10e4vsut6suau8tk9m6dnrm0slgd6npe3jx5xpv","value":{"amount":"8000000000","denom":"uatom"}}},{"type":"cosmos-sdk/MsgDelegate","value":{"delegator_address":"cosmos13vfzpfmg6jgzfk4rke9glzpngrzucjtanq9awx","validator_address":"cosmosvaloper10e4vsut6suau8tk9m6dnrm0slgd6npe3jx5xpv","value":{"amount":"8000000000","denom":"uatom"}}},{"type":"cosmos-sdk/MsgDelegate","value":{"delegator_address":"cosmos13vfzpfmg6jgzfk4rke9glzpngrzucjtanq9awx","validator_address":"cosmosvaloper10e4vsut6suau8tk9m6dnrm0slgd6npe3jx5xpv","value":{"amount":"8000000000","denom":"uatom"}}},{"type":"cosmos-sdk/MsgDelegate","value":{"delegator_address":"cosmos13vfzpfmg6jgzfk4rke9glzpngrzucjtanq9awx","validator_address":"cosmosvaloper10e4vsut6suau8tk9m6dnrm0slgd6npe3jx5xpv","value":{"amount":"8000000000","denom":"uatom"}}},{"type":"cosmos-sdk/MsgDelegate","value":{"delegator_address":"cosmos13vfzpfmg6jgzfk4rke9glzpngrzucjtanq9awx", + "validator_address":"cosmosvaloper10e4vsut6suau8tk9m6dnrm0slgd6npe3jx5xpv","value":{"amount":"8000000000","denom":"uatom"}}},{"type":"cosmos-sdk/MsgDelegate","value":{"delegator_address":"cosmos13vfzpfmg6jgzfk4rke9glzpngrzucjtanq9awx","validator_address":"cosmosvaloper10e4vsut6suau8tk9m6dnrm0slgd6npe3jx5xpv","value":{"amount":"8000000000","denom":"uatom"}}},{"type":"cosmos-sdk/MsgDelegate","value":{"delegator_address":"cosmos13vfzpfmg6jgzfk4rke9glzpngrzucjtanq9awx","validator_address":"cosmosvaloper10e4vsut6suau8tk9m6dnrm0slgd6npe3jx5xpv","value":{"amount":"8000000000","denom":"uatom"}}},{"type":"cosmos-sdk/MsgDelegate","value":{"delegator_address":"cosmos13vfzpfmg6jgzfk4rke9glzpngrzucjtanq9awx","validator_address":"cosmosvaloper10e4vsut6suau8tk9m6dnrm0slgd6npe3jx5xpv","value":{"amount":"8000000000","denom":"uatom"}}},{"type":"cosmos-sdk/MsgDelegate","value":{"delegator_address":"cosmos13vfzpfmg6jgzfk4rke9glzpngrzucjtanq9awx","validator_address":"cosmosvaloper10e4vsut6suau8tk9m6dnrm0slgd6npe3jx5xpv","value":{"amount":"8000000000","denom":"uatom"}}},{"type":"cosmos-sdk/MsgDelegate","value":{"delegator_address":"cosmos13vfzpfmg6jgzfk4rke9glzpngrzucjtanq9awx","validator_address":"cosmosvaloper10e4vsut6suau8tk9m6dnrm0slgd6npe3jx5xpv","value":{"amount":"8000000000","denom":"uatom"}}},{"type":"cosmos-sdk/MsgDelegate","value":{"delegator_address":"cosmos13vfzpfmg6jgzfk4rke9glzpngrzucjtanq9awx","validator_address":"cosmosvaloper10e4vsut6suau8tk9m6dnrm0slgd6npe3jx5xpv","value":{"amount":"8000000000","denom":"uatom"}}},{"type":"cosmos-sdk/MsgDelegate","value":{"delegator_address":"cosmos13vfzpfmg6jgzfk4rke9glzpngrzucjtanq9awx","validator_address":"cosmosvaloper10e4vsut6suau8tk9m6dnrm0slgd6npe3jx5xpv","value":{"amount":"8000000000","denom":"uatom"}}},{"type":"cosmos-sdk/MsgDelegate","value":{"delegator_address":"cosmos13vfzpfmg6jgzfk4rke9glzpngrzucjtanq9awx","validator_address":"cosmosvaloper10e4vsut6suau8tk9m6dnrm0slgd6npe3jx5xpv","value":{"amount":"8000000000","denom":"uatom"}}},{"type":"cosmos-sdk/MsgDelegate","value":{"delegator_address":"cosmos13vfzpfmg6jgzfk4rke9glzpngrzucjtanq9awx","validator_address":"cosmosvaloper10e4vsut6suau8tk9m6dnrm0slgd6npe3jx5xpv","value":{"amount":"8000000000","denom":"uatom"}}},{"type":"cosmos-sdk/MsgDelegate","value":{"delegator_address":"cosmos13vfzpfmg6jgzfk4rke9glzpngrzucjtanq9awx", +"validator_address":"cosmosvaloper10e4vsut6suau8tk9m6dnrm0slgd6npe3jx5xpv","value":{"amount":"8000000000","denom":"uatom"}}},{"type":"cosmos-sdk/MsgDelegate","value":{"delegator_address":"cosmos13vfzpfmg6jgzfk4rke9glzpngrzucjtanq9awx","validator_address":"cosmosvaloper10e4vsut6suau8tk9m6dnrm0slgd6npe3jx5xpv","value":{"amount":"8000000000","denom":"uatom"}}},{"type":"cosmos-sdk/MsgDelegate","value":{"delegator_address":"cosmos13vfzpfmg6jgzfk4rke9glzpngrzucjtanq9awx","validator_address":"cosmosvaloper10e4vsut6suau8tk9m6dnrm0slgd6npe3jx5xpv","value":{"amount":"8000000000","denom":"uatom"}}},{"type":"cosmos-sdk/MsgDelegate","value":{"delegator_address":"cosmos13vfzpfmg6jgzfk4rke9glzpngrzucjtanq9awx", + "validator_address":"cosmosvaloper10e4vsut6suau8tk9m6dnrm0slgd6npe3jx5xpv","value":{"amount":"8000000000","denom":"uatom"}}},{"type":"cosmos-sdk/MsgDelegate","value":{"delegator_address":"cosmos13vfzpfmg6jgzfk4rke9glzpngrzucjtanq9awx","validator_address":"cosmosvaloper10e4vsut6suau8tk9m6dnrm0slgd6npe3jx5xpv","value":{"amount":"8000000000","denom":"uatom"}}},{"type":"cosmos-sdk/MsgDelegate","value":{"delegator_address":"cosmos13vfzpfmg6jgzfk4rke9glzpngrzucjtanq9awx","validator_address":"cosmosvaloper10e4vsut6suau8tk9m6dnrm0slgd6npe3jx5xpv","value":{"amount":"8000000000","denom":"uatom"}}},{"type":"cosmos-sdk/MsgDelegate","value":{"delegator_address":"cosmos13vfzpfmg6jgzfk4rke9glzpngrzucjtanq9awx","validator_address":"cosmosvaloper10e4vsut6suau8tk9m6dnrm0slgd6npe3jx5xpv","value":{"amount":"8000000000","denom":"uatom"}}},{"type":"cosmos-sdk/MsgDelegate","value":{"delegator_address":"cosmos13vfzpfmg6jgzfk4rke9glzpngrzucjtanq9awx","validator_address":"cosmosvaloper10e4vsut6suau8tk9m6dnrm0slgd6npe3jx5xpv","value":{"amount":"8000000000","denom":"uatom"}}},{"type":"cosmos-sdk/MsgDelegate","value":{"delegator_address":"cosmos13vfzpfmg6jgzfk4rke9glzpngrzucjtanq9awx","validator_address":"cosmosvaloper10e4vsut6suau8tk9m6dnrm0slgd6npe3jx5xpv","value":{"amount":"8000000000","denom":"uatom"}}},{"type":"cosmos-sdk/MsgDelegate","value":{"delegator_address":"cosmos13vfzpfmg6jgzfk4rke9glzpngrzucjtanq9awx","validator_address":"cosmosvaloper10e4vsut6suau8tk9m6dnrm0slgd6npe3jx5xpv","value":{"amount":"8000000000","denom":"uatom"}}},{"type":"cosmos-sdk/MsgDelegate","value":{"delegator_address":"cosmos13vfzpfmg6jgzfk4rke9glzpngrzucjtanq9awx","validator_address":"cosmosvaloper10e4vsut6suau8tk9m6dnrm0slgd6npe3jx5xpv","value":{"amount":"8000000000","denom":"uatom"}}},{"type":"cosmos-sdk/MsgDelegate","value":{"delegator_address":"cosmos13vfzpfmg6jgzfk4rke9glzpngrzucjtanq9awx","validator_address":"cosmosvaloper10e4vsut6suau8tk9m6dnrm0slgd6npe3jx5xpv","value":{"amount":"8000000000","denom":"uatom"}}},{"type":"cosmos-sdk/MsgDelegate","value":{"delegator_address":"cosmos13vfzpfmg6jgzfk4rke9glzpngrzucjtanq9awx","validator_address":"cosmosvaloper10e4vsut6suau8tk9m6dnrm0slgd6npe3jx5xpv","value":{"amount":"8000000000","denom":"uatom"}}},{"type":"cosmos-sdk/MsgDelegate","value":{"delegator_address":"cosmos13vfzpfmg6jgzfk4rke9glzpngrzucjtanq9awx","validator_address":"cosmosvaloper10e4vsut6suau8tk9m6dnrm0slgd6npe3jx5xpv","value":{"amount":"8000000000","denom":"uatom"}}},{"type":"cosmos-sdk/MsgDelegate","value":{"delegator_address":"cosmos13vfzpfmg6jgzfk4rke9glzpngrzucjtanq9awx","validator_address":"cosmosvaloper10e4vsut6suau8tk9m6dnrm0slgd6npe3jx5xpv","value":{"amount":"8000000000","denom":"uatom"}}},{"type":"cosmos-sdk/MsgDelegate","value":{"delegator_address":"cosmos13vfzpfmg6jgzfk4rke9glzpngrzucjtanq9awx","validator_address":"cosmosvaloper10e4vsut6suau8tk9m6dnrm0slgd6npe3jx5xpv","value":{"amount":"8000000000","denom":"uatom"}}},{"type":"cosmos-sdk/MsgDelegate","value":{"delegator_address":"cosmos13vfzpfmg6jgzfk4rke9glzpngrzucjtanq9awx", + "validator_address":"cosmosvaloper10e4vsut6suau8tk9m6dnrm0slgd6npe3jx5xpv","value":{"amount":"8000000000","denom":"uatom"}}},{"type":"cosmos-sdk/MsgDelegate","value":{"delegator_address":"cosmos13vfzpfmg6jgzfk4rke9glzpngrzucjtanq9awx","validator_address":"cosmosvaloper10e4vsut6suau8tk9m6dnrm0slgd6npe3jx5xpv","value":{"amount":"8000000000","denom":"uatom"}}},{"type":"cosmos-sdk/MsgDelegate","value":{"delegator_address":"cosmos13vfzpfmg6jgzfk4rke9glzpngrzucjtanq9awx","validator_address":"cosmosvaloper10e4vsut6suau8tk9m6dnrm0slgd6npe3jx5xpv","value":{"amount":"8000000000","denom":"uatom"}}},{"type":"cosmos-sdk/MsgDelegate","value":{"delegator_address":"cosmos13vfzpfmg6jgzfk4rke9glzpngrzucjtanq9awx","validator_address":"cosmosvaloper10e4vsut6suau8tk9m6dnrm0slgd6npe3jx5xpv","value":{"amount":"8000000000","denom":"uatom"}}},{"type":"cosmos-sdk/MsgDelegate","value":{"delegator_address":"cosmos13vfzpfmg6jgzfk4rke9glzpngrzucjtanq9awx","validator_address":"cosmosvaloper10e4vsut6suau8tk9m6dnrm0slgd6npe3jx5xpv","value":{"amount":"8000000000","denom":"uatom"}}},{"type":"cosmos-sdk/MsgDelegate","value":{"delegator_address":"cosmos13vfzpfmg6jgzfk4rke9glzpngrzucjtanq9awx","validator_address":"cosmosvaloper10e4vsut6suau8tk9m6dnrm0slgd6npe3jx5xpv","value":{"amount":"8000000000","denom":"uatom"}}},{"type":"cosmos-sdk/MsgDelegate","value":{"delegator_address":"cosmos13vfzpfmg6jgzfk4rke9glzpngrzucjtanq9awx","validator_address":"cosmosvaloper10e4vsut6suau8tk9m6dnrm0slgd6npe3jx5xpv","value":{"amount":"8000000000","denom":"uatom"}}},{"type":"cosmos-sdk/MsgDelegate","value":{"delegator_address":"cosmos13vfzpfmg6jgzfk4rke9glzpngrzucjtanq9awx","validator_address":"cosmosvaloper10e4vsut6suau8tk9m6dnrm0slgd6npe3jx5xpv","value":{"amount":"8000000000","denom":"uatom"}}},{"type":"cosmos-sdk/MsgDelegate","value":{"delegator_address":"cosmos13vfzpfmg6jgzfk4rke9glzpngrzucjtanq9awx","validator_address":"cosmosvaloper10e4vsut6suau8tk9m6dnrm0slgd6npe3jx5xpv","value":{"amount":"8000000000","denom":"uatom"}}},{"type":"cosmos-sdk/MsgDelegate","value":{"delegator_address":"cosmos13vfzpfmg6jgzfk4rke9glzpngrzucjtanq9awx","validator_address":"cosmosvaloper10e4vsut6suau8tk9m6dnrm0slgd6npe3jx5xpv","value":{"amount":"8000000000","denom":"uatom"}}},{"type":"cosmos-sdk/MsgDelegate","value":{"delegator_address":"cosmos13vfzpfmg6jgzfk4rke9glzpngrzucjtanq9awx","validator_address":"cosmosvaloper10e4vsut6suau8tk9m6dnrm0slgd6npe3jx5xpv","value":{"amount":"8000000000","denom":"uatom"}}},{"type":"cosmos-sdk/MsgDelegate","value":{"delegator_address":"cosmos13vfzpfmg6jgzfk4rke9glzpngrzucjtanq9awx", +"validator_address":"cosmosvaloper10e4vsut6suau8tk9m6dnrm0slgd6npe3jx5xpv","value":{"amount":"8000000000","denom":"uatom"}}},{"type":"cosmos-sdk/MsgDelegate","value":{"delegator_address":"cosmos13vfzpfmg6jgzfk4rke9glzpngrzucjtanq9awx", + "validator_address":"cosmosvaloper10e4vsut6suau8tk9m6dnrm0slgd6npe3jx5xpv","value":{"amount":"8000000000","denom":"uatom"}}},{"type":"cosmos-sdk/MsgDelegate","value":{"delegator_address":"cosmos13vfzpfmg6jgzfk4rke9glzpngrzucjtanq9awx","validator_address":"cosmosvaloper10e4vsut6suau8tk9m6dnrm0slgd6npe3jx5xpv","value":{"amount":"8000000000","denom":"uatom"}}},{"type":"cosmos-sdk/MsgDelegate","value":{"delegator_address":"cosmos13vfzpfmg6jgzfk4rke9glzpngrzucjtanq9awx","validator_address":"cosmosvaloper10e4vsut6suau8tk9m6dnrm0slgd6npe3jx5xpv","value":{"amount":"8000000000","denom":"uatom"}}},{"type":"cosmos-sdk/MsgDelegate","value":{"delegator_address":"cosmos13vfzpfmg6jgzfk4rke9glzpngrzucjtanq9awx", + "validator_address":"cosmosvaloper10e4vsut6suau8tk9m6dnrm0slgd6npe3jx5xpv","value":{"amount":"8000000000","denom":"uatom"}}},{"type":"cosmos-sdk/MsgDelegate","value":{"delegator_address":"cosmos13vfzpfmg6jgzfk4rke9glzpngrzucjtanq9awx","validator_address":"cosmosvaloper10e4vsut6suau8tk9m6dnrm0slgd6npe3jx5xpv","value":{"amount":"8000000000","denom":"uatom"}}},{"type":"cosmos-sdk/MsgDelegate","value":{"delegator_address":"cosmos13vfzpfmg6jgzfk4rke9glzpngrzucjtanq9awx","validator_address":"cosmosvaloper10e4vsut6suau8tk9m6dnrm0slgd6npe3jx5xpv","value":{"amount":"8000000000","denom":"uatom"}}},{"type":"cosmos-sdk/MsgDelegate","value":{"delegator_address":"cosmos13vfzpfmg6jgzfk4rke9glzpngrzucjtanq9awx","validator_address":"cosmosvaloper10e4vsut6suau8tk9m6dnrm0slgd6npe3jx5xpv","value":{"amount":"8000000000","denom":"uatom"}}},{"type":"cosmos-sdk/MsgDelegate","value":{"delegator_address":"cosmos13vfzpfmg6jgzfk4rke9glzpngrzucjtanq9awx","validator_address":"cosmosvaloper10e4vsut6suau8tk9m6dnrm0slgd6npe3jx5xpv","value":{"amount":"8000000000","denom":"uatom"}}},{"type":"cosmos-sdk/MsgDelegate","value":{"delegator_address":"cosmos13vfzpfmg6jgzfk4rke9glzpngrzucjtanq9awx","validator_address":"cosmosvaloper10e4vsut6suau8tk9m6dnrm0slgd6npe3jx5xpv","value":{"amount":"8000000000","denom":"uatom"}}},{"type":"cosmos-sdk/MsgDelegate","value":{"delegator_address":"cosmos13vfzpfmg6jgzfk4rke9glzpngrzucjtanq9awx", + "validator_address":"cosmosvaloper10e4vsut6suau8tk9m6dnrm0slgd6npe3jx5xpv","value":{"amount":"8000000000","denom":"uatom"}}},{"type":"cosmos-sdk/MsgDelegate","value":{"delegator_address":"cosmos13vfzpfmg6jgzfk4rke9glzpngrzucjtanq9awx","validator_address":"cosmosvaloper10e4vsut6suau8tk9m6dnrm0slgd6npe3jx5xpv","value":{"amount":"8000000000","denom":"uatom"}}},{"type":"cosmos-sdk/MsgDelegate","value":{"delegator_address":"cosmos13vfzpfmg6jgzfk4rke9glzpngrzucjtanq9awx","validator_address":"cosmosvaloper10e4vsut6suau8tk9m6dnrm0slgd6npe3jx5xpv","value":{"amount":"8000000000","denom":"uatom"}}},{"type":"cosmos-sdk/MsgDelegate","value":{"delegator_address":"cosmos13vfzpfmg6jgzfk4rke9glzpngrzucjtanq9awx","validator_address":"cosmosvaloper10e4vsut6suau8tk9m6dnrm0slgd6npe3jx5xpv","value":{"amount":"8000000000","denom":"uatom"}}},{"type":"cosmos-sdk/MsgDelegate","value":{"delegator_address":"cosmos13vfzpfmg6jgzfk4rke9glzpngrzucjtanq9awx","validator_address":"cosmosvaloper10e4vsut6suau8tk9m6dnrm0slgd6npe3jx5xpv","value":{"amount":"8000000000","denom":"uatom"}}},{"type":"cosmos-sdk/MsgDelegate","value":{"delegator_address":"cosmos13vfzpfmg6jgzfk4rke9glzpngrzucjtanq9awx","validator_address":"cosmosvaloper10e4vsut6suau8tk9m6dnrm0slgd6npe3jx5xpv","value":{"amount":"8000000000","denom":"uatom"}}},{"type":"cosmos-sdk/MsgDelegate","value":{"delegator_address":"cosmos13vfzpfmg6jgzfk4rke9glzpngrzucjtanq9awx","validator_address":"cosmosvaloper10e4vsut6suau8tk9m6dnrm0slgd6npe3jx5xpv","value":{"amount":"8000000000","denom":"uatom"}}},{"type":"cosmos-sdk/MsgDelegate","value":{"delegator_address":"cosmos13vfzpfmg6jgzfk4rke9glzpngrzucjtanq9awx","validator_address":"cosmosvaloper10e4vsut6suau8tk9m6dnrm0slgd6npe3jx5xpv","value":{"amount":"8000000000","denom":"uatom"}}},{"type":"cosmos-sdk/MsgDelegate","value":{"delegator_address":"cosmos13vfzpfmg6jgzfk4rke9glzpngrzucjtanq9awx","validator_address":"cosmosvaloper10e4vsut6suau8tk9m6dnrm0slgd6npe3jx5xpv","value":{"amount":"8000000000","denom":"uatom"}}},{"type":"cosmos-sdk/MsgDelegate","value":{"delegator_address":"cosmos13vfzpfmg6jgzfk4rke9glzpngrzucjtanq9awx","validator_address":"cosmosvaloper10e4vsut6suau8tk9m6dnrm0slgd6npe3jx5xpv","value":{"amount":"8000000000","denom":"uatom"}}},{"type":"cosmos-sdk/MsgDelegate","value":{"delegator_address":"cosmos13vfzpfmg6jgzfk4rke9glzpngrzucjtanq9awx","validator_address":"cosmosvaloper10e4vsut6suau8tk9m6dnrm0slgd6npe3jx5xpv","value":{"amount":"8000000000","denom":"uatom"}}},{"type":"cosmos-sdk/MsgDelegate","value":{"delegator_address":"cosmos13vfzpfmg6jgzfk4rke9glzpngrzucjtanq9awx","validator_address":"cosmosvaloper10e4vsut6suau8tk9m6dnrm0slgd6npe3jx5xpv","value":{"amount":"8000000000","denom":"uatom"}}},{"type":"cosmos-sdk/MsgDelegate","value":{"delegator_address":"cosmos13vfzpfmg6jgzfk4rke9glzpngrzucjtanq9awx", + "validator_address":"cosmosvaloper10e4vsut6suau8tk9m6dnrm0slgd6npe3jx5xpv","value":{"amount":"8000000000","denom":"uatom"}}},{"type":"cosmos-sdk/MsgDelegate","value":{"delegator_address":"cosmos13vfzpfmg6jgzfk4rke9glzpngrzucjtanq9awx","validator_address":"cosmosvaloper10e4vsut6suau8tk9m6dnrm0slgd6npe3jx5xpv","value":{"amount":"8000000000","denom":"uatom"}}},{"type":"cosmos-sdk/MsgDelegate","value":{"delegator_address":"cosmos13vfzpfmg6jgzfk4rke9glzpngrzucjtanq9awx","validator_address":"cosmosvaloper10e4vsut6suau8tk9m6dnrm0slgd6npe3jx5xpv","value":{"amount":"8000000000","denom":"uatom"}}},{"type":"cosmos-sdk/MsgDelegate","value":{"delegator_address":"cosmos13vfzpfmg6jgzfk4rke9glzpngrzucjtanq9awx", + "validator_address":"cosmosvaloper10e4vsut6suau8tk9m6dnrm0slgd6npe3jx5xpv","value":{"amount":"8000000000","denom":"uatom"}}},{"type":"cosmos-sdk/MsgDelegate","value":{"delegator_address":"cosmos13vfzpfmg6jgzfk4rke9glzpngrzucjtanq9awx","validator_address":"cosmosvaloper10e4vsut6suau8tk9m6dnrm0slgd6npe3jx5xpv","value":{"amount":"8000000000","denom":"uatom"}}},{"type":"cosmos-sdk/MsgDelegate","value":{"delegator_address":"cosmos13vfzpfmg6jgzfk4rke9glzpngrzucjtanq9awx","validator_address":"cosmosvaloper10e4vsut6suau8tk9m6dnrm0slgd6npe3jx5xpv","value":{"amount":"8000000000","denom":"uatom"}}},{"type":"cosmos-sdk/MsgDelegate","value":{"delegator_address":"cosmos13vfzpfmg6jgzfk4rke9glzpngrzucjtanq9awx","validator_address":"cosmosvaloper10e4vsut6suau8tk9m6dnrm0slgd6npe3jx5xpv","value":{"amount":"8000000000","denom":"uatom"}}},{"type":"cosmos-sdk/MsgDelegate","value":{"delegator_address":"cosmos13vfzpfmg6jgzfk4rke9glzpngrzucjtanq9awx","validator_address":"cosmosvaloper10e4vsut6suau8tk9m6dnrm0slgd6npe3jx5xpv","value":{"amount":"8000000000","denom":"uatom"}}},{"type":"cosmos-sdk/MsgDelegate","value":{"delegator_address":"cosmos13vfzpfmg6jgzfk4rke9glzpngrzucjtanq9awx","validator_address":"cosmosvaloper10e4vsut6suau8tk9m6dnrm0slgd6npe3jx5xpv","value":{"amount":"8000000000","denom":"uatom"}}},{"type":"cosmos-sdk/MsgDelegate","value":{"delegator_address":"cosmos13vfzpfmg6jgzfk4rke9glzpngrzucjtanq9awx", + "validator_address":"cosmosvaloper10e4vsut6suau8tk9m6dnrm0slgd6npe3jx5xpv","value":{"amount":"8000000000","denom":"uatom"}}},{"type":"cosmos-sdk/MsgDelegate","value":{"delegator_address":"cosmos13vfzpfmg6jgzfk4rke9glzpngrzucjtanq9awx","validator_address":"cosmosvaloper10e4vsut6suau8tk9m6dnrm0slgd6npe3jx5xpv","value":{"amount":"8000000000","denom":"uatom"}}},{"type":"cosmos-sdk/MsgDelegate","value":{"delegator_address":"cosmos13vfzpfmg6jgzfk4rke9glzpngrzucjtanq9awx","validator_address":"cosmosvaloper10e4vsut6suau8tk9m6dnrm0slgd6npe3jx5xpv","value":{"amount":"8000000000","denom":"uatom"}}},{"type":"cosmos-sdk/MsgDelegate","value":{"delegator_address":"cosmos13vfzpfmg6jgzfk4rke9glzpngrzucjtanq9awx","validator_address":"cosmosvaloper10e4vsut6suau8tk9m6dnrm0slgd6npe3jx5xpv","value":{"amount":"8000000000","denom":"uatom"}}},{"type":"cosmos-sdk/MsgDelegate","value":{"delegator_address":"cosmos13vfzpfmg6jgzfk4rke9glzpngrzucjtanq9awx","validator_address":"cosmosvaloper10e4vsut6suau8tk9m6dnrm0slgd6npe3jx5xpv","value":{"amount":"8000000000","denom":"uatom"}}},{"type":"cosmos-sdk/MsgDelegate","value":{"delegator_address":"cosmos13vfzpfmg6jgzfk4rke9glzpngrzucjtanq9awx","validator_address":"cosmosvaloper10e4vsut6suau8tk9m6dnrm0slgd6npe3jx5xpv","value":{"amount":"8000000000","denom":"uatom"}}},{"type":"cosmos-sdk/MsgDelegate","value":{"delegator_address":"cosmos13vfzpfmg6jgzfk4rke9glzpngrzucjtanq9awx","validator_address":"cosmosvaloper10e4vsut6suau8tk9m6dnrm0slgd6npe3jx5xpv","value":{"amount":"8000000000","denom":"uatom"}}},{"type":"cosmos-sdk/MsgDelegate","value":{"delegator_address":"cosmos13vfzpfmg6jgzfk4rke9glzpngrzucjtanq9awx","validator_address":"cosmosvaloper10e4vsut6suau8tk9m6dnrm0slgd6npe3jx5xpv","value":{"amount":"8000000000","denom":"uatom"}}},{"type":"cosmos-sdk/MsgDelegate","value":{"delegator_address":"cosmos13vfzpfmg6jgzfk4rke9glzpngrzucjtanq9awx","validator_address":"cosmosvaloper10e4vsut6suau8tk9m6dnrm0slgd6npe3jx5xpv","value":{"amount":"8000000000","denom":"uatom"}}},{"type":"cosmos-sdk/MsgDelegate","value":{"delegator_address":"cosmos13vfzpfmg6jgzfk4rke9glzpngrzucjtanq9awx","validator_address":"cosmosvaloper10e4vsut6suau8tk9m6dnrm0slgd6npe3jx5xpv","value":{"amount":"8000000000","denom":"uatom"}}},{"type":"cosmos-sdk/MsgDelegate","value":{"delegator_address":"cosmos13vfzpfmg6jgzfk4rke9glzpngrzucjtanq9awx","validator_address":"cosmosvaloper10e4vsut6suau8tk9m6dnrm0slgd6npe3jx5xpv","value":{"amount":"8000000000","denom":"uatom"}}},{"type":"cosmos-sdk/MsgDelegate","value":{"delegator_address":"cosmos13vfzpfmg6jgzfk4rke9glzpngrzucjtanq9awx","validator_address":"cosmosvaloper10e4vsut6suau8tk9m6dnrm0slgd6npe3jx5xpv","value":{"amount":"8000000000","denom":"uatom"}}},{"type":"cosmos-sdk/MsgDelegate","value":{"delegator_address":"cosmos13vfzpfmg6jgzfk4rke9glzpngrzucjtanq9awx", + "validator_address":"cosmosvaloper10e4vsut6suau8tk9m6dnrm0slgd6npe3jx5xpv","value":{"amount":"8000000000","denom":"uatom"}}},{"type":"cosmos-sdk/MsgDelegate","value":{"delegator_address":"cosmos13vfzpfmg6jgzfk4rke9glzpngrzucjtanq9awx","validator_address":"cosmosvaloper10e4vsut6suau8tk9m6dnrm0slgd6npe3jx5xpv","value":{"amount":"8000000000","denom":"uatom"}}},{"type":"cosmos-sdk/MsgDelegate","value":{"delegator_address":"cosmos13vfzpfmg6jgzfk4rke9glzpngrzucjtanq9awx","validator_address":"cosmosvaloper10e4vsut6suau8tk9m6dnrm0slgd6npe3jx5xpv","value":{"amount":"8000000000","denom":"uatom"}}},{"type":"cosmos-sdk/MsgDelegate","value":{"delegator_address":"cosmos13vfzpfmg6jgzfk4rke9glzpngrzucjtanq9awx", + "validator_address":"cosmosvaloper10e4vsut6suau8tk9m6dnrm0slgd6npe3jx5xpv","value":{"amount":"8000000000","denom":"uatom"}}},{"type":"cosmos-sdk/MsgDelegate","value":{"delegator_address":"cosmos13vfzpfmg6jgzfk4rke9glzpngrzucjtanq9awx","validator_address":"cosmosvaloper10e4vsut6suau8tk9m6dnrm0slgd6npe3jx5xpv","value":{"amount":"8000000000","denom":"uatom"}}},{"type":"cosmos-sdk/MsgDelegate","value":{"delegator_address":"cosmos13vfzpfmg6jgzfk4rke9glzpngrzucjtanq9awx","validator_address":"cosmosvaloper10e4vsut6suau8tk9m6dnrm0slgd6npe3jx5xpv","value":{"amount":"8000000000","denom":"uatom"}}},{"type":"cosmos-sdk/MsgDelegate","value":{"delegator_address":"cosmos13vfzpfmg6jgzfk4rke9glzpngrzucjtanq9awx","validator_address":"cosmosvaloper10e4vsut6suau8tk9m6dnrm0slgd6npe3jx5xpv","value":{"amount":"8000000000","denom":"uatom"}}},{"type":"cosmos-sdk/MsgDelegate","value":{"delegator_address":"cosmos13vfzpfmg6jgzfk4rke9glzpngrzucjtanq9awx","validator_address":"cosmosvaloper10e4vsut6suau8tk9m6dnrm0slgd6npe3jx5xpv","value":{"amount":"8000000000","denom":"uatom"}}},{"type":"cosmos-sdk/MsgDelegate","value":{"delegator_address":"cosmos13vfzpfmg6jgzfk4rke9glzpngrzucjtanq9awx","validator_address":"cosmosvaloper10e4vsut6suau8tk9m6dnrm0slgd6npe3jx5xpv","value":{"amount":"8000000000","denom":"uatom"}}},{"type":"cosmos-sdk/MsgDelegate","value":{"delegator_address":"cosmos13vfzpfmg6jgzfk4rke9glzpngrzucjtanq9awx", + "validator_address":"cosmosvaloper10e4vsut6suau8tk9m6dnrm0slgd6npe3jx5xpv","value":{"amount":"8000000000","denom":"uatom"}}},{"type":"cosmos-sdk/MsgDelegate","value":{"delegator_address":"cosmos13vfzpfmg6jgzfk4rke9glzpngrzucjtanq9awx","validator_address":"cosmosvaloper10e4vsut6suau8tk9m6dnrm0slgd6npe3jx5xpv","value":{"amount":"8000000000","denom":"uatom"}}},{"type":"cosmos-sdk/MsgDelegate","value":{"delegator_address":"cosmos13vfzpfmg6jgzfk4rke9glzpngrzucjtanq9awx","validator_address":"cosmosvaloper10e4vsut6suau8tk9m6dnrm0slgd6npe3jx5xpv","value":{"amount":"8000000000","denom":"uatom"}}},{"type":"cosmos-sdk/MsgDelegate","value":{"delegator_address":"cosmos13vfzpfmg6jgzfk4rke9glzpngrzucjtanq9awx","validator_address":"cosmosvaloper10e4vsut6suau8tk9m6dnrm0slgd6npe3jx5xpv","value":{"amount":"8000000000","denom":"uatom"}}},{"type":"cosmos-sdk/MsgDelegate","value":{"delegator_address":"cosmos13vfzpfmg6jgzfk4rke9glzpngrzucjtanq9awx","validator_address":"cosmosvaloper10e4vsut6suau8tk9m6dnrm0slgd6npe3jx5xpv","value":{"amount":"8000000000","denom":"uatom"}}},{"type":"cosmos-sdk/MsgDelegate","value":{"delegator_address":"cosmos13vfzpfmg6jgzfk4rke9glzpngrzucjtanq9awx","validator_address":"cosmosvaloper10e4vsut6suau8tk9m6dnrm0slgd6npe3jx5xpv","value":{"amount":"8000000000","denom":"uatom"}}},{"type":"cosmos-sdk/MsgDelegate","value":{"delegator_address":"cosmos13vfzpfmg6jgzfk4rke9glzpngrzucjtanq9awx","validator_address":"cosmosvaloper10e4vsut6suau8tk9m6dnrm0slgd6npe3jx5xpv","value":{"amount":"8000000000","denom":"uatom"}}},{"type":"cosmos-sdk/MsgDelegate","value":{"delegator_address":"cosmos13vfzpfmg6jgzfk4rke9glzpngrzucjtanq9awx","validator_address":"cosmosvaloper10e4vsut6suau8tk9m6dnrm0slgd6npe3jx5xpv","value":{"amount":"8000000000","denom":"uatom"}}},{"type":"cosmos-sdk/MsgDelegate","value":{"delegator_address":"cosmos13vfzpfmg6jgzfk4rke9glzpngrzucjtanq9awx","validator_address":"cosmosvaloper10e4vsut6suau8tk9m6dnrm0slgd6npe3jx5xpv","value":{"amount":"8000000000","denom":"uatom"}}},{"type":"cosmos-sdk/MsgDelegate","value":{"delegator_address":"cosmos13vfzpfmg6jgzfk4rke9glzpngrzucjtanq9awx","validator_address":"cosmosvaloper10e4vsut6suau8tk9m6dnrm0slgd6npe3jx5xpv","value":{"amount":"8000000000","denom":"uatom"}}},{"type":"cosmos-sdk/MsgDelegate","value":{"delegator_address":"cosmos13vfzpfmg6jgzfk4rke9glzpngrzucjtanq9awx","validator_address":"cosmosvaloper10e4vsut6suau8tk9m6dnrm0slgd6npe3jx5xpv","value":{"amount":"8000000000","denom":"uatom"}}},{"type":"cosmos-sdk/MsgDelegate","value":{"delegator_address":"cosmos13vfzpfmg6jgzfk4rke9glzpngrzucjtanq9awx","validator_address":"cosmosvaloper10e4vsut6suau8tk9m6dnrm0slgd6npe3jx5xpv","value":{"amount":"8000000000","denom":"uatom"}}},{"type":"cosmos-sdk/MsgDelegate","value":{"delegator_address":"cosmos13vfzpfmg6jgzfk4rke9glzpngrzucjtanq9awx", + "validator_address":"cosmosvaloper10e4vsut6suau8tk9m6dnrm0slgd6npe3jx5xpv","value":{"amount":"8000000000","denom":"uatom"}}},{"type":"cosmos-sdk/MsgDelegate","value":{"delegator_address":"cosmos13vfzpfmg6jgzfk4rke9glzpngrzucjtanq9awx","validator_address":"cosmosvaloper10e4vsut6suau8tk9m6dnrm0slgd6npe3jx5xpv","value":{"amount":"8000000000","denom":"uatom"}}},{"type":"cosmos-sdk/MsgDelegate","value":{"delegator_address":"cosmos13vfzpfmg6jgzfk4rke9glzpngrzucjtanq9awx","validator_address":"cosmosvaloper10e4vsut6suau8tk9m6dnrm0slgd6npe3jx5xpv","value":{"amount":"8000000000","denom":"uatom"}}},{"type":"cosmos-sdk/MsgDelegate","value":{"delegator_address":"cosmos13vfzpfmg6jgzfk4rke9glzpngrzucjtanq9awx", + "validator_address":"cosmosvaloper10e4vsut6suau8tk9m6dnrm0slgd6npe3jx5xpv","value":{"amount":"8000000000","denom":"uatom"}}},{"type":"cosmos-sdk/MsgDelegate","value":{"delegator_address":"cosmos13vfzpfmg6jgzfk4rke9glzpngrzucjtanq9awx","validator_address":"cosmosvaloper10e4vsut6suau8tk9m6dnrm0slgd6npe3jx5xpv","value":{"amount":"8000000000","denom":"uatom"}}},{"type":"cosmos-sdk/MsgDelegate","value":{"delegator_address":"cosmos13vfzpfmg6jgzfk4rke9glzpngrzucjtanq9awx","validator_address":"cosmosvaloper10e4vsut6suau8tk9m6dnrm0slgd6npe3jx5xpv","value":{"amount":"8000000000","denom":"uatom"}}},{"type":"cosmos-sdk/MsgDelegate","value":{"delegator_address":"cosmos13vfzpfmg6jgzfk4rke9glzpngrzucjtanq9awx","validator_address":"cosmosvaloper10e4vsut6suau8tk9m6dnrm0slgd6npe3jx5xpv","value":{"amount":"8000000000","denom":"uatom"}}},{"type":"cosmos-sdk/MsgDelegate","value":{"delegator_address":"cosmos13vfzpfmg6jgzfk4rke9glzpngrzucjtanq9awx","validator_address":"cosmosvaloper10e4vsut6suau8tk9m6dnrm0slgd6npe3jx5xpv","value":{"amount":"8000000000","denom":"uatom"}}},{"type":"cosmos-sdk/MsgDelegate","value":{"delegator_address":"cosmos13vfzpfmg6jgzfk4rke9glzpngrzucjtanq9awx","validator_address":"cosmosvaloper10e4vsut6suau8tk9m6dnrm0slgd6npe3jx5xpv","value":{"amount":"8000000000","denom":"uatom"}}},{"type":"cosmos-sdk/MsgDelegate","value":{"delegator_address":"cosmos13vfzpfmg6jgzfk4rke9glzpngrzucjtanq9awx", + "validator_address":"cosmosvaloper10e4vsut6suau8tk9m6dnrm0slgd6npe3jx5xpv","value":{"amount":"8000000000","denom":"uatom"}}},{"type":"cosmos-sdk/MsgDelegate","value":{"delegator_address":"cosmos13vfzpfmg6jgzfk4rke9glzpngrzucjtanq9awx","validator_address":"cosmosvaloper10e4vsut6suau8tk9m6dnrm0slgd6npe3jx5xpv","value":{"amount":"8000000000","denom":"uatom"}}},{"type":"cosmos-sdk/MsgDelegate","value":{"delegator_address":"cosmos13vfzpfmg6jgzfk4rke9glzpngrzucjtanq9awx","validator_address":"cosmosvaloper10e4vsut6suau8tk9m6dnrm0slgd6npe3jx5xpv","value":{"amount":"8000000000","denom":"uatom"}}},{"type":"cosmos-sdk/MsgDelegate","value":{"delegator_address":"cosmos13vfzpfmg6jgzfk4rke9glzpngrzucjtanq9awx","validator_address":"cosmosvaloper10e4vsut6suau8tk9m6dnrm0slgd6npe3jx5xpv","value":{"amount":"8000000000","denom":"uatom"}}},{"type":"cosmos-sdk/MsgDelegate","value":{"delegator_address":"cosmos13vfzpfmg6jgzfk4rke9glzpngrzucjtanq9awx","validator_address":"cosmosvaloper10e4vsut6suau8tk9m6dnrm0slgd6npe3jx5xpv","value":{"amount":"8000000000","denom":"uatom"}}},{"type":"cosmos-sdk/MsgDelegate","value":{"delegator_address":"cosmos13vfzpfmg6jgzfk4rke9glzpngrzucjtanq9awx","validator_address":"cosmosvaloper10e4vsut6suau8tk9m6dnrm0slgd6npe3jx5xpv","value":{"amount":"8000000000","denom":"uatom"}}},{"type":"cosmos-sdk/MsgDelegate","value":{"delegator_address":"cosmos13vfzpfmg6jgzfk4rke9glzpngrzucjtanq9awx","validator_address":"cosmosvaloper10e4vsut6suau8tk9m6dnrm0slgd6npe3jx5xpv","value":{"amount":"8000000000","denom":"uatom"}}},{"type":"cosmos-sdk/MsgDelegate","value":{"delegator_address":"cosmos13vfzpfmg6jgzfk4rke9glzpngrzucjtanq9awx","validator_address":"cosmosvaloper10e4vsut6suau8tk9m6dnrm0slgd6npe3jx5xpv","value":{"amount":"8000000000","denom":"uatom"}}},{"type":"cosmos-sdk/MsgDelegate","value":{"delegator_address":"cosmos13vfzpfmg6jgzfk4rke9glzpngrzucjtanq9awx","validator_address":"cosmosvaloper10e4vsut6suau8tk9m6dnrm0slgd6npe3jx5xpv","value":{"amount":"8000000000","denom":"uatom"}}},{"type":"cosmos-sdk/MsgDelegate","value":{"delegator_address":"cosmos13vfzpfmg6jgzfk4rke9glzpngrzucjtanq9awx","validator_address":"cosmosvaloper10e4vsut6suau8tk9m6dnrm0slgd6npe3jx5xpv","value":{"amount":"8000000000","denom":"uatom"}}},{"type":"cosmos-sdk/MsgDelegate","value":{"delegator_address":"cosmos13vfzpfmg6jgzfk4rke9glzpngrzucjtanq9awx","validator_address":"cosmosvaloper10e4vsut6suau8tk9m6dnrm0slgd6npe3jx5xpv","value":{"amount":"8000000000","denom":"uatom"}}},{"type":"cosmos-sdk/MsgDelegate","value":{"delegator_address":"cosmos13vfzpfmg6jgzfk4rke9glzpngrzucjtanq9awx","validator_address":"cosmosvaloper10e4vsut6suau8tk9m6dnrm0slgd6npe3jx5xpv","value":{"amount":"8000000000","denom":"uatom"}}},{"type":"cosmos-sdk/MsgDelegate","value":{"delegator_address":"cosmos13vfzpfmg6jgzfk4rke9glzpngrzucjtanq9awx", + "validator_address":"cosmosvaloper10e4vsut6suau8tk9m6dnrm0slgd6npe3jx5xpv","value":{"amount":"8000000000","denom":"uatom"}}},{"type":"cosmos-sdk/MsgDelegate","value":{"delegator_address":"cosmos13vfzpfmg6jgzfk4rke9glzpngrzucjtanq9awx","validator_address":"cosmosvaloper10e4vsut6suau8tk9m6dnrm0slgd6npe3jx5xpv","value":{"amount":"8000000000","denom":"uatom"}}},{"type":"cosmos-sdk/MsgDelegate","value":{"delegator_address":"cosmos13vfzpfmg6jgzfk4rke9glzpngrzucjtanq9awx","validator_address":"cosmosvaloper10e4vsut6suau8tk9m6dnrm0slgd6npe3jx5xpv","value":{"amount":"8000000000","denom":"uatom"}}},{"type":"cosmos-sdk/MsgDelegate","value":{"delegator_address":"cosmos13vfzpfmg6jgzfk4rke9glzpngrzucjtanq9awx", + "validator_address":"cosmosvaloper10e4vsut6suau8tk9m6dnrm0slgd6npe3jx5xpv","value":{"amount":"8000000000","denom":"uatom"}}},{"type":"cosmos-sdk/MsgDelegate","value":{"delegator_address":"cosmos13vfzpfmg6jgzfk4rke9glzpngrzucjtanq9awx","validator_address":"cosmosvaloper10e4vsut6suau8tk9m6dnrm0slgd6npe3jx5xpv","value":{"amount":"8000000000","denom":"uatom"}}},{"type":"cosmos-sdk/MsgDelegate","value":{"delegator_address":"cosmos13vfzpfmg6jgzfk4rke9glzpngrzucjtanq9awx","validator_address":"cosmosvaloper10e4vsut6suau8tk9m6dnrm0slgd6npe3jx5xpv","value":{"amount":"8000000000","denom":"uatom"}}},{"type":"cosmos-sdk/MsgDelegate","value":{"delegator_address":"cosmos13vfzpfmg6jgzfk4rke9glzpngrzucjtanq9awx","validator_address":"cosmosvaloper10e4vsut6suau8tk9m6dnrm0slgd6npe3jx5xpv","value":{"amount":"8000000000","denom":"uatom"}}},{"type":"cosmos-sdk/MsgDelegate","value":{"delegator_address":"cosmos13vfzpfmg6jgzfk4rke9glzpngrzucjtanq9awx","validator_address":"cosmosvaloper10e4vsut6suau8tk9m6dnrm0slgd6npe3jx5xpv","value":{"amount":"8000000000","denom":"uatom"}}},{"type":"cosmos-sdk/MsgDelegate","value":{"delegator_address":"cosmos13vfzpfmg6jgzfk4rke9glzpngrzucjtanq9awx","validator_address":"cosmosvaloper10e4vsut6suau8tk9m6dnrm0slgd6npe3jx5xpv","value":{"amount":"8000000000","denom":"uatom"}}},{"type":"cosmos-sdk/MsgDelegate","value":{"delegator_address":"cosmos13vfzpfmg6jgzfk4rke9glzpngrzucjtanq9awx", + "validator_address":"cosmosvaloper10e4vsut6suau8tk9m6dnrm0slgd6npe3jx5xpv","value":{"amount":"8000000000","denom":"uatom"}}},{"type":"cosmos-sdk/MsgDelegate","value":{"delegator_address":"cosmos13vfzpfmg6jgzfk4rke9glzpngrzucjtanq9awx","validator_address":"cosmosvaloper10e4vsut6suau8tk9m6dnrm0slgd6npe3jx5xpv","value":{"amount":"8000000000","denom":"uatom"}}},{"type":"cosmos-sdk/MsgDelegate","value":{"delegator_address":"cosmos13vfzpfmg6jgzfk4rke9glzpngrzucjtanq9awx","validator_address":"cosmosvaloper10e4vsut6suau8tk9m6dnrm0slgd6npe3jx5xpv","value":{"amount":"8000000000","denom":"uatom"}}},{"type":"cosmos-sdk/MsgDelegate","value":{"delegator_address":"cosmos13vfzpfmg6jgzfk4rke9glzpngrzucjtanq9awx","validator_address":"cosmosvaloper10e4vsut6suau8tk9m6dnrm0slgd6npe3jx5xpv","value":{"amount":"8000000000","denom":"uatom"}}},{"type":"cosmos-sdk/MsgDelegate","value":{"delegator_address":"cosmos13vfzpfmg6jgzfk4rke9glzpngrzucjtanq9awx","validator_address":"cosmosvaloper10e4vsut6suau8tk9m6dnrm0slgd6npe3jx5xpv","value":{"amount":"8000000000","denom":"uatom"}}},{"type":"cosmos-sdk/MsgDelegate","value":{"delegator_address":"cosmos13vfzpfmg6jgzfk4rke9glzpngrzucjtanq9awx","validator_address":"cosmosvaloper10e4vsut6suau8tk9m6dnrm0slgd6npe3jx5xpv","value":{"amount":"8000000000","denom":"uatom"}}},{"type":"cosmos-sdk/MsgDelegate","value":{"delegator_address":"cosmos13vfzpfmg6jgzfk4rke9glzpngrzucjtanq9awx","validator_address":"cosmosvaloper10e4vsut6suau8tk9m6dnrm0slgd6npe3jx5xpv","value":{"amount":"8000000000","denom":"uatom"}}},{"type":"cosmos-sdk/MsgDelegate","value":{"delegator_address":"cosmos13vfzpfmg6jgzfk4rke9glzpngrzucjtanq9awx","validator_address":"cosmosvaloper10e4vsut6suau8tk9m6dnrm0slgd6npe3jx5xpv","value":{"amount":"8000000000","denom":"uatom"}}},{"type":"cosmos-sdk/MsgDelegate","value":{"delegator_address":"cosmos13vfzpfmg6jgzfk4rke9glzpngrzucjtanq9awx","validator_address":"cosmosvaloper10e4vsut6suau8tk9m6dnrm0slgd6npe3jx5xpv","value":{"amount":"8000000000","denom":"uatom"}}},{"type":"cosmos-sdk/MsgDelegate","value":{"delegator_address":"cosmos13vfzpfmg6jgzfk4rke9glzpngrzucjtanq9awx","validator_address":"cosmosvaloper10e4vsut6suau8tk9m6dnrm0slgd6npe3jx5xpv","value":{"amount":"8000000000","denom":"uatom"}}},{"type":"cosmos-sdk/MsgDelegate","value":{"delegator_address":"cosmos13vfzpfmg6jgzfk4rke9glzpngrzucjtanq9awx","validator_address":"cosmosvaloper10e4vsut6suau8tk9m6dnrm0slgd6npe3jx5xpv","value":{"amount":"8000000000","denom":"uatom"}}},{"type":"cosmos-sdk/MsgDelegate","value":{"delegator_address":"cosmos13vfzpfmg6jgzfk4rke9glzpngrzucjtanq9awx","validator_address":"cosmosvaloper10e4vsut6suau8tk9m6dnrm0slgd6npe3jx5xpv","value":{"amount":"8000000000","denom":"uatom"}}},{"type":"cosmos-sdk/MsgDelegate","value":{"delegator_address":"cosmos13vfzpfmg6jgzfk4rke9glzpngrzucjtanq9awx","validator_address":"cosmosvaloper10e4vsut6suau8tk9m6dnrm0slgd6npe3jx5xpv","value":{"amount":"8000000000","denom":"uatom"}}},{"type":"cosmos-sdk/MsgDelegate","value":{"delegator_address":"cosmos13vfzpfmg6jgzfk4rke9glzpngrzucjtanq9awx","validator_address":"cosmosvaloper14kn0kk33szpwus9nh8n87fjel8djx0y070ymmj","value":{"amount":"8000000000","denom":"uatom"}}},{"type":"cosmos-sdk/MsgDelegate","value":{"delegator_address":"cosmos13vfzpfmg6jgzfk4rke9glzpngrzucjtanq9awx","validator_address":"cosmosvaloper14kn0kk33szpwus9nh8n87fjel8djx0y070ymmj","value":{"amount":"8000000000","denom":"uatom"}}},{"type":"cosmos-sdk/MsgDelegate","value":{"delegator_address":"cosmos13vfzpfmg6jgzfk4rke9glzpngrzucjtanq9awx","validator_address":"cosmosvaloper14kn0kk33szpwus9nh8n87fjel8djx0y070ymmj","value":{"amount":"8000000000","denom":"uatom"}}}],"sequence":"1"})"; + + parsed_json_t json; + parser_error_t err; + + err = JSON_PARSE(&json, transaction); + ASSERT_EQ( err, parser_json_too_many_tokens); + + err = tx_validate(&json); + EXPECT_EQ(err, parser_json_missing_chain_id) << "Validation failed, error: " << parser_getErrorDescription(err); + } +} diff --git a/tests/ui_output.cpp b/tests/ui_output.cpp new file mode 100644 index 00000000..0425eb6e --- /dev/null +++ b/tests/ui_output.cpp @@ -0,0 +1,102 @@ +/******************************************************************************* +* (c) 2019 Zondax GmbH +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +********************************************************************************/ + +#include +#include "util/testcases.h" + +#include +#include +#include "common/parser.h" +#include "util/common.h" +#include "app_mode.h" + +using ::testing::TestWithParam; +using ::testing::Values; + +void validate_testcase(const testcase_t &tc) { + parser_context_t ctx; + parser_error_t err; + + const auto *buffer = (const uint8_t *) tc.tx.c_str(); + size_t bufferLen = tc.tx.size(); + + err = parser_parse(&ctx, buffer, bufferLen); + ASSERT_EQ(parser_getErrorDescription(err), tc.parsingErr) << "Parsing error mismatch"; + + if (err != parser_ok) + return; + + err = parser_validate(&ctx); + EXPECT_EQ( parser_getErrorDescription(err), tc.validationErr) << "Validation error mismatch"; +} + +void check_testcase(const testcase_t &tc) { + parser_context_t ctx; + parser_error_t err; + + app_mode_set_expert(tc.expert); + + const auto *buffer = (const uint8_t *) tc.tx.c_str(); + size_t bufferLen = tc.tx.size(); + + err = parser_parse(&ctx, buffer, bufferLen); + ASSERT_EQ(parser_getErrorDescription(err), tc.parsingErr) << "Parsing error mismatch"; + + if (err != parser_ok) + return; + + auto output = dumpUI(&ctx, 40, 40); + + for (const auto &i : output) { + std::cout << i << std::endl; + } + std::cout << std::endl << std::endl; + + EXPECT_EQ(output.size(), tc.expected.size()); + for (size_t i = 0; i < tc.expected.size(); i++) { + if (i < output.size()) { + EXPECT_THAT(output[i], testing::Eq(tc.expected[i])); + } + } +} + +/////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////// + +class JsonTests : public ::testing::TestWithParam { +public: + struct PrintToStringParamName { + template + std::string operator()(const testing::TestParamInfo &info) const { + auto p = static_cast(info.param); + std::stringstream ss; + ss << p.description; + return ss.str(); + } + }; +}; + +INSTANTIATE_TEST_SUITE_P ( + JsonTestCases, + JsonTests, + ::testing::ValuesIn(GetJsonTestCases("testcases/manual.json")), + JsonTests::PrintToStringParamName() +); + +TEST_P(JsonTests, ValidateTestcase) { validate_testcase(GetParam()); } + +TEST_P(JsonTests, CheckUIOutput) { check_testcase(GetParam()); } diff --git a/tests/util/common.cpp b/tests/util/common.cpp new file mode 100644 index 00000000..795e55f4 --- /dev/null +++ b/tests/util/common.cpp @@ -0,0 +1,59 @@ +/******************************************************************************* +* (c) 2018 Zondax GmbH +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +********************************************************************************/ +#include +#include +#include +#include "common.h" + +std::vector dumpUI(parser_context_t *ctx, + uint16_t maxKeyLen, + uint16_t maxValueLen) { + uint8_t numItems; + parser_error_t err = parser_getNumItems(ctx, &numItems); + + auto answer = std::vector(); + + for (uint16_t idx = 0; idx < numItems; idx++) { + char keyBuffer[1000]; + char valueBuffer[1000]; + uint8_t pageIdx = 0; + uint8_t pageCount = 1; + + while (pageIdx < pageCount) { + std::stringstream ss; + + auto err = parser_getItem(ctx, + idx, + keyBuffer, maxKeyLen, + valueBuffer, maxValueLen, + pageIdx, &pageCount); + + ss << idx << " | " << keyBuffer << " : "; + + if (err == parser_ok) { + ss << valueBuffer; + } else { + ss << parser_getErrorDescription(err); + } + + answer.push_back(ss.str()); + + pageIdx++; + } + } + + return answer; +} diff --git a/tests/util/common.h b/tests/util/common.h new file mode 100644 index 00000000..e1c14d62 --- /dev/null +++ b/tests/util/common.h @@ -0,0 +1,31 @@ +/******************************************************************************* +* (c) 2018 Zondax GmbH +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +********************************************************************************/ + +#pragma once + +#include +#include +#include + +#define EXPECT_EQ_STR(_STR1, _STR2, _ERROR_MESSAGE) { if (_STR1 != nullptr & _STR2 != nullptr) \ +EXPECT_TRUE(!strcmp(_STR1, _STR2)) << _ERROR_MESSAGE << ", expected: " << _STR2 << ", received: " << _STR1; \ +else FAIL() << "One of the strings is null"; } + +parser_error_t parse_tx(parsed_json_t *parsed_json, const char *tx); + +std::vector dumpUI(parser_context_t *ctx, uint16_t maxKeyLen, uint16_t maxValueLen); + +#define JSON_PARSE(parsed_json, buffer) json_parse(parsed_json, buffer, strlen(buffer)) diff --git a/tests/util/testcases.cpp b/tests/util/testcases.cpp new file mode 100644 index 00000000..54d4d8b9 --- /dev/null +++ b/tests/util/testcases.cpp @@ -0,0 +1,71 @@ +/******************************************************************************* +* (c) 2019 Zondax GmbH +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +********************************************************************************/ +#include "testcases.h" +#include +#include +#include + +std::vector GetJsonTestCases(const std::string &filename) { + auto answer = std::vector(); + + Json::CharReaderBuilder builder; + std::shared_ptr obj(new Json::Value()); + + std::ifstream inFile(filename); + EXPECT_TRUE(inFile.is_open()) + << "\n" + << "******************\n" + << "Check that your working directory points to the tests directory\n" + << "In CLion use $PROJECT_DIR$\\tests\n" + << "******************\n"; + if (!inFile.is_open()) + return answer; + + // Retrieve all test cases + JSONCPP_STRING errs; + Json::parseFromStream(builder, inFile, obj.get(), &errs); + std::cout << "Number of testcases: " << obj->size() << std::endl; + answer.reserve(obj->size()); + + for (int i = 0; i < obj->size(); i++) { + auto v = (*obj)[i]; + + Json::StreamWriterBuilder wbuilder; + wbuilder["commentStyle"] = "None"; + wbuilder["indentation"] = ""; + std::string txStr = Json::writeString(wbuilder, v["tx"]); + + auto expected = std::vector(); + for (const auto j : v["expected"]) { + expected.push_back(j.asString()); + } + + bool expert = false; + expert = v["expert"].asBool(); + + answer.push_back( + testcase_t{ + v["name"].asString(), + txStr, + v["parsingErr"].asString(), + v["validationErr"].asString(), + expected, + expert + }); + } + + return answer; +} diff --git a/tests/util/testcases.h b/tests/util/testcases.h new file mode 100644 index 00000000..ab29ad2d --- /dev/null +++ b/tests/util/testcases.h @@ -0,0 +1,29 @@ +/******************************************************************************* +* (c) 2019 Zondax GmbH +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +********************************************************************************/ +#pragma once +#include +#include + +typedef struct { + std::string description; + std::string tx; + std::string parsingErr; + std::string validationErr; + std::vector expected; + bool expert; +} testcase_t; + +std::vector GetJsonTestCases(const std::string& filename); diff --git a/tests_zemu/.babelrc b/tests_zemu/.babelrc new file mode 100644 index 00000000..8806109a --- /dev/null +++ b/tests_zemu/.babelrc @@ -0,0 +1,13 @@ +{ + "presets": [ + "@babel/preset-env" + ], + "plugins": [ + [ + "@babel/plugin-transform-runtime", + { + "regenerator": true + } + ] + ] +} diff --git a/tests_zemu/.gitignore b/tests_zemu/.gitignore new file mode 100644 index 00000000..033833d9 --- /dev/null +++ b/tests_zemu/.gitignore @@ -0,0 +1,2 @@ +/node_modules +/yarn-error.log diff --git a/tests_zemu/globalsetup.js b/tests_zemu/globalsetup.js new file mode 100644 index 00000000..ab86d45f --- /dev/null +++ b/tests_zemu/globalsetup.js @@ -0,0 +1,15 @@ +import Zemu from "@zondax/zemu"; + +const catchExit = async () => { + process.on("SIGINT", () => { + Zemu.stopAllEmuContainers(function () { + process.exit(); + }); + }); +}; + +module.exports = async () => { + await catchExit(); + await Zemu.checkAndPullImage(); + await Zemu.stopAllEmuContainers(); +}; diff --git a/tests_zemu/jest.config.js b/tests_zemu/jest.config.js new file mode 100644 index 00000000..fbceb386 --- /dev/null +++ b/tests_zemu/jest.config.js @@ -0,0 +1,33 @@ +// For a detailed explanation regarding each configuration property, visit: +// https://jestjs.io/docs/en/configuration.html + +module.exports = { + modulePaths: ["/src", "/tests"], + + moduleNameMapper: { + "^jest$": "/jest.js", + }, + + // Automatically clear mock calls and instances between every test + clearMocks: true, + + // The directory where Jest should output its coverage files + coverageDirectory: "coverage", + + globalSetup: "/globalsetup.js", + + // A list of paths to directories that Jest should use to search for files in + roots: [""], + + runner: "jest-serial-runner", + + // The test environment that will be used for testing + testEnvironment: "node", + + // The glob patterns Jest uses to detect test files + testMatch: [ + "**/__tests__/**/*.[jt]s?(x)", + "**/?(*.)+(spec|test).[tj]s?(x)", + "**/?(*.)+(ispec|test).[tj]s?(x)", + ], +}; diff --git a/tests_zemu/jest.js b/tests_zemu/jest.js new file mode 100644 index 00000000..2fa397e7 --- /dev/null +++ b/tests_zemu/jest.js @@ -0,0 +1,2 @@ +export default jest; +export const { expect, test } = global; diff --git a/tests_zemu/package.json b/tests_zemu/package.json new file mode 100644 index 00000000..d74ed3ed --- /dev/null +++ b/tests_zemu/package.json @@ -0,0 +1,41 @@ +{ + "name": "integration-tests", + "author": "Zondax GmbH", + "license": "Apache-2.0", + "version": "1.0.0", + "description": "", + "main": "index.js", + "scripts": { + "test": "jest --detectOpenHandles" + }, + "repository": { + "type": "git", + "url": "git+https://github.com/Zondax/ledger-cosmos" + }, + "keywords": [ + "zondax" + ], + "dependencies": { + "ledger-cosmos-js": "^2", + "@zondax/zemu": "^0.2.4" + }, + "devDependencies": { + "@babel/cli": "^7.8.4", + "@babel/core": "^7.9.0", + "@babel/node": "^7.8.7", + "@babel/plugin-transform-runtime": "^7.9.0", + "@babel/preset-env": "^7.9.5", + "babel-eslint": "^10.1.0", + "babel-jest": "24", + "eslint": "^6.8.0", + "eslint-config-airbnb-base": "^14.1.0", + "eslint-config-prettier": "^6.10.1", + "eslint-plugin-import": "^2.20.2", + "eslint-plugin-jest": "^23.8.2", + "eslint-plugin-prettier": "^3.1.3", + "jest": "24", + "jest-serial-runner": "^1.1.0 ", + "secp256k1": "^4.0.1", + "crypto-js": "4.0.0" + } +} diff --git a/tests_zemu/snapshots-tmp/.gitignore b/tests_zemu/snapshots-tmp/.gitignore new file mode 100644 index 00000000..e33609d2 --- /dev/null +++ b/tests_zemu/snapshots-tmp/.gitignore @@ -0,0 +1 @@ +*.png diff --git a/tests_zemu/snapshots-tmp/show-address-and-sign-basic/.gitkeep b/tests_zemu/snapshots-tmp/show-address-and-sign-basic/.gitkeep new file mode 100644 index 00000000..33662f55 --- /dev/null +++ b/tests_zemu/snapshots-tmp/show-address-and-sign-basic/.gitkeep @@ -0,0 +1 @@ +/* diff --git a/tests_zemu/snapshots-tmp/show-address-huge/.gitkeep b/tests_zemu/snapshots-tmp/show-address-huge/.gitkeep new file mode 100644 index 00000000..33662f55 --- /dev/null +++ b/tests_zemu/snapshots-tmp/show-address-huge/.gitkeep @@ -0,0 +1 @@ +/* diff --git a/tests_zemu/snapshots-tmp/show-address/.gitkeep b/tests_zemu/snapshots-tmp/show-address/.gitkeep new file mode 100644 index 00000000..33662f55 --- /dev/null +++ b/tests_zemu/snapshots-tmp/show-address/.gitkeep @@ -0,0 +1 @@ +/* diff --git a/tests_zemu/snapshots-tmp/sign-basic-combined/.gitkeep b/tests_zemu/snapshots-tmp/sign-basic-combined/.gitkeep new file mode 100644 index 00000000..33662f55 --- /dev/null +++ b/tests_zemu/snapshots-tmp/sign-basic-combined/.gitkeep @@ -0,0 +1 @@ +/* diff --git a/tests_zemu/snapshots-tmp/sign-basic/.gitkeep b/tests_zemu/snapshots-tmp/sign-basic/.gitkeep new file mode 100644 index 00000000..33662f55 --- /dev/null +++ b/tests_zemu/snapshots-tmp/sign-basic/.gitkeep @@ -0,0 +1 @@ +/* diff --git a/tests_zemu/snapshots-tmp/sign-expert/.gitkeep b/tests_zemu/snapshots-tmp/sign-expert/.gitkeep new file mode 100644 index 00000000..33662f55 --- /dev/null +++ b/tests_zemu/snapshots-tmp/sign-expert/.gitkeep @@ -0,0 +1 @@ +/* diff --git a/tests_zemu/snapshots/show-address-and-sign-basic/0.png b/tests_zemu/snapshots/show-address-and-sign-basic/0.png new file mode 100644 index 00000000..542ff64c Binary files /dev/null and b/tests_zemu/snapshots/show-address-and-sign-basic/0.png differ diff --git a/tests_zemu/snapshots/show-address-and-sign-basic/1.png b/tests_zemu/snapshots/show-address-and-sign-basic/1.png new file mode 100644 index 00000000..88bf23ee Binary files /dev/null and b/tests_zemu/snapshots/show-address-and-sign-basic/1.png differ diff --git a/tests_zemu/snapshots/show-address-and-sign-basic/10.png b/tests_zemu/snapshots/show-address-and-sign-basic/10.png new file mode 100644 index 00000000..610edeb1 Binary files /dev/null and b/tests_zemu/snapshots/show-address-and-sign-basic/10.png differ diff --git a/tests_zemu/snapshots/show-address-and-sign-basic/11.png b/tests_zemu/snapshots/show-address-and-sign-basic/11.png new file mode 100644 index 00000000..4aa28b6b Binary files /dev/null and b/tests_zemu/snapshots/show-address-and-sign-basic/11.png differ diff --git a/tests_zemu/snapshots/show-address-and-sign-basic/12.png b/tests_zemu/snapshots/show-address-and-sign-basic/12.png new file mode 100644 index 00000000..4aa28b6b Binary files /dev/null and b/tests_zemu/snapshots/show-address-and-sign-basic/12.png differ diff --git a/tests_zemu/snapshots/show-address-and-sign-basic/2.png b/tests_zemu/snapshots/show-address-and-sign-basic/2.png new file mode 100644 index 00000000..afa6c07d Binary files /dev/null and b/tests_zemu/snapshots/show-address-and-sign-basic/2.png differ diff --git a/tests_zemu/snapshots/show-address-and-sign-basic/3.png b/tests_zemu/snapshots/show-address-and-sign-basic/3.png new file mode 100644 index 00000000..9794a2c1 Binary files /dev/null and b/tests_zemu/snapshots/show-address-and-sign-basic/3.png differ diff --git a/tests_zemu/snapshots/show-address-and-sign-basic/4.png b/tests_zemu/snapshots/show-address-and-sign-basic/4.png new file mode 100644 index 00000000..1f186b3e Binary files /dev/null and b/tests_zemu/snapshots/show-address-and-sign-basic/4.png differ diff --git a/tests_zemu/snapshots/show-address-and-sign-basic/5.png b/tests_zemu/snapshots/show-address-and-sign-basic/5.png new file mode 100644 index 00000000..871ae859 Binary files /dev/null and b/tests_zemu/snapshots/show-address-and-sign-basic/5.png differ diff --git a/tests_zemu/snapshots/show-address-and-sign-basic/6.png b/tests_zemu/snapshots/show-address-and-sign-basic/6.png new file mode 100644 index 00000000..bcea849f Binary files /dev/null and b/tests_zemu/snapshots/show-address-and-sign-basic/6.png differ diff --git a/tests_zemu/snapshots/show-address-and-sign-basic/7.png b/tests_zemu/snapshots/show-address-and-sign-basic/7.png new file mode 100644 index 00000000..c61097ad Binary files /dev/null and b/tests_zemu/snapshots/show-address-and-sign-basic/7.png differ diff --git a/tests_zemu/snapshots/show-address-and-sign-basic/8.png b/tests_zemu/snapshots/show-address-and-sign-basic/8.png new file mode 100644 index 00000000..d0842b5c Binary files /dev/null and b/tests_zemu/snapshots/show-address-and-sign-basic/8.png differ diff --git a/tests_zemu/snapshots/show-address-and-sign-basic/9.png b/tests_zemu/snapshots/show-address-and-sign-basic/9.png new file mode 100644 index 00000000..ae789b14 Binary files /dev/null and b/tests_zemu/snapshots/show-address-and-sign-basic/9.png differ diff --git a/tests_zemu/snapshots/show-address-huge/0.png b/tests_zemu/snapshots/show-address-huge/0.png new file mode 100644 index 00000000..1f186b3e Binary files /dev/null and b/tests_zemu/snapshots/show-address-huge/0.png differ diff --git a/tests_zemu/snapshots/show-address-huge/1.png b/tests_zemu/snapshots/show-address-huge/1.png new file mode 100644 index 00000000..6ee7de32 Binary files /dev/null and b/tests_zemu/snapshots/show-address-huge/1.png differ diff --git a/tests_zemu/snapshots/show-address-huge/2.png b/tests_zemu/snapshots/show-address-huge/2.png new file mode 100644 index 00000000..a2751624 Binary files /dev/null and b/tests_zemu/snapshots/show-address-huge/2.png differ diff --git a/tests_zemu/snapshots/show-address-huge/3.png b/tests_zemu/snapshots/show-address-huge/3.png new file mode 100644 index 00000000..bf520f45 Binary files /dev/null and b/tests_zemu/snapshots/show-address-huge/3.png differ diff --git a/tests_zemu/snapshots/show-address-huge/4.png b/tests_zemu/snapshots/show-address-huge/4.png new file mode 100644 index 00000000..305185c0 Binary files /dev/null and b/tests_zemu/snapshots/show-address-huge/4.png differ diff --git a/tests_zemu/snapshots/show-address-huge/5.png b/tests_zemu/snapshots/show-address-huge/5.png new file mode 100644 index 00000000..f345cffa Binary files /dev/null and b/tests_zemu/snapshots/show-address-huge/5.png differ diff --git a/tests_zemu/snapshots/show-address-huge/6.png b/tests_zemu/snapshots/show-address-huge/6.png new file mode 100644 index 00000000..159079fd Binary files /dev/null and b/tests_zemu/snapshots/show-address-huge/6.png differ diff --git a/tests_zemu/snapshots/show-address-huge/7.png b/tests_zemu/snapshots/show-address-huge/7.png new file mode 100644 index 00000000..b685970b Binary files /dev/null and b/tests_zemu/snapshots/show-address-huge/7.png differ diff --git a/tests_zemu/snapshots/show-address-huge/8.png b/tests_zemu/snapshots/show-address-huge/8.png new file mode 100644 index 00000000..9794a2c1 Binary files /dev/null and b/tests_zemu/snapshots/show-address-huge/8.png differ diff --git a/tests_zemu/snapshots/show-address-huge/9.png b/tests_zemu/snapshots/show-address-huge/9.png new file mode 100644 index 00000000..1f186b3e Binary files /dev/null and b/tests_zemu/snapshots/show-address-huge/9.png differ diff --git a/tests_zemu/snapshots/show-address/0.png b/tests_zemu/snapshots/show-address/0.png new file mode 100644 index 00000000..c337bb87 Binary files /dev/null and b/tests_zemu/snapshots/show-address/0.png differ diff --git a/tests_zemu/snapshots/show-address/1.png b/tests_zemu/snapshots/show-address/1.png new file mode 100644 index 00000000..d45cb1be Binary files /dev/null and b/tests_zemu/snapshots/show-address/1.png differ diff --git a/tests_zemu/snapshots/show-address/2.png b/tests_zemu/snapshots/show-address/2.png new file mode 100644 index 00000000..22d40b33 Binary files /dev/null and b/tests_zemu/snapshots/show-address/2.png differ diff --git a/tests_zemu/snapshots/show-address/3.png b/tests_zemu/snapshots/show-address/3.png new file mode 100644 index 00000000..9794a2c1 Binary files /dev/null and b/tests_zemu/snapshots/show-address/3.png differ diff --git a/tests_zemu/snapshots/show-address/4.png b/tests_zemu/snapshots/show-address/4.png new file mode 100644 index 00000000..1f186b3e Binary files /dev/null and b/tests_zemu/snapshots/show-address/4.png differ diff --git a/tests_zemu/snapshots/sign-basic-combined/0.png b/tests_zemu/snapshots/sign-basic-combined/0.png new file mode 100644 index 00000000..871ae859 Binary files /dev/null and b/tests_zemu/snapshots/sign-basic-combined/0.png differ diff --git a/tests_zemu/snapshots/sign-basic-combined/1.png b/tests_zemu/snapshots/sign-basic-combined/1.png new file mode 100644 index 00000000..57c55af7 Binary files /dev/null and b/tests_zemu/snapshots/sign-basic-combined/1.png differ diff --git a/tests_zemu/snapshots/sign-basic-combined/2.png b/tests_zemu/snapshots/sign-basic-combined/2.png new file mode 100644 index 00000000..92724317 Binary files /dev/null and b/tests_zemu/snapshots/sign-basic-combined/2.png differ diff --git a/tests_zemu/snapshots/sign-basic-combined/3.png b/tests_zemu/snapshots/sign-basic-combined/3.png new file mode 100644 index 00000000..beeb6f3e Binary files /dev/null and b/tests_zemu/snapshots/sign-basic-combined/3.png differ diff --git a/tests_zemu/snapshots/sign-basic-combined/4.png b/tests_zemu/snapshots/sign-basic-combined/4.png new file mode 100644 index 00000000..07bd9d3a Binary files /dev/null and b/tests_zemu/snapshots/sign-basic-combined/4.png differ diff --git a/tests_zemu/snapshots/sign-basic-combined/5.png b/tests_zemu/snapshots/sign-basic-combined/5.png new file mode 100644 index 00000000..57c55af7 Binary files /dev/null and b/tests_zemu/snapshots/sign-basic-combined/5.png differ diff --git a/tests_zemu/snapshots/sign-basic-combined/6.png b/tests_zemu/snapshots/sign-basic-combined/6.png new file mode 100644 index 00000000..92724317 Binary files /dev/null and b/tests_zemu/snapshots/sign-basic-combined/6.png differ diff --git a/tests_zemu/snapshots/sign-basic-combined/7.png b/tests_zemu/snapshots/sign-basic-combined/7.png new file mode 100644 index 00000000..610edeb1 Binary files /dev/null and b/tests_zemu/snapshots/sign-basic-combined/7.png differ diff --git a/tests_zemu/snapshots/sign-basic-combined/8.png b/tests_zemu/snapshots/sign-basic-combined/8.png new file mode 100644 index 00000000..4aa28b6b Binary files /dev/null and b/tests_zemu/snapshots/sign-basic-combined/8.png differ diff --git a/tests_zemu/snapshots/sign-basic/0.png b/tests_zemu/snapshots/sign-basic/0.png new file mode 100644 index 00000000..871ae859 Binary files /dev/null and b/tests_zemu/snapshots/sign-basic/0.png differ diff --git a/tests_zemu/snapshots/sign-basic/1.png b/tests_zemu/snapshots/sign-basic/1.png new file mode 100644 index 00000000..bcea849f Binary files /dev/null and b/tests_zemu/snapshots/sign-basic/1.png differ diff --git a/tests_zemu/snapshots/sign-basic/2.png b/tests_zemu/snapshots/sign-basic/2.png new file mode 100644 index 00000000..c61097ad Binary files /dev/null and b/tests_zemu/snapshots/sign-basic/2.png differ diff --git a/tests_zemu/snapshots/sign-basic/3.png b/tests_zemu/snapshots/sign-basic/3.png new file mode 100644 index 00000000..d0842b5c Binary files /dev/null and b/tests_zemu/snapshots/sign-basic/3.png differ diff --git a/tests_zemu/snapshots/sign-basic/4.png b/tests_zemu/snapshots/sign-basic/4.png new file mode 100644 index 00000000..ae789b14 Binary files /dev/null and b/tests_zemu/snapshots/sign-basic/4.png differ diff --git a/tests_zemu/snapshots/sign-basic/5.png b/tests_zemu/snapshots/sign-basic/5.png new file mode 100644 index 00000000..610edeb1 Binary files /dev/null and b/tests_zemu/snapshots/sign-basic/5.png differ diff --git a/tests_zemu/snapshots/sign-basic/6.png b/tests_zemu/snapshots/sign-basic/6.png new file mode 100644 index 00000000..4aa28b6b Binary files /dev/null and b/tests_zemu/snapshots/sign-basic/6.png differ diff --git a/tests_zemu/snapshots/sign-basic/7.png b/tests_zemu/snapshots/sign-basic/7.png new file mode 100644 index 00000000..4aa28b6b Binary files /dev/null and b/tests_zemu/snapshots/sign-basic/7.png differ diff --git a/tests_zemu/snapshots/sign-expert/0.png b/tests_zemu/snapshots/sign-expert/0.png new file mode 100644 index 00000000..48d39002 Binary files /dev/null and b/tests_zemu/snapshots/sign-expert/0.png differ diff --git a/tests_zemu/snapshots/sign-expert/1.png b/tests_zemu/snapshots/sign-expert/1.png new file mode 100644 index 00000000..1b843f73 Binary files /dev/null and b/tests_zemu/snapshots/sign-expert/1.png differ diff --git a/tests_zemu/snapshots/sign-expert/10.png b/tests_zemu/snapshots/sign-expert/10.png new file mode 100644 index 00000000..d0842b5c Binary files /dev/null and b/tests_zemu/snapshots/sign-expert/10.png differ diff --git a/tests_zemu/snapshots/sign-expert/11.png b/tests_zemu/snapshots/sign-expert/11.png new file mode 100644 index 00000000..ae789b14 Binary files /dev/null and b/tests_zemu/snapshots/sign-expert/11.png differ diff --git a/tests_zemu/snapshots/sign-expert/12.png b/tests_zemu/snapshots/sign-expert/12.png new file mode 100644 index 00000000..dbd08208 Binary files /dev/null and b/tests_zemu/snapshots/sign-expert/12.png differ diff --git a/tests_zemu/snapshots/sign-expert/13.png b/tests_zemu/snapshots/sign-expert/13.png new file mode 100644 index 00000000..3bfe48e6 Binary files /dev/null and b/tests_zemu/snapshots/sign-expert/13.png differ diff --git a/tests_zemu/snapshots/sign-expert/14.png b/tests_zemu/snapshots/sign-expert/14.png new file mode 100644 index 00000000..4aa28b6b Binary files /dev/null and b/tests_zemu/snapshots/sign-expert/14.png differ diff --git a/tests_zemu/snapshots/sign-expert/2.png b/tests_zemu/snapshots/sign-expert/2.png new file mode 100644 index 00000000..7596e72e Binary files /dev/null and b/tests_zemu/snapshots/sign-expert/2.png differ diff --git a/tests_zemu/snapshots/sign-expert/3.png b/tests_zemu/snapshots/sign-expert/3.png new file mode 100644 index 00000000..5b34a92c Binary files /dev/null and b/tests_zemu/snapshots/sign-expert/3.png differ diff --git a/tests_zemu/snapshots/sign-expert/4.png b/tests_zemu/snapshots/sign-expert/4.png new file mode 100644 index 00000000..cadf7aec Binary files /dev/null and b/tests_zemu/snapshots/sign-expert/4.png differ diff --git a/tests_zemu/snapshots/sign-expert/5.png b/tests_zemu/snapshots/sign-expert/5.png new file mode 100644 index 00000000..ece626fa Binary files /dev/null and b/tests_zemu/snapshots/sign-expert/5.png differ diff --git a/tests_zemu/snapshots/sign-expert/6.png b/tests_zemu/snapshots/sign-expert/6.png new file mode 100644 index 00000000..bcea849f Binary files /dev/null and b/tests_zemu/snapshots/sign-expert/6.png differ diff --git a/tests_zemu/snapshots/sign-expert/7.png b/tests_zemu/snapshots/sign-expert/7.png new file mode 100644 index 00000000..c61097ad Binary files /dev/null and b/tests_zemu/snapshots/sign-expert/7.png differ diff --git a/tests_zemu/snapshots/sign-expert/8.png b/tests_zemu/snapshots/sign-expert/8.png new file mode 100644 index 00000000..cadf7aec Binary files /dev/null and b/tests_zemu/snapshots/sign-expert/8.png differ diff --git a/tests_zemu/snapshots/sign-expert/9.png b/tests_zemu/snapshots/sign-expert/9.png new file mode 100644 index 00000000..ece626fa Binary files /dev/null and b/tests_zemu/snapshots/sign-expert/9.png differ diff --git a/tests_zemu/snapshots/signTransaction.png b/tests_zemu/snapshots/signTransaction.png new file mode 100644 index 00000000..1e430afe Binary files /dev/null and b/tests_zemu/snapshots/signTransaction.png differ diff --git a/tests_zemu/tests/test.js b/tests_zemu/tests/test.js new file mode 100644 index 00000000..147a44bf --- /dev/null +++ b/tests_zemu/tests/test.js @@ -0,0 +1,565 @@ +import {expect, test} from "jest"; +import Zemu from "@zondax/zemu"; +import CosmosApp from "ledger-cosmos-js"; +import secp256k1 from "secp256k1/elliptic"; +import crypto from "crypto"; + +const Resolve = require("path").resolve; +const APP_PATH = Resolve("../app/bin/app.elf"); + +const APP_SEED = "equip will roof matter pink blind book anxiety banner elbow sun young" +const sim_options = { + logging: true, + start_delay: 4000, + custom: `-s "${APP_SEED}"` + , X11: true +}; + +jest.setTimeout(30000) + +const example_tx_str_basic = { + "account_number": "108", + "chain_id": "cosmoshub-3", + "fee": { + "amount": [ + { + "amount": "600", + "denom": "uatom" + } + ], + "gas": "200000" + }, + "memo": "", + "msgs": [ + { + "type": "cosmos-sdk/MsgWithdrawDelegationReward", + "value": { + "delegator_address": "cosmos1w34k53py5v5xyluazqpq65agyajavep2rflq6h", + "validator_address": "cosmosvaloper1kn3wugetjuy4zetlq6wadchfhvu3x740ae6z6x" + } + }, + { + "type": "cosmos-sdk/MsgWithdrawDelegationReward", + "value": { + "delegator_address": "cosmos1w34k53py5v5xyluazqpq65agyajavep2rflq6h", + "validator_address": "cosmosvaloper1sjllsnramtg3ewxqwwrwjxfgc4n4ef9u2lcnj0" + } + } + ], + "sequence": "106" +}; + +const example_tx_str_expert = { + "account_number": "108", + "chain_id": "cosmoshub-2", + "fee": { + "amount": [ + { + "amount": "600", + "denom": "uatom" + } + ], + "gas": "200000" + }, + "memo": "", + "msgs": [ + { + "type": "cosmos-sdk/MsgWithdrawDelegationReward", + "value": { + "delegator_address": "cosmos1kky4yzth6gdrm8ga5zlfwhav33yr7hl87jycah", + "validator_address": "cosmosvaloper1kn3wugetjuy4zetlq6wadchfhvu3x740ae6z6x" + } + }, + { + "type": "cosmos-sdk/MsgWithdrawDelegationReward", + "value": { + "delegator_address": "cosmos1kky4yzth6gdrm8ga5zlfwhav33yr7hl87jycah", + "validator_address": "cosmosvaloper1sjllsnramtg3ewxqwwrwjxfgc4n4ef9u2lcnj0" + } + } + ], + "sequence": "106" +}; + +const example_tx_str_combined = { + "account_number": "108", + "chain_id": "cosmoshub-3", + "fee": { + "amount": [ + { + "amount": "600", + "denom": "uatom" + } + ], + "gas": "200000" + }, + "memo": "", + "msgs": [ + { + "type": "cosmos-sdk/MsgWithdrawDelegationReward", + "value": { + "delegator_address": "cosmos1w34k53py5v5xyluazqpq65agyajavep2rflq6h", + "validator_address": "cosmosvaloper1648ynlpdw7fqa2axt0w2yp3fk542junl7rsvq6" + } + }, + { + "type": "cosmos-sdk/MsgDelegate", + "value": { + "amount": { + "amount": "20139397", + "denom": "uatom" + }, + "delegator_address": "cosmos1w34k53py5v5xyluazqpq65agyajavep2rflq6h", + "validator_address": "cosmosvaloper1648ynlpdw7fqa2axt0w2yp3fk542junl7rsvq6", + } + } + ], + "sequence": "106" +}; + +describe('Basic checks', function () { + it('can start and stop container', async function () { + const sim = new Zemu(APP_PATH); + try { + await sim.start(sim_options); + } finally { + await sim.close(); + } + }); + + it('get app version', async function () { + const sim = new Zemu(APP_PATH); + try { + await sim.start(sim_options); + const app = new CosmosApp(sim.getTransport()); + const resp = await app.getVersion(); + + console.log(resp); + + expect(resp.return_code).toEqual(0x9000); + expect(resp.error_message).toEqual("No errors"); + expect(resp).toHaveProperty("test_mode"); + expect(resp).toHaveProperty("major"); + expect(resp).toHaveProperty("minor"); + expect(resp).toHaveProperty("patch"); + } finally { + await sim.close(); + } + }); + + it('get app info', async function () { + const sim = new Zemu(APP_PATH); + try { + await sim.start(sim_options); + const app = new CosmosApp(sim.getTransport()); + const info = await app.appInfo(); + + console.log(info) + } finally { + await sim.close(); + } + }); + + // NOTE: Temporarily Disabled due to Ledger's Request + // it('get device info', async function () { + // const sim = new Zemu(APP_PATH); + // try { + // await sim.start(sim_options); + // const app = new CosmosApp(sim.getTransport()); + // const resp = await app.deviceInfo(); + // + // console.log(resp); + // + // expect(resp.return_code).toEqual(0x9000); + // expect(resp.error_message).toEqual("No errors"); + // + // expect(resp).toHaveProperty("targetId"); + // expect(resp).toHaveProperty("seVersion"); + // expect(resp).toHaveProperty("flag"); + // expect(resp).toHaveProperty("mcuVersion"); + // } finally { + // await sim.close(); + // } + // }); + + it('get address', async function () { + const sim = new Zemu(APP_PATH); + try { + await sim.start(sim_options); + const app = new CosmosApp(sim.getTransport()); + // Derivation path. First 3 items are automatically hardened! + const path = [44, 118, 5, 0, 3]; + const resp = await app.getAddressAndPubKey(path, "cosmos"); + + console.log(resp); + + expect(resp.return_code).toEqual(0x9000); + expect(resp.error_message).toEqual("No errors"); + + expect(resp).toHaveProperty("bech32_address"); + expect(resp).toHaveProperty("compressed_pk"); + + expect(resp.bech32_address).toEqual("cosmos1wkd9tfm5pqvhhaxq77wv9tvjcsazuaykwsld65"); + expect(resp.compressed_pk.length).toEqual(33); + } finally { + await sim.close(); + } + }); + + function compareSnapshots(snapshotPrefixTmp, snapshotPrefixGolden, snapshotCount) { + for (let i = 0; i < snapshotCount; i++) { + const img1 = Zemu.LoadPng2RGB(`${snapshotPrefixTmp}${i}.png`); + const img2 = Zemu.LoadPng2RGB(`${snapshotPrefixGolden}${i}.png`); + expect(img1).toEqual(img2); + } + } + + it('show address', async function () { + const snapshotPrefixGolden = "snapshots/show-address/"; + const snapshotPrefixTmp = "snapshots-tmp/show-address/"; + let snapshotCount = 0; + + const sim = new Zemu(APP_PATH); + try { + await sim.start(sim_options); + const app = new CosmosApp(sim.getTransport()); + + // Derivation path. First 3 items are automatically hardened! + const path = [44, 118, 5, 0, 3]; + const respRequest = app.showAddressAndPubKey(path, "cosmos"); + + // We need to wait until the app responds to the APDU + await Zemu.sleep(2000); + + // Now navigate the address / path + await sim.snapshot(`${snapshotPrefixTmp}${snapshotCount++}.png`); + await sim.clickRight(`${snapshotPrefixTmp}${snapshotCount++}.png`); + await sim.clickRight(`${snapshotPrefixTmp}${snapshotCount++}.png`); + await sim.clickRight(`${snapshotPrefixTmp}${snapshotCount++}.png`); + await sim.clickBoth(`${snapshotPrefixTmp}${snapshotCount++}.png`); + + const resp = await respRequest; + console.log(resp); + + compareSnapshots(snapshotPrefixTmp, snapshotPrefixGolden, snapshotCount); + + expect(resp.return_code).toEqual(0x9000); + expect(resp.error_message).toEqual("No errors"); + + expect(resp).toHaveProperty("bech32_address"); + expect(resp).toHaveProperty("compressed_pk"); + + expect(resp.bech32_address).toEqual("cosmos1wkd9tfm5pqvhhaxq77wv9tvjcsazuaykwsld65"); + expect(resp.compressed_pk.length).toEqual(33); + } finally { + await sim.close(); + } + }); + + it('show address - HUGE', async function () { + const sim = new Zemu(APP_PATH); + try { + await sim.start(sim_options); + const app = new CosmosApp(sim.getTransport()); + + // Derivation path. First 3 items are automatically hardened! + const path = [44, 118, 2147483647, 0, 4294967295]; + const resp = await app.showAddressAndPubKey(path, "cosmos"); + console.log(resp); + + expect(resp.return_code).toEqual(0x6985); + expect(resp.error_message).toEqual("Conditions not satisfied"); + } finally { + await sim.close(); + } + }); + + it('show address - HUGE - expert', async function () { + const snapshotPrefixGolden = "snapshots/show-address-huge/"; + const snapshotPrefixTmp = "snapshots-tmp/show-address-huge/"; + let snapshotCount = 0; + + const sim = new Zemu(APP_PATH); + try { + await sim.start(sim_options); + const app = new CosmosApp(sim.getTransport()); + + // Activate expert mode + await sim.clickRight(`${snapshotPrefixTmp}${snapshotCount++}.png`); + await sim.clickBoth(`${snapshotPrefixTmp}${snapshotCount++}.png`); + + // Derivation path. First 3 items are automatically hardened! + const path = [44, 118, 2147483647, 0, 4294967295]; + const respRequest = app.showAddressAndPubKey(path, "cosmos"); + + // We need to wait until the app responds to the APDU + await Zemu.sleep(2000); + + // Now navigate the address / path + await sim.snapshot(`${snapshotPrefixTmp}${snapshotCount++}.png`); + await sim.clickRight(`${snapshotPrefixTmp}${snapshotCount++}.png`); + await sim.clickRight(`${snapshotPrefixTmp}${snapshotCount++}.png`); + await sim.clickRight(`${snapshotPrefixTmp}${snapshotCount++}.png`); + await sim.clickRight(`${snapshotPrefixTmp}${snapshotCount++}.png`); + await sim.clickRight(`${snapshotPrefixTmp}${snapshotCount++}.png`); + await sim.clickRight(`${snapshotPrefixTmp}${snapshotCount++}.png`); + await sim.clickBoth(`${snapshotPrefixTmp}${snapshotCount++}.png`); + + const resp = await respRequest; + console.log(resp); + + compareSnapshots(snapshotPrefixTmp, snapshotPrefixGolden, snapshotCount); + + expect(resp.return_code).toEqual(0x9000); + expect(resp.error_message).toEqual("No errors"); + + expect(resp).toHaveProperty("bech32_address"); + expect(resp).toHaveProperty("compressed_pk"); + + expect(resp.bech32_address).toEqual("cosmos1ex7gkwwmq4vcgdwcalaq3t20pgwr37u6ntkqzh"); + expect(resp.compressed_pk.length).toEqual(33); + } finally { + await sim.close(); + } + }); + + it('sign basic', async function () { + const snapshotPrefixGolden = "snapshots/sign-basic/"; + const snapshotPrefixTmp = "snapshots-tmp/sign-basic/"; + let snapshotCount = 0; + + const sim = new Zemu(APP_PATH); + try { + await sim.start(sim_options); + const app = new CosmosApp(sim.getTransport()); + + const path = [44, 118, 0, 0, 0]; + let tx = JSON.stringify(example_tx_str_basic); + + // get address / publickey + const respPk = await app.getAddressAndPubKey(path, "cosmos"); + expect(respPk.return_code).toEqual(0x9000); + expect(respPk.error_message).toEqual("No errors"); + console.log(respPk) + + // do not wait here.. + const signatureRequest = app.sign(path, tx); + + await Zemu.sleep(2000); + + // Reference window + await sim.snapshot(`${snapshotPrefixTmp}${snapshotCount++}.png`); + for (let i = 0; i < 6; i++) { + await sim.clickRight(Resolve(`${snapshotPrefixTmp}${snapshotCount++}.png`)); + } + await sim.clickBoth(); + + let resp = await signatureRequest; + console.log(resp); + + compareSnapshots(snapshotPrefixTmp, snapshotPrefixGolden, snapshotCount); + + expect(resp.return_code).toEqual(0x9000); + expect(resp.error_message).toEqual("No errors"); + + // Now verify the signature + const hash = crypto.createHash("sha256"); + const msgHash = Uint8Array.from(hash.update(tx).digest()); + + const signatureDER = resp.signature; + const signature = secp256k1.signatureImport(Uint8Array.from(signatureDER)); + + const pk = Uint8Array.from(respPk.compressed_pk) + + const signatureOk = secp256k1.ecdsaVerify(signature, msgHash, pk); + expect(signatureOk).toEqual(true); + + } finally { + await sim.close(); + } + }); + + it('sign basic - combined tx', async function () { + const snapshotPrefixGolden = "snapshots/sign-basic-combined/"; + const snapshotPrefixTmp = "snapshots-tmp/sign-basic-combined/"; + let snapshotCount = 0; + + const sim = new Zemu(APP_PATH); + try { + await sim.start(sim_options); + const app = new CosmosApp(sim.getTransport()); + + const path = [44, 118, 0, 0, 0]; + let tx = JSON.stringify(example_tx_str_combined); + + // get address / publickey + const respPk = await app.getAddressAndPubKey(path, "cosmos"); + expect(respPk.return_code).toEqual(0x9000); + expect(respPk.error_message).toEqual("No errors"); + console.log(respPk) + + // do not wait here.. + const signatureRequest = app.sign(path, tx); + + await Zemu.sleep(3000); + + // Reference window + await sim.snapshot(`${snapshotPrefixTmp}${snapshotCount++}.png`); + for (let i = 0; i < 8; i++) { + await sim.clickRight(Resolve(`${snapshotPrefixTmp}${snapshotCount++}.png`)); + } + await sim.clickBoth(); + + let resp = await signatureRequest; + console.log(resp); + + compareSnapshots(snapshotPrefixTmp, snapshotPrefixGolden, snapshotCount); + + expect(resp.return_code).toEqual(0x9000); + expect(resp.error_message).toEqual("No errors"); + + // Now verify the signature + const hash = crypto.createHash("sha256"); + const msgHash = Uint8Array.from(hash.update(tx).digest()); + + const signatureDER = resp.signature; + const signature = secp256k1.signatureImport(Uint8Array.from(signatureDER)); + + const pk = Uint8Array.from(respPk.compressed_pk) + + const signatureOk = secp256k1.ecdsaVerify(signature, msgHash, pk); + expect(signatureOk).toEqual(true); + + } finally { + await sim.close(); + } + }); + + it('show address and sign basic', async function () { + const snapshotPrefixGolden = "snapshots/show-address-and-sign-basic/"; + const snapshotPrefixTmp = "snapshots-tmp/show-address-and-sign-basic/"; + let snapshotCount = 0; + + const sim = new Zemu(APP_PATH); + try { + await sim.start(sim_options); + const app = new CosmosApp(sim.getTransport()); + + const path = [44, 118, 0, 0, 0]; + let tx = JSON.stringify(example_tx_str_basic); + + // get address / publickey + const respRequest = app.showAddressAndPubKey(path, "cosmos"); + + // We need to wait until the app responds to the APDU + await Zemu.sleep(3000); + + // Now navigate the address / path + await sim.snapshot(`${snapshotPrefixTmp}${snapshotCount++}.png`); + await sim.clickRight(`${snapshotPrefixTmp}${snapshotCount++}.png`); + await sim.clickRight(`${snapshotPrefixTmp}${snapshotCount++}.png`); + await sim.clickRight(`${snapshotPrefixTmp}${snapshotCount++}.png`); + await sim.clickBoth(`${snapshotPrefixTmp}${snapshotCount++}.png`); + + const respPk = await respRequest; + console.log(respPk); + + expect(respPk.return_code).toEqual(0x9000); + expect(respPk.error_message).toEqual("No errors"); + console.log(respPk) + + // do not wait here.. + const signatureRequest = app.sign(path, tx); + + await Zemu.sleep(3000); + + // Reference window + await sim.snapshot(`${snapshotPrefixTmp}${snapshotCount++}.png`); + for (let i = 0; i < 6; i++) { + await sim.clickRight(Resolve(`${snapshotPrefixTmp}${snapshotCount++}.png`)); + } + await sim.clickBoth(); + + let resp = await signatureRequest; + console.log(resp); + + compareSnapshots(snapshotPrefixTmp, snapshotPrefixGolden, snapshotCount); + + expect(resp.return_code).toEqual(0x9000); + expect(resp.error_message).toEqual("No errors"); + + // Now verify the signature + const hash = crypto.createHash("sha256"); + const msgHash = Uint8Array.from(hash.update(tx).digest()); + + const signatureDER = resp.signature; + const signature = secp256k1.signatureImport(Uint8Array.from(signatureDER)); + + const pk = Uint8Array.from(respPk.compressed_pk) + + const signatureOk = secp256k1.ecdsaVerify(signature, msgHash, pk); + expect(signatureOk).toEqual(true); + + } finally { + await sim.close(); + } + }); + + it('sign expert', async function () { + const snapshotPrefixGolden = "snapshots/sign-expert/"; + const snapshotPrefixTmp = "snapshots-tmp/sign-expert/"; + let snapshotCount = 0; + + const sim = new Zemu(APP_PATH); + try { + await sim.start(sim_options); + const app = new CosmosApp(sim.getTransport()); + + const path = [44, 118, 0, 0, 0]; + let tx = JSON.stringify(example_tx_str_expert); + + // get address / publickey + const respPk = await app.getAddressAndPubKey(path, "cosmos"); + expect(respPk.return_code).toEqual(0x9000); + expect(respPk.error_message).toEqual("No errors"); + console.log(respPk) + + // do not wait here.. + const signatureRequest = app.sign(path, tx); + + await Zemu.sleep(3000); + + // Reference window + await sim.snapshot(`${snapshotPrefixTmp}${snapshotCount++}.png`); + for (let i = 0; i < 14; i++) { + await sim.clickRight(Resolve(`${snapshotPrefixTmp}${snapshotCount++}.png`)); + } + await sim.clickBoth(); + await sim.clickBoth(); + await sim.clickBoth(); + + let resp = await signatureRequest; + console.log(resp); + + compareSnapshots(snapshotPrefixTmp, snapshotPrefixGolden, snapshotCount); + + expect(resp.return_code).toEqual(0x9000); + expect(resp.error_message).toEqual("No errors"); + + // Now verify the signature + const hash = crypto.createHash("sha256"); + const msgHash = Uint8Array.from(hash.update(tx).digest()); + + const signatureDER = resp.signature; + const signature = secp256k1.signatureImport(Uint8Array.from(signatureDER)); + + const pk = Uint8Array.from(respPk.compressed_pk) + + const signatureOk = secp256k1.ecdsaVerify(signature, msgHash, pk); + expect(signatureOk).toEqual(true); + + } finally { + await sim.close(); + } + }); +}); diff --git a/tests_zemu/tools/debug.mjs b/tests_zemu/tools/debug.mjs new file mode 100644 index 00000000..61b53c7f --- /dev/null +++ b/tests_zemu/tools/debug.mjs @@ -0,0 +1,121 @@ +import Zemu from "@zondax/zemu"; +import CosmosApp from "ledger-cosmos-js"; +import path from "path"; + +const APP_PATH = path.resolve(`./../../app/bin/app.elf`); + +const seed = "equip will roof matter pink blind book anxiety banner elbow sun young" +const SIM_OPTIONS = { + logging: true, + start_delay: 4000, + X11: true, + custom: `-s "${seed}" --color LAGOON_BLUE` +}; + +const example_tx_str = { + "account_number": "108", + "chain_id": "cosmoshub-3", + "fee": { + "amount": [ + { + "amount": "600", + "denom": "uatom" + } + ], + "gas": "200000" + }, + "memo": "", + "msgs": [ + { + "type": "cosmos-sdk/MsgWithdrawDelegationReward", + "value": { + "delegator_address": "cosmos19umvgcvk8cxsvzemy239nj9ngc2ltukantgyp3", + "validator_address": "cosmosvaloper1648ynlpdw7fqa2axt0w2yp3fk542junl7rsvq6" + } + }, + { + "type": "cosmos-sdk/MsgDelegate", + "value": { + "amount": { + "amount": "20139397", + "denom": "uatom" + }, + "delegator_address": "cosmos19umvgcvk8cxsvzemy239nj9ngc2ltukantgyp3", + "validator_address": "cosmosvaloper1648ynlpdw7fqa2axt0w2yp3fk542junl7rsvq6", + } + } + ], + "sequence": "106" +}; + +async function beforeStart() { + process.on("SIGINT", () => { + Zemu.default.stopAllEmuContainers(function () { + process.exit(); + }); + }); + await Zemu.default.checkAndPullImage(); +} + +async function beforeEnd() { + await Zemu.default.stopAllEmuContainers(); +} + +async function debugScenario(sim, app) { + const path = [44, 118, 0, 0, 0]; + let tx = JSON.stringify(example_tx_str); + +// await Zemu.default.sleep(120000); + + const addr = await app.getAddressAndPubKey(path, "cosmos"); + console.log(addr) + + console.log(tx); + + // do not wait here.. + // const signatureRequest = app.sign(path, tx); + await Zemu.default.sleep(100000); + + // await sim.clickRight(); + // await sim.clickRight(); + // await sim.clickRight(); + // await sim.clickRight(); + // await sim.clickRight(); + // await sim.clickRight(); + // await sim.clickRight(); + // await sim.clickRight(); + // await sim.clickBoth(); + + let resp = await signatureRequest; + console.log(resp); +} + +async function main() { + await beforeStart(); + + if (process.argv.length > 2 && process.argv[2] === "debug") { + SIM_OPTIONS["custom"] = SIM_OPTIONS["custom"] + " --debug"; + } + + const sim = new Zemu.default(APP_PATH); + + try { + await sim.start(SIM_OPTIONS); + const app = new CosmosApp.default(sim.getTransport()); + + //////////// + /// TIP you can use zemu commands here to take the app to the point where you trigger a breakpoint + + await debugScenario(sim, app); + + /// TIP + + } finally { + await sim.close(); + await beforeEnd(); + } +} + +(async () => { + await main(); +})(); diff --git a/tests_zemu/yarn.lock b/tests_zemu/yarn.lock new file mode 100644 index 00000000..f4d055af --- /dev/null +++ b/tests_zemu/yarn.lock @@ -0,0 +1,5364 @@ +# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. +# yarn lockfile v1 + + +"@babel/cli@^7.8.4": + version "7.8.4" + resolved "https://registry.yarnpkg.com/@babel/cli/-/cli-7.8.4.tgz#505fb053721a98777b2b175323ea4f090b7d3c1c" + integrity sha512-XXLgAm6LBbaNxaGhMAznXXaxtCWfuv6PIDJ9Alsy9JYTOh+j2jJz+L/162kkfU1j/pTSxK1xGmlwI4pdIMkoag== + dependencies: + commander "^4.0.1" + convert-source-map "^1.1.0" + fs-readdir-recursive "^1.1.0" + glob "^7.0.0" + lodash "^4.17.13" + make-dir "^2.1.0" + slash "^2.0.0" + source-map "^0.5.0" + optionalDependencies: + chokidar "^2.1.8" + +"@babel/code-frame@^7.0.0", "@babel/code-frame@^7.8.3": + version "7.8.3" + resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.8.3.tgz#33e25903d7481181534e12ec0a25f16b6fcf419e" + integrity sha512-a9gxpmdXtZEInkCSHUJDLHZVBgb1QS0jhss4cPP93EW7s+uC5bikET2twEF3KV+7rDblJcmNvTR7VJejqd2C2g== + dependencies: + "@babel/highlight" "^7.8.3" + +"@babel/compat-data@^7.9.6": + version "7.9.6" + resolved "https://registry.yarnpkg.com/@babel/compat-data/-/compat-data-7.9.6.tgz#3f604c40e420131affe6f2c8052e9a275ae2049b" + integrity sha512-5QPTrNen2bm7RBc7dsOmcA5hbrS4O2Vhmk5XOL4zWW/zD/hV0iinpefDlkm+tBBy8kDtFaaeEvmAqt+nURAV2g== + dependencies: + browserslist "^4.11.1" + invariant "^2.2.4" + semver "^5.5.0" + +"@babel/core@^7.1.0", "@babel/core@^7.9.0": + version "7.9.6" + resolved "https://registry.yarnpkg.com/@babel/core/-/core-7.9.6.tgz#d9aa1f580abf3b2286ef40b6904d390904c63376" + integrity sha512-nD3deLvbsApbHAHttzIssYqgb883yU/d9roe4RZymBCDaZryMJDbptVpEpeQuRh4BJ+SYI8le9YGxKvFEvl1Wg== + dependencies: + "@babel/code-frame" "^7.8.3" + "@babel/generator" "^7.9.6" + "@babel/helper-module-transforms" "^7.9.0" + "@babel/helpers" "^7.9.6" + "@babel/parser" "^7.9.6" + "@babel/template" "^7.8.6" + "@babel/traverse" "^7.9.6" + "@babel/types" "^7.9.6" + convert-source-map "^1.7.0" + debug "^4.1.0" + gensync "^1.0.0-beta.1" + json5 "^2.1.2" + lodash "^4.17.13" + resolve "^1.3.2" + semver "^5.4.1" + source-map "^0.5.0" + +"@babel/generator@^7.4.0", "@babel/generator@^7.9.6": + version "7.9.6" + resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.9.6.tgz#5408c82ac5de98cda0d77d8124e99fa1f2170a43" + integrity sha512-+htwWKJbH2bL72HRluF8zumBxzuX0ZZUFl3JLNyoUjM/Ho8wnVpPXM6aUz8cfKDqQ/h7zHqKt4xzJteUosckqQ== + dependencies: + "@babel/types" "^7.9.6" + jsesc "^2.5.1" + lodash "^4.17.13" + source-map "^0.5.0" + +"@babel/helper-annotate-as-pure@^7.8.3": + version "7.8.3" + resolved "https://registry.yarnpkg.com/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.8.3.tgz#60bc0bc657f63a0924ff9a4b4a0b24a13cf4deee" + integrity sha512-6o+mJrZBxOoEX77Ezv9zwW7WV8DdluouRKNY/IR5u/YTMuKHgugHOzYWlYvYLpLA9nPsQCAAASpCIbjI9Mv+Uw== + dependencies: + "@babel/types" "^7.8.3" + +"@babel/helper-builder-binary-assignment-operator-visitor@^7.8.3": + version "7.8.3" + resolved "https://registry.yarnpkg.com/@babel/helper-builder-binary-assignment-operator-visitor/-/helper-builder-binary-assignment-operator-visitor-7.8.3.tgz#c84097a427a061ac56a1c30ebf54b7b22d241503" + integrity sha512-5eFOm2SyFPK4Rh3XMMRDjN7lBH0orh3ss0g3rTYZnBQ+r6YPj7lgDyCvPphynHvUrobJmeMignBr6Acw9mAPlw== + dependencies: + "@babel/helper-explode-assignable-expression" "^7.8.3" + "@babel/types" "^7.8.3" + +"@babel/helper-compilation-targets@^7.9.6": + version "7.9.6" + resolved "https://registry.yarnpkg.com/@babel/helper-compilation-targets/-/helper-compilation-targets-7.9.6.tgz#1e05b7ccc9d38d2f8b40b458b380a04dcfadd38a" + integrity sha512-x2Nvu0igO0ejXzx09B/1fGBxY9NXQlBW2kZsSxCJft+KHN8t9XWzIvFxtPHnBOAXpVsdxZKZFbRUC8TsNKajMw== + dependencies: + "@babel/compat-data" "^7.9.6" + browserslist "^4.11.1" + invariant "^2.2.4" + levenary "^1.1.1" + semver "^5.5.0" + +"@babel/helper-create-regexp-features-plugin@^7.8.3", "@babel/helper-create-regexp-features-plugin@^7.8.8": + version "7.8.8" + resolved "https://registry.yarnpkg.com/@babel/helper-create-regexp-features-plugin/-/helper-create-regexp-features-plugin-7.8.8.tgz#5d84180b588f560b7864efaeea89243e58312087" + integrity sha512-LYVPdwkrQEiX9+1R29Ld/wTrmQu1SSKYnuOk3g0CkcZMA1p0gsNxJFj/3gBdaJ7Cg0Fnek5z0DsMULePP7Lrqg== + dependencies: + "@babel/helper-annotate-as-pure" "^7.8.3" + "@babel/helper-regex" "^7.8.3" + regexpu-core "^4.7.0" + +"@babel/helper-define-map@^7.8.3": + version "7.8.3" + resolved "https://registry.yarnpkg.com/@babel/helper-define-map/-/helper-define-map-7.8.3.tgz#a0655cad5451c3760b726eba875f1cd8faa02c15" + integrity sha512-PoeBYtxoZGtct3md6xZOCWPcKuMuk3IHhgxsRRNtnNShebf4C8YonTSblsK4tvDbm+eJAw2HAPOfCr+Q/YRG/g== + dependencies: + "@babel/helper-function-name" "^7.8.3" + "@babel/types" "^7.8.3" + lodash "^4.17.13" + +"@babel/helper-explode-assignable-expression@^7.8.3": + version "7.8.3" + resolved "https://registry.yarnpkg.com/@babel/helper-explode-assignable-expression/-/helper-explode-assignable-expression-7.8.3.tgz#a728dc5b4e89e30fc2dfc7d04fa28a930653f982" + integrity sha512-N+8eW86/Kj147bO9G2uclsg5pwfs/fqqY5rwgIL7eTBklgXjcOJ3btzS5iM6AitJcftnY7pm2lGsrJVYLGjzIw== + dependencies: + "@babel/traverse" "^7.8.3" + "@babel/types" "^7.8.3" + +"@babel/helper-function-name@^7.8.3", "@babel/helper-function-name@^7.9.5": + version "7.9.5" + resolved "https://registry.yarnpkg.com/@babel/helper-function-name/-/helper-function-name-7.9.5.tgz#2b53820d35275120e1874a82e5aabe1376920a5c" + integrity sha512-JVcQZeXM59Cd1qanDUxv9fgJpt3NeKUaqBqUEvfmQ+BCOKq2xUgaWZW2hr0dkbyJgezYuplEoh5knmrnS68efw== + dependencies: + "@babel/helper-get-function-arity" "^7.8.3" + "@babel/template" "^7.8.3" + "@babel/types" "^7.9.5" + +"@babel/helper-get-function-arity@^7.8.3": + version "7.8.3" + resolved "https://registry.yarnpkg.com/@babel/helper-get-function-arity/-/helper-get-function-arity-7.8.3.tgz#b894b947bd004381ce63ea1db9f08547e920abd5" + integrity sha512-FVDR+Gd9iLjUMY1fzE2SR0IuaJToR4RkCDARVfsBBPSP53GEqSFjD8gNyxg246VUyc/ALRxFaAK8rVG7UT7xRA== + dependencies: + "@babel/types" "^7.8.3" + +"@babel/helper-hoist-variables@^7.8.3": + version "7.8.3" + resolved "https://registry.yarnpkg.com/@babel/helper-hoist-variables/-/helper-hoist-variables-7.8.3.tgz#1dbe9b6b55d78c9b4183fc8cdc6e30ceb83b7134" + integrity sha512-ky1JLOjcDUtSc+xkt0xhYff7Z6ILTAHKmZLHPxAhOP0Nd77O+3nCsd6uSVYur6nJnCI029CrNbYlc0LoPfAPQg== + dependencies: + "@babel/types" "^7.8.3" + +"@babel/helper-member-expression-to-functions@^7.8.3": + version "7.8.3" + resolved "https://registry.yarnpkg.com/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.8.3.tgz#659b710498ea6c1d9907e0c73f206eee7dadc24c" + integrity sha512-fO4Egq88utkQFjbPrSHGmGLFqmrshs11d46WI+WZDESt7Wu7wN2G2Iu+NMMZJFDOVRHAMIkB5SNh30NtwCA7RA== + dependencies: + "@babel/types" "^7.8.3" + +"@babel/helper-module-imports@^7.8.3": + version "7.8.3" + resolved "https://registry.yarnpkg.com/@babel/helper-module-imports/-/helper-module-imports-7.8.3.tgz#7fe39589b39c016331b6b8c3f441e8f0b1419498" + integrity sha512-R0Bx3jippsbAEtzkpZ/6FIiuzOURPcMjHp+Z6xPe6DtApDJx+w7UYyOLanZqO8+wKR9G10s/FmHXvxaMd9s6Kg== + dependencies: + "@babel/types" "^7.8.3" + +"@babel/helper-module-transforms@^7.9.0": + version "7.9.0" + resolved "https://registry.yarnpkg.com/@babel/helper-module-transforms/-/helper-module-transforms-7.9.0.tgz#43b34dfe15961918707d247327431388e9fe96e5" + integrity sha512-0FvKyu0gpPfIQ8EkxlrAydOWROdHpBmiCiRwLkUiBGhCUPRRbVD2/tm3sFr/c/GWFrQ/ffutGUAnx7V0FzT2wA== + dependencies: + "@babel/helper-module-imports" "^7.8.3" + "@babel/helper-replace-supers" "^7.8.6" + "@babel/helper-simple-access" "^7.8.3" + "@babel/helper-split-export-declaration" "^7.8.3" + "@babel/template" "^7.8.6" + "@babel/types" "^7.9.0" + lodash "^4.17.13" + +"@babel/helper-optimise-call-expression@^7.8.3": + version "7.8.3" + resolved "https://registry.yarnpkg.com/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.8.3.tgz#7ed071813d09c75298ef4f208956006b6111ecb9" + integrity sha512-Kag20n86cbO2AvHca6EJsvqAd82gc6VMGule4HwebwMlwkpXuVqrNRj6CkCV2sKxgi9MyAUnZVnZ6lJ1/vKhHQ== + dependencies: + "@babel/types" "^7.8.3" + +"@babel/helper-plugin-utils@^7.0.0", "@babel/helper-plugin-utils@^7.8.0", "@babel/helper-plugin-utils@^7.8.3": + version "7.8.3" + resolved "https://registry.yarnpkg.com/@babel/helper-plugin-utils/-/helper-plugin-utils-7.8.3.tgz#9ea293be19babc0f52ff8ca88b34c3611b208670" + integrity sha512-j+fq49Xds2smCUNYmEHF9kGNkhbet6yVIBp4e6oeQpH1RUs/Ir06xUKzDjDkGcaaokPiTNs2JBWHjaE4csUkZQ== + +"@babel/helper-regex@^7.8.3": + version "7.8.3" + resolved "https://registry.yarnpkg.com/@babel/helper-regex/-/helper-regex-7.8.3.tgz#139772607d51b93f23effe72105b319d2a4c6965" + integrity sha512-BWt0QtYv/cg/NecOAZMdcn/waj/5P26DR4mVLXfFtDokSR6fyuG0Pj+e2FqtSME+MqED1khnSMulkmGl8qWiUQ== + dependencies: + lodash "^4.17.13" + +"@babel/helper-remap-async-to-generator@^7.8.3": + version "7.8.3" + resolved "https://registry.yarnpkg.com/@babel/helper-remap-async-to-generator/-/helper-remap-async-to-generator-7.8.3.tgz#273c600d8b9bf5006142c1e35887d555c12edd86" + integrity sha512-kgwDmw4fCg7AVgS4DukQR/roGp+jP+XluJE5hsRZwxCYGg+Rv9wSGErDWhlI90FODdYfd4xG4AQRiMDjjN0GzA== + dependencies: + "@babel/helper-annotate-as-pure" "^7.8.3" + "@babel/helper-wrap-function" "^7.8.3" + "@babel/template" "^7.8.3" + "@babel/traverse" "^7.8.3" + "@babel/types" "^7.8.3" + +"@babel/helper-replace-supers@^7.8.3", "@babel/helper-replace-supers@^7.8.6": + version "7.9.6" + resolved "https://registry.yarnpkg.com/@babel/helper-replace-supers/-/helper-replace-supers-7.9.6.tgz#03149d7e6a5586ab6764996cd31d6981a17e1444" + integrity sha512-qX+chbxkbArLyCImk3bWV+jB5gTNU/rsze+JlcF6Nf8tVTigPJSI1o1oBow/9Resa1yehUO9lIipsmu9oG4RzA== + dependencies: + "@babel/helper-member-expression-to-functions" "^7.8.3" + "@babel/helper-optimise-call-expression" "^7.8.3" + "@babel/traverse" "^7.9.6" + "@babel/types" "^7.9.6" + +"@babel/helper-simple-access@^7.8.3": + version "7.8.3" + resolved "https://registry.yarnpkg.com/@babel/helper-simple-access/-/helper-simple-access-7.8.3.tgz#7f8109928b4dab4654076986af575231deb639ae" + integrity sha512-VNGUDjx5cCWg4vvCTR8qQ7YJYZ+HBjxOgXEl7ounz+4Sn7+LMD3CFrCTEU6/qXKbA2nKg21CwhhBzO0RpRbdCw== + dependencies: + "@babel/template" "^7.8.3" + "@babel/types" "^7.8.3" + +"@babel/helper-split-export-declaration@^7.8.3": + version "7.8.3" + resolved "https://registry.yarnpkg.com/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.8.3.tgz#31a9f30070f91368a7182cf05f831781065fc7a9" + integrity sha512-3x3yOeyBhW851hroze7ElzdkeRXQYQbFIb7gLK1WQYsw2GWDay5gAJNw1sWJ0VFP6z5J1whqeXH/WCdCjZv6dA== + dependencies: + "@babel/types" "^7.8.3" + +"@babel/helper-validator-identifier@^7.9.0", "@babel/helper-validator-identifier@^7.9.5": + version "7.9.5" + resolved "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.9.5.tgz#90977a8e6fbf6b431a7dc31752eee233bf052d80" + integrity sha512-/8arLKUFq882w4tWGj9JYzRpAlZgiWUJ+dtteNTDqrRBz9Iguck9Rn3ykuBDoUwh2TO4tSAJlrxDUOXWklJe4g== + +"@babel/helper-wrap-function@^7.8.3": + version "7.8.3" + resolved "https://registry.yarnpkg.com/@babel/helper-wrap-function/-/helper-wrap-function-7.8.3.tgz#9dbdb2bb55ef14aaa01fe8c99b629bd5352d8610" + integrity sha512-LACJrbUET9cQDzb6kG7EeD7+7doC3JNvUgTEQOx2qaO1fKlzE/Bf05qs9w1oXQMmXlPO65lC3Tq9S6gZpTErEQ== + dependencies: + "@babel/helper-function-name" "^7.8.3" + "@babel/template" "^7.8.3" + "@babel/traverse" "^7.8.3" + "@babel/types" "^7.8.3" + +"@babel/helpers@^7.9.6": + version "7.9.6" + resolved "https://registry.yarnpkg.com/@babel/helpers/-/helpers-7.9.6.tgz#092c774743471d0bb6c7de3ad465ab3d3486d580" + integrity sha512-tI4bUbldloLcHWoRUMAj4g1bF313M/o6fBKhIsb3QnGVPwRm9JsNf/gqMkQ7zjqReABiffPV6RWj7hEglID5Iw== + dependencies: + "@babel/template" "^7.8.3" + "@babel/traverse" "^7.9.6" + "@babel/types" "^7.9.6" + +"@babel/highlight@^7.8.3": + version "7.9.0" + resolved "https://registry.yarnpkg.com/@babel/highlight/-/highlight-7.9.0.tgz#4e9b45ccb82b79607271b2979ad82c7b68163079" + integrity sha512-lJZPilxX7Op3Nv/2cvFdnlepPXDxi29wxteT57Q965oc5R9v86ztx0jfxVrTcBk8C2kcPkkDa2Z4T3ZsPPVWsQ== + dependencies: + "@babel/helper-validator-identifier" "^7.9.0" + chalk "^2.0.0" + js-tokens "^4.0.0" + +"@babel/node@^7.8.7": + version "7.8.7" + resolved "https://registry.yarnpkg.com/@babel/node/-/node-7.8.7.tgz#4213ea99f0c86cc1cf460e61131e7acbb723e13a" + integrity sha512-o8cBT3cfRPLwoPh7VBYonSeZypIawGUeVfOIt1xSDgcDdirRGDPZ7/x+FLhhgQmKp3PKbz5Juh9/BNP4Jzrr9Q== + dependencies: + "@babel/register" "^7.8.3" + commander "^4.0.1" + core-js "^3.2.1" + lodash "^4.17.13" + node-environment-flags "^1.0.5" + regenerator-runtime "^0.13.4" + resolve "^1.13.1" + v8flags "^3.1.1" + +"@babel/parser@^7.1.0", "@babel/parser@^7.4.3", "@babel/parser@^7.7.0", "@babel/parser@^7.8.6", "@babel/parser@^7.9.6": + version "7.9.6" + resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.9.6.tgz#3b1bbb30dabe600cd72db58720998376ff653bc7" + integrity sha512-AoeIEJn8vt+d/6+PXDRPaksYhnlbMIiejioBZvvMQsOjW/JYK6k/0dKnvvP3EhK5GfMBWDPtrxRtegWdAcdq9Q== + +"@babel/plugin-proposal-async-generator-functions@^7.8.3": + version "7.8.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-async-generator-functions/-/plugin-proposal-async-generator-functions-7.8.3.tgz#bad329c670b382589721b27540c7d288601c6e6f" + integrity sha512-NZ9zLv848JsV3hs8ryEh7Uaz/0KsmPLqv0+PdkDJL1cJy0K4kOCFa8zc1E3mp+RHPQcpdfb/6GovEsW4VDrOMw== + dependencies: + "@babel/helper-plugin-utils" "^7.8.3" + "@babel/helper-remap-async-to-generator" "^7.8.3" + "@babel/plugin-syntax-async-generators" "^7.8.0" + +"@babel/plugin-proposal-dynamic-import@^7.8.3": + version "7.8.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-dynamic-import/-/plugin-proposal-dynamic-import-7.8.3.tgz#38c4fe555744826e97e2ae930b0fb4cc07e66054" + integrity sha512-NyaBbyLFXFLT9FP+zk0kYlUlA8XtCUbehs67F0nnEg7KICgMc2mNkIeu9TYhKzyXMkrapZFwAhXLdnt4IYHy1w== + dependencies: + "@babel/helper-plugin-utils" "^7.8.3" + "@babel/plugin-syntax-dynamic-import" "^7.8.0" + +"@babel/plugin-proposal-json-strings@^7.8.3": + version "7.8.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-json-strings/-/plugin-proposal-json-strings-7.8.3.tgz#da5216b238a98b58a1e05d6852104b10f9a70d6b" + integrity sha512-KGhQNZ3TVCQG/MjRbAUwuH+14y9q0tpxs1nWWs3pbSleRdDro9SAMMDyye8HhY1gqZ7/NqIc8SKhya0wRDgP1Q== + dependencies: + "@babel/helper-plugin-utils" "^7.8.3" + "@babel/plugin-syntax-json-strings" "^7.8.0" + +"@babel/plugin-proposal-nullish-coalescing-operator@^7.8.3": + version "7.8.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-nullish-coalescing-operator/-/plugin-proposal-nullish-coalescing-operator-7.8.3.tgz#e4572253fdeed65cddeecfdab3f928afeb2fd5d2" + integrity sha512-TS9MlfzXpXKt6YYomudb/KU7nQI6/xnapG6in1uZxoxDghuSMZsPb6D2fyUwNYSAp4l1iR7QtFOjkqcRYcUsfw== + dependencies: + "@babel/helper-plugin-utils" "^7.8.3" + "@babel/plugin-syntax-nullish-coalescing-operator" "^7.8.0" + +"@babel/plugin-proposal-numeric-separator@^7.8.3": + version "7.8.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-numeric-separator/-/plugin-proposal-numeric-separator-7.8.3.tgz#5d6769409699ec9b3b68684cd8116cedff93bad8" + integrity sha512-jWioO1s6R/R+wEHizfaScNsAx+xKgwTLNXSh7tTC4Usj3ItsPEhYkEpU4h+lpnBwq7NBVOJXfO6cRFYcX69JUQ== + dependencies: + "@babel/helper-plugin-utils" "^7.8.3" + "@babel/plugin-syntax-numeric-separator" "^7.8.3" + +"@babel/plugin-proposal-object-rest-spread@^7.9.6": + version "7.9.6" + resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-object-rest-spread/-/plugin-proposal-object-rest-spread-7.9.6.tgz#7a093586fcb18b08266eb1a7177da671ac575b63" + integrity sha512-Ga6/fhGqA9Hj+y6whNpPv8psyaK5xzrQwSPsGPloVkvmH+PqW1ixdnfJ9uIO06OjQNYol3PMnfmJ8vfZtkzF+A== + dependencies: + "@babel/helper-plugin-utils" "^7.8.3" + "@babel/plugin-syntax-object-rest-spread" "^7.8.0" + "@babel/plugin-transform-parameters" "^7.9.5" + +"@babel/plugin-proposal-optional-catch-binding@^7.8.3": + version "7.8.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-optional-catch-binding/-/plugin-proposal-optional-catch-binding-7.8.3.tgz#9dee96ab1650eed88646ae9734ca167ac4a9c5c9" + integrity sha512-0gkX7J7E+AtAw9fcwlVQj8peP61qhdg/89D5swOkjYbkboA2CVckn3kiyum1DE0wskGb7KJJxBdyEBApDLLVdw== + dependencies: + "@babel/helper-plugin-utils" "^7.8.3" + "@babel/plugin-syntax-optional-catch-binding" "^7.8.0" + +"@babel/plugin-proposal-optional-chaining@^7.9.0": + version "7.9.0" + resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-optional-chaining/-/plugin-proposal-optional-chaining-7.9.0.tgz#31db16b154c39d6b8a645292472b98394c292a58" + integrity sha512-NDn5tu3tcv4W30jNhmc2hyD5c56G6cXx4TesJubhxrJeCvuuMpttxr0OnNCqbZGhFjLrg+NIhxxC+BK5F6yS3w== + dependencies: + "@babel/helper-plugin-utils" "^7.8.3" + "@babel/plugin-syntax-optional-chaining" "^7.8.0" + +"@babel/plugin-proposal-unicode-property-regex@^7.4.4", "@babel/plugin-proposal-unicode-property-regex@^7.8.3": + version "7.8.8" + resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-unicode-property-regex/-/plugin-proposal-unicode-property-regex-7.8.8.tgz#ee3a95e90cdc04fe8cd92ec3279fa017d68a0d1d" + integrity sha512-EVhjVsMpbhLw9ZfHWSx2iy13Q8Z/eg8e8ccVWt23sWQK5l1UdkoLJPN5w69UA4uITGBnEZD2JOe4QOHycYKv8A== + dependencies: + "@babel/helper-create-regexp-features-plugin" "^7.8.8" + "@babel/helper-plugin-utils" "^7.8.3" + +"@babel/plugin-syntax-async-generators@^7.8.0": + version "7.8.4" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-async-generators/-/plugin-syntax-async-generators-7.8.4.tgz#a983fb1aeb2ec3f6ed042a210f640e90e786fe0d" + integrity sha512-tycmZxkGfZaxhMRbXlPXuVFpdWlXpir2W4AMhSJgRKzk/eDlIXOhb2LHWoLpDF7TEHylV5zNhykX6KAgHJmTNw== + dependencies: + "@babel/helper-plugin-utils" "^7.8.0" + +"@babel/plugin-syntax-dynamic-import@^7.8.0": + version "7.8.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-dynamic-import/-/plugin-syntax-dynamic-import-7.8.3.tgz#62bf98b2da3cd21d626154fc96ee5b3cb68eacb3" + integrity sha512-5gdGbFon+PszYzqs83S3E5mpi7/y/8M9eC90MRTZfduQOYW76ig6SOSPNe41IG5LoP3FGBn2N0RjVDSQiS94kQ== + dependencies: + "@babel/helper-plugin-utils" "^7.8.0" + +"@babel/plugin-syntax-json-strings@^7.8.0": + version "7.8.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-json-strings/-/plugin-syntax-json-strings-7.8.3.tgz#01ca21b668cd8218c9e640cb6dd88c5412b2c96a" + integrity sha512-lY6kdGpWHvjoe2vk4WrAapEuBR69EMxZl+RoGRhrFGNYVK8mOPAW8VfbT/ZgrFbXlDNiiaxQnAtgVCZ6jv30EA== + dependencies: + "@babel/helper-plugin-utils" "^7.8.0" + +"@babel/plugin-syntax-nullish-coalescing-operator@^7.8.0": + version "7.8.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-nullish-coalescing-operator/-/plugin-syntax-nullish-coalescing-operator-7.8.3.tgz#167ed70368886081f74b5c36c65a88c03b66d1a9" + integrity sha512-aSff4zPII1u2QD7y+F8oDsz19ew4IGEJg9SVW+bqwpwtfFleiQDMdzA/R+UlWDzfnHFCxxleFT0PMIrR36XLNQ== + dependencies: + "@babel/helper-plugin-utils" "^7.8.0" + +"@babel/plugin-syntax-numeric-separator@^7.8.0", "@babel/plugin-syntax-numeric-separator@^7.8.3": + version "7.8.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-numeric-separator/-/plugin-syntax-numeric-separator-7.8.3.tgz#0e3fb63e09bea1b11e96467271c8308007e7c41f" + integrity sha512-H7dCMAdN83PcCmqmkHB5dtp+Xa9a6LKSvA2hiFBC/5alSHxM5VgWZXFqDi0YFe8XNGT6iCa+z4V4zSt/PdZ7Dw== + dependencies: + "@babel/helper-plugin-utils" "^7.8.3" + +"@babel/plugin-syntax-object-rest-spread@^7.0.0", "@babel/plugin-syntax-object-rest-spread@^7.8.0": + version "7.8.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-object-rest-spread/-/plugin-syntax-object-rest-spread-7.8.3.tgz#60e225edcbd98a640332a2e72dd3e66f1af55871" + integrity sha512-XoqMijGZb9y3y2XskN+P1wUGiVwWZ5JmoDRwx5+3GmEplNyVM2s2Dg8ILFQm8rWM48orGy5YpI5Bl8U1y7ydlA== + dependencies: + "@babel/helper-plugin-utils" "^7.8.0" + +"@babel/plugin-syntax-optional-catch-binding@^7.8.0": + version "7.8.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-optional-catch-binding/-/plugin-syntax-optional-catch-binding-7.8.3.tgz#6111a265bcfb020eb9efd0fdfd7d26402b9ed6c1" + integrity sha512-6VPD0Pc1lpTqw0aKoeRTMiB+kWhAoT24PA+ksWSBrFtl5SIRVpZlwN3NNPQjehA2E/91FV3RjLWoVTglWcSV3Q== + dependencies: + "@babel/helper-plugin-utils" "^7.8.0" + +"@babel/plugin-syntax-optional-chaining@^7.8.0": + version "7.8.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-optional-chaining/-/plugin-syntax-optional-chaining-7.8.3.tgz#4f69c2ab95167e0180cd5336613f8c5788f7d48a" + integrity sha512-KoK9ErH1MBlCPxV0VANkXW2/dw4vlbGDrFgz8bmUsBGYkFRcbRwMh6cIJubdPrkxRwuGdtCk0v/wPTKbQgBjkg== + dependencies: + "@babel/helper-plugin-utils" "^7.8.0" + +"@babel/plugin-syntax-top-level-await@^7.8.3": + version "7.8.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-top-level-await/-/plugin-syntax-top-level-await-7.8.3.tgz#3acdece695e6b13aaf57fc291d1a800950c71391" + integrity sha512-kwj1j9lL/6Wd0hROD3b/OZZ7MSrZLqqn9RAZ5+cYYsflQ9HZBIKCUkr3+uL1MEJ1NePiUbf98jjiMQSv0NMR9g== + dependencies: + "@babel/helper-plugin-utils" "^7.8.3" + +"@babel/plugin-transform-arrow-functions@^7.8.3": + version "7.8.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-arrow-functions/-/plugin-transform-arrow-functions-7.8.3.tgz#82776c2ed0cd9e1a49956daeb896024c9473b8b6" + integrity sha512-0MRF+KC8EqH4dbuITCWwPSzsyO3HIWWlm30v8BbbpOrS1B++isGxPnnuq/IZvOX5J2D/p7DQalQm+/2PnlKGxg== + dependencies: + "@babel/helper-plugin-utils" "^7.8.3" + +"@babel/plugin-transform-async-to-generator@^7.8.3": + version "7.8.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-async-to-generator/-/plugin-transform-async-to-generator-7.8.3.tgz#4308fad0d9409d71eafb9b1a6ee35f9d64b64086" + integrity sha512-imt9tFLD9ogt56Dd5CI/6XgpukMwd/fLGSrix2httihVe7LOGVPhyhMh1BU5kDM7iHD08i8uUtmV2sWaBFlHVQ== + dependencies: + "@babel/helper-module-imports" "^7.8.3" + "@babel/helper-plugin-utils" "^7.8.3" + "@babel/helper-remap-async-to-generator" "^7.8.3" + +"@babel/plugin-transform-block-scoped-functions@^7.8.3": + version "7.8.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-block-scoped-functions/-/plugin-transform-block-scoped-functions-7.8.3.tgz#437eec5b799b5852072084b3ae5ef66e8349e8a3" + integrity sha512-vo4F2OewqjbB1+yaJ7k2EJFHlTP3jR634Z9Cj9itpqNjuLXvhlVxgnjsHsdRgASR8xYDrx6onw4vW5H6We0Jmg== + dependencies: + "@babel/helper-plugin-utils" "^7.8.3" + +"@babel/plugin-transform-block-scoping@^7.8.3": + version "7.8.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-block-scoping/-/plugin-transform-block-scoping-7.8.3.tgz#97d35dab66857a437c166358b91d09050c868f3a" + integrity sha512-pGnYfm7RNRgYRi7bids5bHluENHqJhrV4bCZRwc5GamaWIIs07N4rZECcmJL6ZClwjDz1GbdMZFtPs27hTB06w== + dependencies: + "@babel/helper-plugin-utils" "^7.8.3" + lodash "^4.17.13" + +"@babel/plugin-transform-classes@^7.9.5": + version "7.9.5" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-classes/-/plugin-transform-classes-7.9.5.tgz#800597ddb8aefc2c293ed27459c1fcc935a26c2c" + integrity sha512-x2kZoIuLC//O5iA7PEvecB105o7TLzZo8ofBVhP79N+DO3jaX+KYfww9TQcfBEZD0nikNyYcGB1IKtRq36rdmg== + dependencies: + "@babel/helper-annotate-as-pure" "^7.8.3" + "@babel/helper-define-map" "^7.8.3" + "@babel/helper-function-name" "^7.9.5" + "@babel/helper-optimise-call-expression" "^7.8.3" + "@babel/helper-plugin-utils" "^7.8.3" + "@babel/helper-replace-supers" "^7.8.6" + "@babel/helper-split-export-declaration" "^7.8.3" + globals "^11.1.0" + +"@babel/plugin-transform-computed-properties@^7.8.3": + version "7.8.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-computed-properties/-/plugin-transform-computed-properties-7.8.3.tgz#96d0d28b7f7ce4eb5b120bb2e0e943343c86f81b" + integrity sha512-O5hiIpSyOGdrQZRQ2ccwtTVkgUDBBiCuK//4RJ6UfePllUTCENOzKxfh6ulckXKc0DixTFLCfb2HVkNA7aDpzA== + dependencies: + "@babel/helper-plugin-utils" "^7.8.3" + +"@babel/plugin-transform-destructuring@^7.9.5": + version "7.9.5" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-destructuring/-/plugin-transform-destructuring-7.9.5.tgz#72c97cf5f38604aea3abf3b935b0e17b1db76a50" + integrity sha512-j3OEsGel8nHL/iusv/mRd5fYZ3DrOxWC82x0ogmdN/vHfAP4MYw+AFKYanzWlktNwikKvlzUV//afBW5FTp17Q== + dependencies: + "@babel/helper-plugin-utils" "^7.8.3" + +"@babel/plugin-transform-dotall-regex@^7.4.4", "@babel/plugin-transform-dotall-regex@^7.8.3": + version "7.8.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-dotall-regex/-/plugin-transform-dotall-regex-7.8.3.tgz#c3c6ec5ee6125c6993c5cbca20dc8621a9ea7a6e" + integrity sha512-kLs1j9Nn4MQoBYdRXH6AeaXMbEJFaFu/v1nQkvib6QzTj8MZI5OQzqmD83/2jEM1z0DLilra5aWO5YpyC0ALIw== + dependencies: + "@babel/helper-create-regexp-features-plugin" "^7.8.3" + "@babel/helper-plugin-utils" "^7.8.3" + +"@babel/plugin-transform-duplicate-keys@^7.8.3": + version "7.8.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-duplicate-keys/-/plugin-transform-duplicate-keys-7.8.3.tgz#8d12df309aa537f272899c565ea1768e286e21f1" + integrity sha512-s8dHiBUbcbSgipS4SMFuWGqCvyge5V2ZeAWzR6INTVC3Ltjig/Vw1G2Gztv0vU/hRG9X8IvKvYdoksnUfgXOEQ== + dependencies: + "@babel/helper-plugin-utils" "^7.8.3" + +"@babel/plugin-transform-exponentiation-operator@^7.8.3": + version "7.8.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-exponentiation-operator/-/plugin-transform-exponentiation-operator-7.8.3.tgz#581a6d7f56970e06bf51560cd64f5e947b70d7b7" + integrity sha512-zwIpuIymb3ACcInbksHaNcR12S++0MDLKkiqXHl3AzpgdKlFNhog+z/K0+TGW+b0w5pgTq4H6IwV/WhxbGYSjQ== + dependencies: + "@babel/helper-builder-binary-assignment-operator-visitor" "^7.8.3" + "@babel/helper-plugin-utils" "^7.8.3" + +"@babel/plugin-transform-for-of@^7.9.0": + version "7.9.0" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-for-of/-/plugin-transform-for-of-7.9.0.tgz#0f260e27d3e29cd1bb3128da5e76c761aa6c108e" + integrity sha512-lTAnWOpMwOXpyDx06N+ywmF3jNbafZEqZ96CGYabxHrxNX8l5ny7dt4bK/rGwAh9utyP2b2Hv7PlZh1AAS54FQ== + dependencies: + "@babel/helper-plugin-utils" "^7.8.3" + +"@babel/plugin-transform-function-name@^7.8.3": + version "7.8.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-function-name/-/plugin-transform-function-name-7.8.3.tgz#279373cb27322aaad67c2683e776dfc47196ed8b" + integrity sha512-rO/OnDS78Eifbjn5Py9v8y0aR+aSYhDhqAwVfsTl0ERuMZyr05L1aFSCJnbv2mmsLkit/4ReeQ9N2BgLnOcPCQ== + dependencies: + "@babel/helper-function-name" "^7.8.3" + "@babel/helper-plugin-utils" "^7.8.3" + +"@babel/plugin-transform-literals@^7.8.3": + version "7.8.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-literals/-/plugin-transform-literals-7.8.3.tgz#aef239823d91994ec7b68e55193525d76dbd5dc1" + integrity sha512-3Tqf8JJ/qB7TeldGl+TT55+uQei9JfYaregDcEAyBZ7akutriFrt6C/wLYIer6OYhleVQvH/ntEhjE/xMmy10A== + dependencies: + "@babel/helper-plugin-utils" "^7.8.3" + +"@babel/plugin-transform-member-expression-literals@^7.8.3": + version "7.8.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-member-expression-literals/-/plugin-transform-member-expression-literals-7.8.3.tgz#963fed4b620ac7cbf6029c755424029fa3a40410" + integrity sha512-3Wk2EXhnw+rP+IDkK6BdtPKsUE5IeZ6QOGrPYvw52NwBStw9V1ZVzxgK6fSKSxqUvH9eQPR3tm3cOq79HlsKYA== + dependencies: + "@babel/helper-plugin-utils" "^7.8.3" + +"@babel/plugin-transform-modules-amd@^7.9.6": + version "7.9.6" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-amd/-/plugin-transform-modules-amd-7.9.6.tgz#8539ec42c153d12ea3836e0e3ac30d5aae7b258e" + integrity sha512-zoT0kgC3EixAyIAU+9vfaUVKTv9IxBDSabgHoUCBP6FqEJ+iNiN7ip7NBKcYqbfUDfuC2mFCbM7vbu4qJgOnDw== + dependencies: + "@babel/helper-module-transforms" "^7.9.0" + "@babel/helper-plugin-utils" "^7.8.3" + babel-plugin-dynamic-import-node "^2.3.3" + +"@babel/plugin-transform-modules-commonjs@^7.9.6": + version "7.9.6" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.9.6.tgz#64b7474a4279ee588cacd1906695ca721687c277" + integrity sha512-7H25fSlLcn+iYimmsNe3uK1at79IE6SKW9q0/QeEHTMC9MdOZ+4bA+T1VFB5fgOqBWoqlifXRzYD0JPdmIrgSQ== + dependencies: + "@babel/helper-module-transforms" "^7.9.0" + "@babel/helper-plugin-utils" "^7.8.3" + "@babel/helper-simple-access" "^7.8.3" + babel-plugin-dynamic-import-node "^2.3.3" + +"@babel/plugin-transform-modules-systemjs@^7.9.6": + version "7.9.6" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-systemjs/-/plugin-transform-modules-systemjs-7.9.6.tgz#207f1461c78a231d5337a92140e52422510d81a4" + integrity sha512-NW5XQuW3N2tTHim8e1b7qGy7s0kZ2OH3m5octc49K1SdAKGxYxeIx7hiIz05kS1R2R+hOWcsr1eYwcGhrdHsrg== + dependencies: + "@babel/helper-hoist-variables" "^7.8.3" + "@babel/helper-module-transforms" "^7.9.0" + "@babel/helper-plugin-utils" "^7.8.3" + babel-plugin-dynamic-import-node "^2.3.3" + +"@babel/plugin-transform-modules-umd@^7.9.0": + version "7.9.0" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-umd/-/plugin-transform-modules-umd-7.9.0.tgz#e909acae276fec280f9b821a5f38e1f08b480697" + integrity sha512-uTWkXkIVtg/JGRSIABdBoMsoIeoHQHPTL0Y2E7xf5Oj7sLqwVsNXOkNk0VJc7vF0IMBsPeikHxFjGe+qmwPtTQ== + dependencies: + "@babel/helper-module-transforms" "^7.9.0" + "@babel/helper-plugin-utils" "^7.8.3" + +"@babel/plugin-transform-named-capturing-groups-regex@^7.8.3": + version "7.8.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-named-capturing-groups-regex/-/plugin-transform-named-capturing-groups-regex-7.8.3.tgz#a2a72bffa202ac0e2d0506afd0939c5ecbc48c6c" + integrity sha512-f+tF/8UVPU86TrCb06JoPWIdDpTNSGGcAtaD9mLP0aYGA0OS0j7j7DHJR0GTFrUZPUU6loZhbsVZgTh0N+Qdnw== + dependencies: + "@babel/helper-create-regexp-features-plugin" "^7.8.3" + +"@babel/plugin-transform-new-target@^7.8.3": + version "7.8.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-new-target/-/plugin-transform-new-target-7.8.3.tgz#60cc2ae66d85c95ab540eb34babb6434d4c70c43" + integrity sha512-QuSGysibQpyxexRyui2vca+Cmbljo8bcRckgzYV4kRIsHpVeyeC3JDO63pY+xFZ6bWOBn7pfKZTqV4o/ix9sFw== + dependencies: + "@babel/helper-plugin-utils" "^7.8.3" + +"@babel/plugin-transform-object-super@^7.8.3": + version "7.8.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-object-super/-/plugin-transform-object-super-7.8.3.tgz#ebb6a1e7a86ffa96858bd6ac0102d65944261725" + integrity sha512-57FXk+gItG/GejofIyLIgBKTas4+pEU47IXKDBWFTxdPd7F80H8zybyAY7UoblVfBhBGs2EKM+bJUu2+iUYPDQ== + dependencies: + "@babel/helper-plugin-utils" "^7.8.3" + "@babel/helper-replace-supers" "^7.8.3" + +"@babel/plugin-transform-parameters@^7.9.5": + version "7.9.5" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-parameters/-/plugin-transform-parameters-7.9.5.tgz#173b265746f5e15b2afe527eeda65b73623a0795" + integrity sha512-0+1FhHnMfj6lIIhVvS4KGQJeuhe1GI//h5uptK4PvLt+BGBxsoUJbd3/IW002yk//6sZPlFgsG1hY6OHLcy6kA== + dependencies: + "@babel/helper-get-function-arity" "^7.8.3" + "@babel/helper-plugin-utils" "^7.8.3" + +"@babel/plugin-transform-property-literals@^7.8.3": + version "7.8.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-property-literals/-/plugin-transform-property-literals-7.8.3.tgz#33194300d8539c1ed28c62ad5087ba3807b98263" + integrity sha512-uGiiXAZMqEoQhRWMK17VospMZh5sXWg+dlh2soffpkAl96KAm+WZuJfa6lcELotSRmooLqg0MWdH6UUq85nmmg== + dependencies: + "@babel/helper-plugin-utils" "^7.8.3" + +"@babel/plugin-transform-regenerator@^7.8.7": + version "7.8.7" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-regenerator/-/plugin-transform-regenerator-7.8.7.tgz#5e46a0dca2bee1ad8285eb0527e6abc9c37672f8" + integrity sha512-TIg+gAl4Z0a3WmD3mbYSk+J9ZUH6n/Yc57rtKRnlA/7rcCvpekHXe0CMZHP1gYp7/KLe9GHTuIba0vXmls6drA== + dependencies: + regenerator-transform "^0.14.2" + +"@babel/plugin-transform-reserved-words@^7.8.3": + version "7.8.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-reserved-words/-/plugin-transform-reserved-words-7.8.3.tgz#9a0635ac4e665d29b162837dd3cc50745dfdf1f5" + integrity sha512-mwMxcycN3omKFDjDQUl+8zyMsBfjRFr0Zn/64I41pmjv4NJuqcYlEtezwYtw9TFd9WR1vN5kiM+O0gMZzO6L0A== + dependencies: + "@babel/helper-plugin-utils" "^7.8.3" + +"@babel/plugin-transform-runtime@^7.9.0": + version "7.9.6" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-runtime/-/plugin-transform-runtime-7.9.6.tgz#3ba804438ad0d880a17bca5eaa0cdf1edeedb2fd" + integrity sha512-qcmiECD0mYOjOIt8YHNsAP1SxPooC/rDmfmiSK9BNY72EitdSc7l44WTEklaWuFtbOEBjNhWWyph/kOImbNJ4w== + dependencies: + "@babel/helper-module-imports" "^7.8.3" + "@babel/helper-plugin-utils" "^7.8.3" + resolve "^1.8.1" + semver "^5.5.1" + +"@babel/plugin-transform-shorthand-properties@^7.8.3": + version "7.8.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-shorthand-properties/-/plugin-transform-shorthand-properties-7.8.3.tgz#28545216e023a832d4d3a1185ed492bcfeac08c8" + integrity sha512-I9DI6Odg0JJwxCHzbzW08ggMdCezoWcuQRz3ptdudgwaHxTjxw5HgdFJmZIkIMlRymL6YiZcped4TTCB0JcC8w== + dependencies: + "@babel/helper-plugin-utils" "^7.8.3" + +"@babel/plugin-transform-spread@^7.8.3": + version "7.8.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-spread/-/plugin-transform-spread-7.8.3.tgz#9c8ffe8170fdfb88b114ecb920b82fb6e95fe5e8" + integrity sha512-CkuTU9mbmAoFOI1tklFWYYbzX5qCIZVXPVy0jpXgGwkplCndQAa58s2jr66fTeQnA64bDox0HL4U56CFYoyC7g== + dependencies: + "@babel/helper-plugin-utils" "^7.8.3" + +"@babel/plugin-transform-sticky-regex@^7.8.3": + version "7.8.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-sticky-regex/-/plugin-transform-sticky-regex-7.8.3.tgz#be7a1290f81dae767475452199e1f76d6175b100" + integrity sha512-9Spq0vGCD5Bb4Z/ZXXSK5wbbLFMG085qd2vhL1JYu1WcQ5bXqZBAYRzU1d+p79GcHs2szYv5pVQCX13QgldaWw== + dependencies: + "@babel/helper-plugin-utils" "^7.8.3" + "@babel/helper-regex" "^7.8.3" + +"@babel/plugin-transform-template-literals@^7.8.3": + version "7.8.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-template-literals/-/plugin-transform-template-literals-7.8.3.tgz#7bfa4732b455ea6a43130adc0ba767ec0e402a80" + integrity sha512-820QBtykIQOLFT8NZOcTRJ1UNuztIELe4p9DCgvj4NK+PwluSJ49we7s9FB1HIGNIYT7wFUJ0ar2QpCDj0escQ== + dependencies: + "@babel/helper-annotate-as-pure" "^7.8.3" + "@babel/helper-plugin-utils" "^7.8.3" + +"@babel/plugin-transform-typeof-symbol@^7.8.4": + version "7.8.4" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-typeof-symbol/-/plugin-transform-typeof-symbol-7.8.4.tgz#ede4062315ce0aaf8a657a920858f1a2f35fc412" + integrity sha512-2QKyfjGdvuNfHsb7qnBBlKclbD4CfshH2KvDabiijLMGXPHJXGxtDzwIF7bQP+T0ysw8fYTtxPafgfs/c1Lrqg== + dependencies: + "@babel/helper-plugin-utils" "^7.8.3" + +"@babel/plugin-transform-unicode-regex@^7.8.3": + version "7.8.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-unicode-regex/-/plugin-transform-unicode-regex-7.8.3.tgz#0cef36e3ba73e5c57273effb182f46b91a1ecaad" + integrity sha512-+ufgJjYdmWfSQ+6NS9VGUR2ns8cjJjYbrbi11mZBTaWm+Fui/ncTLFF28Ei1okavY+xkojGr1eJxNsWYeA5aZw== + dependencies: + "@babel/helper-create-regexp-features-plugin" "^7.8.3" + "@babel/helper-plugin-utils" "^7.8.3" + +"@babel/preset-env@^7.9.5": + version "7.9.6" + resolved "https://registry.yarnpkg.com/@babel/preset-env/-/preset-env-7.9.6.tgz#df063b276c6455ec6fcfc6e53aacc38da9b0aea6" + integrity sha512-0gQJ9RTzO0heXOhzftog+a/WyOuqMrAIugVYxMYf83gh1CQaQDjMtsOpqOwXyDL/5JcWsrCm8l4ju8QC97O7EQ== + dependencies: + "@babel/compat-data" "^7.9.6" + "@babel/helper-compilation-targets" "^7.9.6" + "@babel/helper-module-imports" "^7.8.3" + "@babel/helper-plugin-utils" "^7.8.3" + "@babel/plugin-proposal-async-generator-functions" "^7.8.3" + "@babel/plugin-proposal-dynamic-import" "^7.8.3" + "@babel/plugin-proposal-json-strings" "^7.8.3" + "@babel/plugin-proposal-nullish-coalescing-operator" "^7.8.3" + "@babel/plugin-proposal-numeric-separator" "^7.8.3" + "@babel/plugin-proposal-object-rest-spread" "^7.9.6" + "@babel/plugin-proposal-optional-catch-binding" "^7.8.3" + "@babel/plugin-proposal-optional-chaining" "^7.9.0" + "@babel/plugin-proposal-unicode-property-regex" "^7.8.3" + "@babel/plugin-syntax-async-generators" "^7.8.0" + "@babel/plugin-syntax-dynamic-import" "^7.8.0" + "@babel/plugin-syntax-json-strings" "^7.8.0" + "@babel/plugin-syntax-nullish-coalescing-operator" "^7.8.0" + "@babel/plugin-syntax-numeric-separator" "^7.8.0" + "@babel/plugin-syntax-object-rest-spread" "^7.8.0" + "@babel/plugin-syntax-optional-catch-binding" "^7.8.0" + "@babel/plugin-syntax-optional-chaining" "^7.8.0" + "@babel/plugin-syntax-top-level-await" "^7.8.3" + "@babel/plugin-transform-arrow-functions" "^7.8.3" + "@babel/plugin-transform-async-to-generator" "^7.8.3" + "@babel/plugin-transform-block-scoped-functions" "^7.8.3" + "@babel/plugin-transform-block-scoping" "^7.8.3" + "@babel/plugin-transform-classes" "^7.9.5" + "@babel/plugin-transform-computed-properties" "^7.8.3" + "@babel/plugin-transform-destructuring" "^7.9.5" + "@babel/plugin-transform-dotall-regex" "^7.8.3" + "@babel/plugin-transform-duplicate-keys" "^7.8.3" + "@babel/plugin-transform-exponentiation-operator" "^7.8.3" + "@babel/plugin-transform-for-of" "^7.9.0" + "@babel/plugin-transform-function-name" "^7.8.3" + "@babel/plugin-transform-literals" "^7.8.3" + "@babel/plugin-transform-member-expression-literals" "^7.8.3" + "@babel/plugin-transform-modules-amd" "^7.9.6" + "@babel/plugin-transform-modules-commonjs" "^7.9.6" + "@babel/plugin-transform-modules-systemjs" "^7.9.6" + "@babel/plugin-transform-modules-umd" "^7.9.0" + "@babel/plugin-transform-named-capturing-groups-regex" "^7.8.3" + "@babel/plugin-transform-new-target" "^7.8.3" + "@babel/plugin-transform-object-super" "^7.8.3" + "@babel/plugin-transform-parameters" "^7.9.5" + "@babel/plugin-transform-property-literals" "^7.8.3" + "@babel/plugin-transform-regenerator" "^7.8.7" + "@babel/plugin-transform-reserved-words" "^7.8.3" + "@babel/plugin-transform-shorthand-properties" "^7.8.3" + "@babel/plugin-transform-spread" "^7.8.3" + "@babel/plugin-transform-sticky-regex" "^7.8.3" + "@babel/plugin-transform-template-literals" "^7.8.3" + "@babel/plugin-transform-typeof-symbol" "^7.8.4" + "@babel/plugin-transform-unicode-regex" "^7.8.3" + "@babel/preset-modules" "^0.1.3" + "@babel/types" "^7.9.6" + browserslist "^4.11.1" + core-js-compat "^3.6.2" + invariant "^2.2.2" + levenary "^1.1.1" + semver "^5.5.0" + +"@babel/preset-modules@^0.1.3": + version "0.1.3" + resolved "https://registry.yarnpkg.com/@babel/preset-modules/-/preset-modules-0.1.3.tgz#13242b53b5ef8c883c3cf7dddd55b36ce80fbc72" + integrity sha512-Ra3JXOHBq2xd56xSF7lMKXdjBn3T772Y1Wet3yWnkDly9zHvJki029tAFzvAAK5cf4YV3yoxuP61crYRol6SVg== + dependencies: + "@babel/helper-plugin-utils" "^7.0.0" + "@babel/plugin-proposal-unicode-property-regex" "^7.4.4" + "@babel/plugin-transform-dotall-regex" "^7.4.4" + "@babel/types" "^7.4.4" + esutils "^2.0.2" + +"@babel/register@^7.8.3": + version "7.9.0" + resolved "https://registry.yarnpkg.com/@babel/register/-/register-7.9.0.tgz#02464ede57548bddbb5e9f705d263b7c3f43d48b" + integrity sha512-Tv8Zyi2J2VRR8g7pC5gTeIN8Ihultbmk0ocyNz8H2nEZbmhp1N6q0A1UGsQbDvGP/sNinQKUHf3SqXwqjtFv4Q== + dependencies: + find-cache-dir "^2.0.0" + lodash "^4.17.13" + make-dir "^2.1.0" + pirates "^4.0.0" + source-map-support "^0.5.16" + +"@babel/runtime@^7.7.4", "@babel/runtime@^7.8.4", "@babel/runtime@^7.9.2": + version "7.9.6" + resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.9.6.tgz#a9102eb5cadedf3f31d08a9ecf294af7827ea29f" + integrity sha512-64AF1xY3OAkFHqOb9s4jpgk1Mm5vDZ4L3acHvAml+53nO1XbXLuDodsVpO4OIUsmemlUHMxNdYMNJmsvOwLrvQ== + dependencies: + regenerator-runtime "^0.13.4" + +"@babel/template@^7.4.0", "@babel/template@^7.8.3", "@babel/template@^7.8.6": + version "7.8.6" + resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.8.6.tgz#86b22af15f828dfb086474f964dcc3e39c43ce2b" + integrity sha512-zbMsPMy/v0PWFZEhQJ66bqjhH+z0JgMoBWuikXybgG3Gkd/3t5oQ1Rw2WQhnSrsOmsKXnZOx15tkC4qON/+JPg== + dependencies: + "@babel/code-frame" "^7.8.3" + "@babel/parser" "^7.8.6" + "@babel/types" "^7.8.6" + +"@babel/traverse@^7.1.0", "@babel/traverse@^7.4.3", "@babel/traverse@^7.7.0", "@babel/traverse@^7.8.3", "@babel/traverse@^7.9.6": + version "7.9.6" + resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.9.6.tgz#5540d7577697bf619cc57b92aa0f1c231a94f442" + integrity sha512-b3rAHSjbxy6VEAvlxM8OV/0X4XrG72zoxme6q1MOoe2vd0bEc+TwayhuC1+Dfgqh1QEG+pj7atQqvUprHIccsg== + dependencies: + "@babel/code-frame" "^7.8.3" + "@babel/generator" "^7.9.6" + "@babel/helper-function-name" "^7.9.5" + "@babel/helper-split-export-declaration" "^7.8.3" + "@babel/parser" "^7.9.6" + "@babel/types" "^7.9.6" + debug "^4.1.0" + globals "^11.1.0" + lodash "^4.17.13" + +"@babel/types@^7.0.0", "@babel/types@^7.3.0", "@babel/types@^7.4.0", "@babel/types@^7.4.4", "@babel/types@^7.7.0", "@babel/types@^7.8.3", "@babel/types@^7.8.6", "@babel/types@^7.9.0", "@babel/types@^7.9.5", "@babel/types@^7.9.6": + version "7.9.6" + resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.9.6.tgz#2c5502b427251e9de1bd2dff95add646d95cc9f7" + integrity sha512-qxXzvBO//jO9ZnoasKF1uJzHd2+M6Q2ZPIVfnFps8JJvXy0ZBbwbNOmE6SGIY5XOY6d1Bo5lb9d9RJ8nv3WSeA== + dependencies: + "@babel/helper-validator-identifier" "^7.9.5" + lodash "^4.17.13" + to-fast-properties "^2.0.0" + +"@cnakazawa/watch@^1.0.3": + version "1.0.4" + resolved "https://registry.yarnpkg.com/@cnakazawa/watch/-/watch-1.0.4.tgz#f864ae85004d0fcab6f50be9141c4da368d1656a" + integrity sha512-v9kIhKwjeZThiWrLmj0y17CWoyddASLj9O2yvbZkbvw/N3rWOYy9zkV66ursAoVr0mV15bL8g0c4QZUE6cdDoQ== + dependencies: + exec-sh "^0.3.2" + minimist "^1.2.0" + +"@jest/console@^24.7.1", "@jest/console@^24.9.0": + version "24.9.0" + resolved "https://registry.yarnpkg.com/@jest/console/-/console-24.9.0.tgz#79b1bc06fb74a8cfb01cbdedf945584b1b9707f0" + integrity sha512-Zuj6b8TnKXi3q4ymac8EQfc3ea/uhLeCGThFqXeC8H9/raaH8ARPUTdId+XyGd03Z4In0/VjD2OYFcBF09fNLQ== + dependencies: + "@jest/source-map" "^24.9.0" + chalk "^2.0.1" + slash "^2.0.0" + +"@jest/core@^24.9.0": + version "24.9.0" + resolved "https://registry.yarnpkg.com/@jest/core/-/core-24.9.0.tgz#2ceccd0b93181f9c4850e74f2a9ad43d351369c4" + integrity sha512-Fogg3s4wlAr1VX7q+rhV9RVnUv5tD7VuWfYy1+whMiWUrvl7U3QJSJyWcDio9Lq2prqYsZaeTv2Rz24pWGkJ2A== + dependencies: + "@jest/console" "^24.7.1" + "@jest/reporters" "^24.9.0" + "@jest/test-result" "^24.9.0" + "@jest/transform" "^24.9.0" + "@jest/types" "^24.9.0" + ansi-escapes "^3.0.0" + chalk "^2.0.1" + exit "^0.1.2" + graceful-fs "^4.1.15" + jest-changed-files "^24.9.0" + jest-config "^24.9.0" + jest-haste-map "^24.9.0" + jest-message-util "^24.9.0" + jest-regex-util "^24.3.0" + jest-resolve "^24.9.0" + jest-resolve-dependencies "^24.9.0" + jest-runner "^24.9.0" + jest-runtime "^24.9.0" + jest-snapshot "^24.9.0" + jest-util "^24.9.0" + jest-validate "^24.9.0" + jest-watcher "^24.9.0" + micromatch "^3.1.10" + p-each-series "^1.0.0" + realpath-native "^1.1.0" + rimraf "^2.5.4" + slash "^2.0.0" + strip-ansi "^5.0.0" + +"@jest/environment@^24.9.0": + version "24.9.0" + resolved "https://registry.yarnpkg.com/@jest/environment/-/environment-24.9.0.tgz#21e3afa2d65c0586cbd6cbefe208bafade44ab18" + integrity sha512-5A1QluTPhvdIPFYnO3sZC3smkNeXPVELz7ikPbhUj0bQjB07EoE9qtLrem14ZUYWdVayYbsjVwIiL4WBIMV4aQ== + dependencies: + "@jest/fake-timers" "^24.9.0" + "@jest/transform" "^24.9.0" + "@jest/types" "^24.9.0" + jest-mock "^24.9.0" + +"@jest/fake-timers@^24.9.0": + version "24.9.0" + resolved "https://registry.yarnpkg.com/@jest/fake-timers/-/fake-timers-24.9.0.tgz#ba3e6bf0eecd09a636049896434d306636540c93" + integrity sha512-eWQcNa2YSwzXWIMC5KufBh3oWRIijrQFROsIqt6v/NS9Io/gknw1jsAC9c+ih/RQX4A3O7SeWAhQeN0goKhT9A== + dependencies: + "@jest/types" "^24.9.0" + jest-message-util "^24.9.0" + jest-mock "^24.9.0" + +"@jest/reporters@^24.9.0": + version "24.9.0" + resolved "https://registry.yarnpkg.com/@jest/reporters/-/reporters-24.9.0.tgz#86660eff8e2b9661d042a8e98a028b8d631a5b43" + integrity sha512-mu4X0yjaHrffOsWmVLzitKmmmWSQ3GGuefgNscUSWNiUNcEOSEQk9k3pERKEQVBb0Cnn88+UESIsZEMH3o88Gw== + dependencies: + "@jest/environment" "^24.9.0" + "@jest/test-result" "^24.9.0" + "@jest/transform" "^24.9.0" + "@jest/types" "^24.9.0" + chalk "^2.0.1" + exit "^0.1.2" + glob "^7.1.2" + istanbul-lib-coverage "^2.0.2" + istanbul-lib-instrument "^3.0.1" + istanbul-lib-report "^2.0.4" + istanbul-lib-source-maps "^3.0.1" + istanbul-reports "^2.2.6" + jest-haste-map "^24.9.0" + jest-resolve "^24.9.0" + jest-runtime "^24.9.0" + jest-util "^24.9.0" + jest-worker "^24.6.0" + node-notifier "^5.4.2" + slash "^2.0.0" + source-map "^0.6.0" + string-length "^2.0.0" + +"@jest/source-map@^24.3.0", "@jest/source-map@^24.9.0": + version "24.9.0" + resolved "https://registry.yarnpkg.com/@jest/source-map/-/source-map-24.9.0.tgz#0e263a94430be4b41da683ccc1e6bffe2a191714" + integrity sha512-/Xw7xGlsZb4MJzNDgB7PW5crou5JqWiBQaz6xyPd3ArOg2nfn/PunV8+olXbbEZzNl591o5rWKE9BRDaFAuIBg== + dependencies: + callsites "^3.0.0" + graceful-fs "^4.1.15" + source-map "^0.6.0" + +"@jest/test-result@^24.9.0": + version "24.9.0" + resolved "https://registry.yarnpkg.com/@jest/test-result/-/test-result-24.9.0.tgz#11796e8aa9dbf88ea025757b3152595ad06ba0ca" + integrity sha512-XEFrHbBonBJ8dGp2JmF8kP/nQI/ImPpygKHwQ/SY+es59Z3L5PI4Qb9TQQMAEeYsThG1xF0k6tmG0tIKATNiiA== + dependencies: + "@jest/console" "^24.9.0" + "@jest/types" "^24.9.0" + "@types/istanbul-lib-coverage" "^2.0.0" + +"@jest/test-sequencer@^24.9.0": + version "24.9.0" + resolved "https://registry.yarnpkg.com/@jest/test-sequencer/-/test-sequencer-24.9.0.tgz#f8f334f35b625a4f2f355f2fe7e6036dad2e6b31" + integrity sha512-6qqsU4o0kW1dvA95qfNog8v8gkRN9ph6Lz7r96IvZpHdNipP2cBcb07J1Z45mz/VIS01OHJ3pY8T5fUY38tg4A== + dependencies: + "@jest/test-result" "^24.9.0" + jest-haste-map "^24.9.0" + jest-runner "^24.9.0" + jest-runtime "^24.9.0" + +"@jest/transform@^24.9.0": + version "24.9.0" + resolved "https://registry.yarnpkg.com/@jest/transform/-/transform-24.9.0.tgz#4ae2768b296553fadab09e9ec119543c90b16c56" + integrity sha512-TcQUmyNRxV94S0QpMOnZl0++6RMiqpbH/ZMccFB/amku6Uwvyb1cjYX7xkp5nGNkbX4QPH/FcB6q1HBTHynLmQ== + dependencies: + "@babel/core" "^7.1.0" + "@jest/types" "^24.9.0" + babel-plugin-istanbul "^5.1.0" + chalk "^2.0.1" + convert-source-map "^1.4.0" + fast-json-stable-stringify "^2.0.0" + graceful-fs "^4.1.15" + jest-haste-map "^24.9.0" + jest-regex-util "^24.9.0" + jest-util "^24.9.0" + micromatch "^3.1.10" + pirates "^4.0.1" + realpath-native "^1.1.0" + slash "^2.0.0" + source-map "^0.6.1" + write-file-atomic "2.4.1" + +"@jest/types@^24.9.0": + version "24.9.0" + resolved "https://registry.yarnpkg.com/@jest/types/-/types-24.9.0.tgz#63cb26cb7500d069e5a389441a7c6ab5e909fc59" + integrity sha512-XKK7ze1apu5JWQ5eZjHITP66AX+QsLlbaJRBGYr8pNzwcAE2JVkwnf0yqjHTsDRcjR0mujy/NmZMXw5kl+kGBw== + dependencies: + "@types/istanbul-lib-coverage" "^2.0.0" + "@types/istanbul-reports" "^1.1.1" + "@types/yargs" "^13.0.0" + +"@ledgerhq/devices@^4.78.0": + version "4.78.0" + resolved "https://registry.yarnpkg.com/@ledgerhq/devices/-/devices-4.78.0.tgz#149b572f0616096e2bd5eb14ce14d0061c432be6" + integrity sha512-tWKS5WM/UU82czihnVjRwz9SXNTQzWjGJ/7+j/xZ70O86nlnGJ1aaFbs5/WTzfrVKpOKgj1ZoZkAswX67i/JTw== + dependencies: + "@ledgerhq/errors" "^4.78.0" + "@ledgerhq/logs" "^4.72.0" + rxjs "^6.5.3" + +"@ledgerhq/devices@^5.13.1": + version "5.13.1" + resolved "https://registry.yarnpkg.com/@ledgerhq/devices/-/devices-5.13.1.tgz#1d65fcbcc470874968e2e74f7e87e3f350c4b332" + integrity sha512-E3zgmA51+esMkczM9xLddVz0myXTauJaT5g4bbwGxxeHQyHyvaJNyOhMmcuxZN/aa/lfRmxYGthcN2JBUPju5A== + dependencies: + "@ledgerhq/errors" "^5.13.1" + "@ledgerhq/logs" "^5.13.1" + rxjs "^6.5.5" + +"@ledgerhq/errors@^4.78.0": + version "4.78.0" + resolved "https://registry.yarnpkg.com/@ledgerhq/errors/-/errors-4.78.0.tgz#23daf3af54d03b1bda3e616002b555da1bdb705a" + integrity sha512-FX6zHZeiNtegBvXabK6M5dJ+8OV8kQGGaGtuXDeK/Ss5EmG4Ltxc6Lnhe8hiHpm9pCHtktOsnUVL7IFBdHhYUg== + +"@ledgerhq/errors@^5.13.1": + version "5.13.1" + resolved "https://registry.yarnpkg.com/@ledgerhq/errors/-/errors-5.13.1.tgz#1df73a8084609888fabcefbdb9edfd4c11ab3a22" + integrity sha512-IuEw9a70K3C3AZV4yVGk75HlwmKmJaR6EjMIxBAupiCw0G6rBP0d62MA1Vx4dg082LKKNXKafWcDstLG4ySpBA== + +"@ledgerhq/hw-transport-http@^5.13.1": + version "5.13.1" + resolved "https://registry.yarnpkg.com/@ledgerhq/hw-transport-http/-/hw-transport-http-5.13.1.tgz#f0220bc58b3905ba10b8355984cf082a66d637f5" + integrity sha512-mdsF2ZRAwEGZV/rsNnZeT6+T1bISu3jgYbxI91H9MzBdnaC1Q7UwN+b/d+MWFFHd3QpPX46k0EaBVjjcyUaNlw== + dependencies: + "@ledgerhq/errors" "^5.13.1" + "@ledgerhq/hw-transport" "^5.13.1" + "@ledgerhq/logs" "^5.13.1" + axios "^0.19.0" + ws "6" + +"@ledgerhq/hw-transport@^4.77.0": + version "4.78.0" + resolved "https://registry.yarnpkg.com/@ledgerhq/hw-transport/-/hw-transport-4.78.0.tgz#714786658e1f2fbc0569e06e2abf8d15d310d931" + integrity sha512-xQu16OMPQjFYLjqCysij+8sXtdWv2YLxPrB6FoLvEWGTlQ7yL1nUBRQyzyQtWIYqZd4THQowQmzm1VjxuN6SZw== + dependencies: + "@ledgerhq/devices" "^4.78.0" + "@ledgerhq/errors" "^4.78.0" + events "^3.0.0" + +"@ledgerhq/hw-transport@^5.13.1": + version "5.13.1" + resolved "https://registry.yarnpkg.com/@ledgerhq/hw-transport/-/hw-transport-5.13.1.tgz#b0fb8208b0fb383f3984c1af26a9b9e7a3ad3d75" + integrity sha512-TQJJY10ZoToYjaGK+u9wud0W2dK+6TBA753FGlHgptydSaRmep0uc4A2TxOLPexdVlbHXmkMa1skd3w4ZrKKdA== + dependencies: + "@ledgerhq/devices" "^5.13.1" + "@ledgerhq/errors" "^5.13.1" + events "^3.1.0" + +"@ledgerhq/logs@^4.72.0": + version "4.72.0" + resolved "https://registry.yarnpkg.com/@ledgerhq/logs/-/logs-4.72.0.tgz#43df23af013ad1135407e5cf33ca6e4c4c7708d5" + integrity sha512-o+TYF8vBcyySRsb2kqBDv/KMeme8a2nwWoG+lAWzbDmWfb2/MrVWYCVYDYvjXdSoI/Cujqy1i0gIDrkdxa9chA== + +"@ledgerhq/logs@^5.13.1": + version "5.13.1" + resolved "https://registry.yarnpkg.com/@ledgerhq/logs/-/logs-5.13.1.tgz#80a13031c9e0a9b874b96b6636f3f6df603ec1b2" + integrity sha512-ag2wX5VcAqPMKooCn/S6kKblVlsn74ixtagpwR+6GdFqYa/bspH8mWw+zwcdzSwa/wbOQRuY75zH52qBonfXBA== + +"@types/babel__core@^7.1.0": + version "7.1.7" + resolved "https://registry.yarnpkg.com/@types/babel__core/-/babel__core-7.1.7.tgz#1dacad8840364a57c98d0dd4855c6dd3752c6b89" + integrity sha512-RL62NqSFPCDK2FM1pSDH0scHpJvsXtZNiYlMB73DgPBaG1E38ZYVL+ei5EkWRbr+KC4YNiAUNBnRj+bgwpgjMw== + dependencies: + "@babel/parser" "^7.1.0" + "@babel/types" "^7.0.0" + "@types/babel__generator" "*" + "@types/babel__template" "*" + "@types/babel__traverse" "*" + +"@types/babel__generator@*": + version "7.6.1" + resolved "https://registry.yarnpkg.com/@types/babel__generator/-/babel__generator-7.6.1.tgz#4901767b397e8711aeb99df8d396d7ba7b7f0e04" + integrity sha512-bBKm+2VPJcMRVwNhxKu8W+5/zT7pwNEqeokFOmbvVSqGzFneNxYcEBro9Ac7/N9tlsaPYnZLK8J1LWKkMsLAew== + dependencies: + "@babel/types" "^7.0.0" + +"@types/babel__template@*": + version "7.0.2" + resolved "https://registry.yarnpkg.com/@types/babel__template/-/babel__template-7.0.2.tgz#4ff63d6b52eddac1de7b975a5223ed32ecea9307" + integrity sha512-/K6zCpeW7Imzgab2bLkLEbz0+1JlFSrUMdw7KoIIu+IUdu51GWaBZpd3y1VXGVXzynvGa4DaIaxNZHiON3GXUg== + dependencies: + "@babel/parser" "^7.1.0" + "@babel/types" "^7.0.0" + +"@types/babel__traverse@*", "@types/babel__traverse@^7.0.6": + version "7.0.11" + resolved "https://registry.yarnpkg.com/@types/babel__traverse/-/babel__traverse-7.0.11.tgz#1ae3010e8bf8851d324878b42acec71986486d18" + integrity sha512-ddHK5icION5U6q11+tV2f9Mo6CZVuT8GJKld2q9LqHSZbvLbH34Kcu2yFGckZut453+eQU6btIA3RihmnRgI+Q== + dependencies: + "@babel/types" "^7.3.0" + +"@types/color-name@^1.1.1": + version "1.1.1" + resolved "https://registry.yarnpkg.com/@types/color-name/-/color-name-1.1.1.tgz#1c1261bbeaa10a8055bbc5d8ab84b7b2afc846a0" + integrity sha512-rr+OQyAjxze7GgWrSaJwydHStIhHq2lvY3BOC2Mj7KnzI7XK0Uw1TOOdI9lDoajEbSWLiYgoo4f1R51erQfhPQ== + +"@types/istanbul-lib-coverage@*", "@types/istanbul-lib-coverage@^2.0.0": + version "2.0.1" + resolved "https://registry.yarnpkg.com/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.1.tgz#42995b446db9a48a11a07ec083499a860e9138ff" + integrity sha512-hRJD2ahnnpLgsj6KWMYSrmXkM3rm2Dl1qkx6IOFD5FnuNPXJIG5L0dhgKXCYTRMGzU4n0wImQ/xfmRc4POUFlg== + +"@types/istanbul-lib-report@*": + version "3.0.0" + resolved "https://registry.yarnpkg.com/@types/istanbul-lib-report/-/istanbul-lib-report-3.0.0.tgz#c14c24f18ea8190c118ee7562b7ff99a36552686" + integrity sha512-plGgXAPfVKFoYfa9NpYDAkseG+g6Jr294RqeqcqDixSbU34MZVJRi/P+7Y8GDpzkEwLaGZZOpKIEmeVZNtKsrg== + dependencies: + "@types/istanbul-lib-coverage" "*" + +"@types/istanbul-reports@^1.1.1": + version "1.1.1" + resolved "https://registry.yarnpkg.com/@types/istanbul-reports/-/istanbul-reports-1.1.1.tgz#7a8cbf6a406f36c8add871625b278eaf0b0d255a" + integrity sha512-UpYjBi8xefVChsCoBpKShdxTllC9pwISirfoZsUa2AAdQg/Jd2KQGtSbw+ya7GPo7x/wAPlH6JBhKhAsXUEZNA== + dependencies: + "@types/istanbul-lib-coverage" "*" + "@types/istanbul-lib-report" "*" + +"@types/json-schema@^7.0.3": + version "7.0.4" + resolved "https://registry.yarnpkg.com/@types/json-schema/-/json-schema-7.0.4.tgz#38fd73ddfd9b55abb1e1b2ed578cb55bd7b7d339" + integrity sha512-8+KAKzEvSUdeo+kmqnKrqgeE+LcA0tjYWFY7RPProVYwnqDjukzO+3b6dLD56rYX5TdWejnEOLJYOIeh4CXKuA== + +"@types/stack-utils@^1.0.1": + version "1.0.1" + resolved "https://registry.yarnpkg.com/@types/stack-utils/-/stack-utils-1.0.1.tgz#0a851d3bd96498fa25c33ab7278ed3bd65f06c3e" + integrity sha512-l42BggppR6zLmpfU6fq9HEa2oGPEI8yrSPL3GITjfRInppYFahObbIQOQK3UGxEnyQpltZLaPe75046NOZQikw== + +"@types/yargs-parser@*": + version "15.0.0" + resolved "https://registry.yarnpkg.com/@types/yargs-parser/-/yargs-parser-15.0.0.tgz#cb3f9f741869e20cce330ffbeb9271590483882d" + integrity sha512-FA/BWv8t8ZWJ+gEOnLLd8ygxH/2UFbAvgEonyfN6yWGLKc7zVjbpl2Y4CTjid9h2RfgPP6SEt6uHwEOply00yw== + +"@types/yargs@^13.0.0": + version "13.0.8" + resolved "https://registry.yarnpkg.com/@types/yargs/-/yargs-13.0.8.tgz#a38c22def2f1c2068f8971acb3ea734eb3c64a99" + integrity sha512-XAvHLwG7UQ+8M4caKIH0ZozIOYay5fQkAgyIXegXT9jPtdIGdhga+sUEdAr1CiG46aB+c64xQEYyEzlwWVTNzA== + dependencies: + "@types/yargs-parser" "*" + +"@typescript-eslint/experimental-utils@^2.5.0": + version "2.30.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/experimental-utils/-/experimental-utils-2.30.0.tgz#9845e868c01f3aed66472c561d4b6bac44809dd0" + integrity sha512-L3/tS9t+hAHksy8xuorhOzhdefN0ERPDWmR9CclsIGOUqGKy6tqc/P+SoXeJRye5gazkuPO0cK9MQRnolykzkA== + dependencies: + "@types/json-schema" "^7.0.3" + "@typescript-eslint/typescript-estree" "2.30.0" + eslint-scope "^5.0.0" + eslint-utils "^2.0.0" + +"@typescript-eslint/typescript-estree@2.30.0": + version "2.30.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/typescript-estree/-/typescript-estree-2.30.0.tgz#1b8e848b55144270255ffbfe4c63291f8f766615" + integrity sha512-nI5WOechrA0qAhnr+DzqwmqHsx7Ulr/+0H7bWCcClDhhWkSyZR5BmTvnBEyONwJCTWHfc5PAQExX24VD26IAVw== + dependencies: + debug "^4.1.1" + eslint-visitor-keys "^1.1.0" + glob "^7.1.6" + is-glob "^4.0.1" + lodash "^4.17.15" + semver "^6.3.0" + tsutils "^3.17.1" + +"@zondax/zemu@^0.2.4": + version "0.2.4" + resolved "https://registry.yarnpkg.com/@zondax/zemu/-/zemu-0.2.4.tgz#117f7d44ded3c764b918922769fd7f299d1c98ae" + integrity sha512-7E2gOJRLaMbm2CLVltu4DB+9F3CyfHSXezIr4oRZGAyUwonaNpGX6GdanUmzTRNyQ97pdk+CHu62F6gD4q4pvw== + dependencies: + "@babel/runtime" "^7.9.2" + "@ledgerhq/hw-transport" "^5.13.1" + "@ledgerhq/hw-transport-http" "^5.13.1" + dockerode "^3.2.0" + path "^0.12.7" + pngjs "^5.0.0" + randomstring "^1.1.5" + rfb2 "^0.2.2" + sleep "^6.1.0" + +abab@^2.0.0: + version "2.0.3" + resolved "https://registry.yarnpkg.com/abab/-/abab-2.0.3.tgz#623e2075e02eb2d3f2475e49f99c91846467907a" + integrity sha512-tsFzPpcttalNjFBCFMqsKYQcWxxen1pgJR56by//QwvJc4/OUS3kPOOttx2tSIfjsylB0pYu7f5D3K1RCxUnUg== + +acorn-globals@^4.1.0: + version "4.3.4" + resolved "https://registry.yarnpkg.com/acorn-globals/-/acorn-globals-4.3.4.tgz#9fa1926addc11c97308c4e66d7add0d40c3272e7" + integrity sha512-clfQEh21R+D0leSbUdWf3OcfqyaCSAQ8Ryq00bofSekfr9W8u1jyYZo6ir0xu9Gtcf7BjcHJpnbZH7JOCpP60A== + dependencies: + acorn "^6.0.1" + acorn-walk "^6.0.1" + +acorn-jsx@^5.2.0: + version "5.2.0" + resolved "https://registry.yarnpkg.com/acorn-jsx/-/acorn-jsx-5.2.0.tgz#4c66069173d6fdd68ed85239fc256226182b2ebe" + integrity sha512-HiUX/+K2YpkpJ+SzBffkM/AQ2YE03S0U1kjTLVpoJdhZMOWy8qvXVN9JdLqv2QsaQ6MPYQIuNmwD8zOiYUofLQ== + +acorn-walk@^6.0.1: + version "6.2.0" + resolved "https://registry.yarnpkg.com/acorn-walk/-/acorn-walk-6.2.0.tgz#123cb8f3b84c2171f1f7fb252615b1c78a6b1a8c" + integrity sha512-7evsyfH1cLOCdAzZAd43Cic04yKydNx0cF+7tiA19p1XnLLPU4dpCQOqpjqwokFe//vS0QqfqqjCS2JkiIs0cA== + +acorn@^5.5.3: + version "5.7.4" + resolved "https://registry.yarnpkg.com/acorn/-/acorn-5.7.4.tgz#3e8d8a9947d0599a1796d10225d7432f4a4acf5e" + integrity sha512-1D++VG7BhrtvQpNbBzovKNc1FLGGEE/oGe7b9xJm/RFHMBeUaUGpluV9RLjZa47YFdPcDAenEYuq9pQPcMdLJg== + +acorn@^6.0.1: + version "6.4.1" + resolved "https://registry.yarnpkg.com/acorn/-/acorn-6.4.1.tgz#531e58ba3f51b9dacb9a6646ca4debf5b14ca474" + integrity sha512-ZVA9k326Nwrj3Cj9jlh3wGFutC2ZornPNARZwsNYqQYgN0EsV2d53w5RN/co65Ohn4sUAUtb1rSUAOD6XN9idA== + +acorn@^7.1.1: + version "7.1.1" + resolved "https://registry.yarnpkg.com/acorn/-/acorn-7.1.1.tgz#e35668de0b402f359de515c5482a1ab9f89a69bf" + integrity sha512-add7dgA5ppRPxCFJoAGfMDi7PIBXq1RtGo7BhbLaxwrXPOmw8gq48Y9ozT01hUKy9byMjlR20EJhu5zlkErEkg== + +ajv@^6.10.0, ajv@^6.10.2, ajv@^6.5.5: + version "6.12.2" + resolved "https://registry.yarnpkg.com/ajv/-/ajv-6.12.2.tgz#c629c5eced17baf314437918d2da88c99d5958cd" + integrity sha512-k+V+hzjm5q/Mr8ef/1Y9goCmlsK4I6Sm74teeyGvFk1XrOsbsKLjEdrvny42CZ+a8sXbk8KWpY/bDwS+FLL2UQ== + dependencies: + fast-deep-equal "^3.1.1" + fast-json-stable-stringify "^2.0.0" + json-schema-traverse "^0.4.1" + uri-js "^4.2.2" + +ansi-escapes@^3.0.0: + version "3.2.0" + resolved "https://registry.yarnpkg.com/ansi-escapes/-/ansi-escapes-3.2.0.tgz#8780b98ff9dbf5638152d1f1fe5c1d7b4442976b" + integrity sha512-cBhpre4ma+U0T1oM5fXg7Dy1Jw7zzwv7lt/GoCpr+hDQJoYnKVPLL4dCvSEFMmQurOQvSrwT7SL/DAlhBI97RQ== + +ansi-escapes@^4.2.1: + version "4.3.1" + resolved "https://registry.yarnpkg.com/ansi-escapes/-/ansi-escapes-4.3.1.tgz#a5c47cc43181f1f38ffd7076837700d395522a61" + integrity sha512-JWF7ocqNrp8u9oqpgV+wH5ftbt+cfvv+PTjOvKLT3AdYly/LmORARfEVT1iyjwN+4MqE5UmVKoAdIBqeoCHgLA== + dependencies: + type-fest "^0.11.0" + +ansi-regex@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-3.0.0.tgz#ed0317c322064f79466c02966bddb605ab37d998" + integrity sha1-7QMXwyIGT3lGbAKWa922Bas32Zg= + +ansi-regex@^4.0.0, ansi-regex@^4.1.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-4.1.0.tgz#8b9f8f08cf1acb843756a839ca8c7e3168c51997" + integrity sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg== + +ansi-regex@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-5.0.0.tgz#388539f55179bf39339c81af30a654d69f87cb75" + integrity sha512-bY6fj56OUQ0hU1KjFNDQuJFezqKdrAyFdIevADiqrWHwSlbmBNMHp5ak2f40Pm8JTFyM2mqxkG6ngkHO11f/lg== + +ansi-styles@^3.2.0, ansi-styles@^3.2.1: + version "3.2.1" + resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-3.2.1.tgz#41fbb20243e50b12be0f04b8dedbf07520ce841d" + integrity sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA== + dependencies: + color-convert "^1.9.0" + +ansi-styles@^4.1.0: + version "4.2.1" + resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-4.2.1.tgz#90ae75c424d008d2624c5bf29ead3177ebfcf359" + integrity sha512-9VGjrMsG1vePxcSweQsN20KY/c4zN0h9fLjqAbwbPfahM3t+NL+M9HC8xeXG2I8pX5NoamTGNuomEUFI7fcUjA== + dependencies: + "@types/color-name" "^1.1.1" + color-convert "^2.0.1" + +anymatch@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/anymatch/-/anymatch-2.0.0.tgz#bcb24b4f37934d9aa7ac17b4adaf89e7c76ef2eb" + integrity sha512-5teOsQWABXHHBFP9y3skS5P3d/WfWXpv3FUpy+LorMrNYaT9pI4oLMQX7jzQ2KklNpGpWHzdCXTDT2Y3XGlZBw== + dependencies: + micromatch "^3.1.4" + normalize-path "^2.1.1" + +argparse@^1.0.7: + version "1.0.10" + resolved "https://registry.yarnpkg.com/argparse/-/argparse-1.0.10.tgz#bcd6791ea5ae09725e17e5ad988134cd40b3d911" + integrity sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg== + dependencies: + sprintf-js "~1.0.2" + +arr-diff@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/arr-diff/-/arr-diff-4.0.0.tgz#d6461074febfec71e7e15235761a329a5dc7c520" + integrity sha1-1kYQdP6/7HHn4VI1dhoyml3HxSA= + +arr-flatten@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/arr-flatten/-/arr-flatten-1.1.0.tgz#36048bbff4e7b47e136644316c99669ea5ae91f1" + integrity sha512-L3hKV5R/p5o81R7O02IGnwpDmkp6E982XhtbuwSe3O4qOtMMMtodicASA1Cny2U+aCXcNpml+m4dPsvsJ3jatg== + +arr-union@^3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/arr-union/-/arr-union-3.1.0.tgz#e39b09aea9def866a8f206e288af63919bae39c4" + integrity sha1-45sJrqne+Gao8gbiiK9jkZuuOcQ= + +array-equal@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/array-equal/-/array-equal-1.0.0.tgz#8c2a5ef2472fd9ea742b04c77a75093ba2757c93" + integrity sha1-jCpe8kcv2ep0KwTHenUJO6J1fJM= + +array-includes@^3.0.3: + version "3.1.1" + resolved "https://registry.yarnpkg.com/array-includes/-/array-includes-3.1.1.tgz#cdd67e6852bdf9c1215460786732255ed2459348" + integrity sha512-c2VXaCHl7zPsvpkFsw4nxvFie4fh1ur9bpcgsVkIjqn0H/Xwdg+7fv3n2r/isyS8EBj5b06M9kHyZuIr4El6WQ== + dependencies: + define-properties "^1.1.3" + es-abstract "^1.17.0" + is-string "^1.0.5" + +array-uniq@1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/array-uniq/-/array-uniq-1.0.2.tgz#5fcc373920775723cfd64d65c64bef53bf9eba6d" + integrity sha1-X8w3OSB3VyPP1k1lxkvvU7+eum0= + +array-unique@^0.3.2: + version "0.3.2" + resolved "https://registry.yarnpkg.com/array-unique/-/array-unique-0.3.2.tgz#a894b75d4bc4f6cd679ef3244a9fd8f46ae2d428" + integrity sha1-qJS3XUvE9s1nnvMkSp/Y9Gri1Cg= + +array.prototype.flat@^1.2.1: + version "1.2.3" + resolved "https://registry.yarnpkg.com/array.prototype.flat/-/array.prototype.flat-1.2.3.tgz#0de82b426b0318dbfdb940089e38b043d37f6c7b" + integrity sha512-gBlRZV0VSmfPIeWfuuy56XZMvbVfbEUnOXUvt3F/eUUUSyzlgLxhEX4YAEpxNAogRGehPSnfXyPtYyKAhkzQhQ== + dependencies: + define-properties "^1.1.3" + es-abstract "^1.17.0-next.1" + +asn1@~0.2.0, asn1@~0.2.3: + version "0.2.4" + resolved "https://registry.yarnpkg.com/asn1/-/asn1-0.2.4.tgz#8d2475dfab553bb33e77b54e59e880bb8ce23136" + integrity sha512-jxwzQpLQjSmWXgwaCZE9Nz+glAG01yF1QnWgbhGwHI5A6FRIEY6IVqtHhIepHqI7/kyEyQEagBC5mBEFlIYvdg== + dependencies: + safer-buffer "~2.1.0" + +assert-plus@1.0.0, assert-plus@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/assert-plus/-/assert-plus-1.0.0.tgz#f12e0f3c5d77b0b1cdd9146942e4e96c1e4dd525" + integrity sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU= + +assign-symbols@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/assign-symbols/-/assign-symbols-1.0.0.tgz#59667f41fadd4f20ccbc2bb96b8d4f7f78ec0367" + integrity sha1-WWZ/QfrdTyDMvCu5a41Pf3jsA2c= + +astral-regex@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/astral-regex/-/astral-regex-1.0.0.tgz#6c8c3fb827dd43ee3918f27b82782ab7658a6fd9" + integrity sha512-+Ryf6g3BKoRc7jfp7ad8tM4TtMiaWvbF/1/sQcZPkkS7ag3D5nMBCe2UfOTONtAkaG0tO0ij3C5Lwmf1EiyjHg== + +async-each@^1.0.1: + version "1.0.3" + resolved "https://registry.yarnpkg.com/async-each/-/async-each-1.0.3.tgz#b727dbf87d7651602f06f4d4ac387f47d91b0cbf" + integrity sha512-z/WhQ5FPySLdvREByI2vZiTWwCnF0moMJ1hK9YQwDTHKh6I7/uSckMetoRGb5UBZPC1z0jlw+n/XCgjeH7y1AQ== + +async-limiter@~1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/async-limiter/-/async-limiter-1.0.1.tgz#dd379e94f0db8310b08291f9d64c3209766617fd" + integrity sha512-csOlWGAcRFJaI6m+F2WKdnMKr4HhdhFVBk0H/QbJFMCr+uO2kwohwXQPxw/9OCxp05r5ghVBFSyioixx3gfkNQ== + +asynckit@^0.4.0: + version "0.4.0" + resolved "https://registry.yarnpkg.com/asynckit/-/asynckit-0.4.0.tgz#c79ed97f7f34cb8f2ba1bc9790bcc366474b4b79" + integrity sha1-x57Zf380y48robyXkLzDZkdLS3k= + +atob@^2.1.2: + version "2.1.2" + resolved "https://registry.yarnpkg.com/atob/-/atob-2.1.2.tgz#6d9517eb9e030d2436666651e86bd9f6f13533c9" + integrity sha512-Wm6ukoaOGJi/73p/cl2GvLjTI5JM1k/O14isD73YML8StrH/7/lRFgmg8nICZgD3bZZvjwCGxtMOD3wWNAu8cg== + +aws-sign2@~0.7.0: + version "0.7.0" + resolved "https://registry.yarnpkg.com/aws-sign2/-/aws-sign2-0.7.0.tgz#b46e890934a9591f2d2f6f86d7e6a9f1b3fe76a8" + integrity sha1-tG6JCTSpWR8tL2+G1+ap8bP+dqg= + +aws4@^1.8.0: + version "1.9.1" + resolved "https://registry.yarnpkg.com/aws4/-/aws4-1.9.1.tgz#7e33d8f7d449b3f673cd72deb9abdc552dbe528e" + integrity sha512-wMHVg2EOHaMRxbzgFJ9gtjOOCrI80OHLG14rxi28XwOW8ux6IiEbRCGGGqCtdAIg4FQCbW20k9RsT4y3gJlFug== + +axios@^0.19.0: + version "0.19.2" + resolved "https://registry.yarnpkg.com/axios/-/axios-0.19.2.tgz#3ea36c5d8818d0d5f8a8a97a6d36b86cdc00cb27" + integrity sha512-fjgm5MvRHLhx+osE2xoekY70AhARk3a6hkN+3Io1jc00jtquGvxYlKlsFUhmUET0V5te6CcZI7lcv2Ym61mjHA== + dependencies: + follow-redirects "1.5.10" + +babel-eslint@^10.1.0: + version "10.1.0" + resolved "https://registry.yarnpkg.com/babel-eslint/-/babel-eslint-10.1.0.tgz#6968e568a910b78fb3779cdd8b6ac2f479943232" + integrity sha512-ifWaTHQ0ce+448CYop8AdrQiBsGrnC+bMgfyKFdi6EsPLTAWG+QfyDeM6OH+FmWnKvEq5NnBMLvlBUPKQZoDSg== + dependencies: + "@babel/code-frame" "^7.0.0" + "@babel/parser" "^7.7.0" + "@babel/traverse" "^7.7.0" + "@babel/types" "^7.7.0" + eslint-visitor-keys "^1.0.0" + resolve "^1.12.0" + +babel-jest@24, babel-jest@^24.9.0: + version "24.9.0" + resolved "https://registry.yarnpkg.com/babel-jest/-/babel-jest-24.9.0.tgz#3fc327cb8467b89d14d7bc70e315104a783ccd54" + integrity sha512-ntuddfyiN+EhMw58PTNL1ph4C9rECiQXjI4nMMBKBaNjXvqLdkXpPRcMSr4iyBrJg/+wz9brFUD6RhOAT6r4Iw== + dependencies: + "@jest/transform" "^24.9.0" + "@jest/types" "^24.9.0" + "@types/babel__core" "^7.1.0" + babel-plugin-istanbul "^5.1.0" + babel-preset-jest "^24.9.0" + chalk "^2.4.2" + slash "^2.0.0" + +babel-plugin-dynamic-import-node@^2.3.3: + version "2.3.3" + resolved "https://registry.yarnpkg.com/babel-plugin-dynamic-import-node/-/babel-plugin-dynamic-import-node-2.3.3.tgz#84fda19c976ec5c6defef57f9427b3def66e17a3" + integrity sha512-jZVI+s9Zg3IqA/kdi0i6UDCybUI3aSBLnglhYbSSjKlV7yF1F/5LWv8MakQmvYpnbJDS6fcBL2KzHSxNCMtWSQ== + dependencies: + object.assign "^4.1.0" + +babel-plugin-istanbul@^5.1.0: + version "5.2.0" + resolved "https://registry.yarnpkg.com/babel-plugin-istanbul/-/babel-plugin-istanbul-5.2.0.tgz#df4ade83d897a92df069c4d9a25cf2671293c854" + integrity sha512-5LphC0USA8t4i1zCtjbbNb6jJj/9+X6P37Qfirc/70EQ34xKlMW+a1RHGwxGI+SwWpNwZ27HqvzAobeqaXwiZw== + dependencies: + "@babel/helper-plugin-utils" "^7.0.0" + find-up "^3.0.0" + istanbul-lib-instrument "^3.3.0" + test-exclude "^5.2.3" + +babel-plugin-jest-hoist@^24.9.0: + version "24.9.0" + resolved "https://registry.yarnpkg.com/babel-plugin-jest-hoist/-/babel-plugin-jest-hoist-24.9.0.tgz#4f837091eb407e01447c8843cbec546d0002d756" + integrity sha512-2EMA2P8Vp7lG0RAzr4HXqtYwacfMErOuv1U3wrvxHX6rD1sV6xS3WXG3r8TRQ2r6w8OhvSdWt+z41hQNwNm3Xw== + dependencies: + "@types/babel__traverse" "^7.0.6" + +babel-preset-jest@^24.9.0: + version "24.9.0" + resolved "https://registry.yarnpkg.com/babel-preset-jest/-/babel-preset-jest-24.9.0.tgz#192b521e2217fb1d1f67cf73f70c336650ad3cdc" + integrity sha512-izTUuhE4TMfTRPF92fFwD2QfdXaZW08qvWTFCI51V8rW5x00UuPgc3ajRoWofXOuxjfcOM5zzSYsQS3H8KGCAg== + dependencies: + "@babel/plugin-syntax-object-rest-spread" "^7.0.0" + babel-plugin-jest-hoist "^24.9.0" + +balanced-match@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.0.tgz#89b4d199ab2bee49de164ea02b89ce462d71b767" + integrity sha1-ibTRmasr7kneFk6gK4nORi1xt2c= + +base64-js@^1.0.2: + version "1.3.1" + resolved "https://registry.yarnpkg.com/base64-js/-/base64-js-1.3.1.tgz#58ece8cb75dd07e71ed08c736abc5fac4dbf8df1" + integrity sha512-mLQ4i2QO1ytvGWFWmcngKO//JXAQueZvwEKtjgQFM4jIK0kU+ytMfplL8j+n5mspOfjHwoAg+9yhb7BwAHm36g== + +base@^0.11.1: + version "0.11.2" + resolved "https://registry.yarnpkg.com/base/-/base-0.11.2.tgz#7bde5ced145b6d551a90db87f83c558b4eb48a8f" + integrity sha512-5T6P4xPgpp0YDFvSWwEZ4NoE3aM4QBQXDzmVbraCkFj8zHM+mba8SyqB5DbZWyR7mYHo6Y7BdQo3MoA4m0TeQg== + dependencies: + cache-base "^1.0.1" + class-utils "^0.3.5" + component-emitter "^1.2.1" + define-property "^1.0.0" + isobject "^3.0.1" + mixin-deep "^1.2.0" + pascalcase "^0.1.1" + +bcrypt-pbkdf@^1.0.0, bcrypt-pbkdf@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.2.tgz#a4301d389b6a43f9b67ff3ca11a3f6637e360e9e" + integrity sha1-pDAdOJtqQ/m2f/PKEaP2Y342Dp4= + dependencies: + tweetnacl "^0.14.3" + +bech32@^1.1.3: + version "1.1.4" + resolved "https://registry.yarnpkg.com/bech32/-/bech32-1.1.4.tgz#e38c9f37bf179b8eb16ae3a772b40c356d4832e9" + integrity sha512-s0IrSOzLlbvX7yp4WBfPITzpAU8sqQcpsmwXDiKwrG4r491vwCO/XpejasRNl0piBMe/DvP4Tz0mIS/X1DPJBQ== + +binary-extensions@^1.0.0: + version "1.13.1" + resolved "https://registry.yarnpkg.com/binary-extensions/-/binary-extensions-1.13.1.tgz#598afe54755b2868a5330d2aff9d4ebb53209b65" + integrity sha512-Un7MIEDdUC5gNpcGDV97op1Ywk748MpHcFTHoYs6qnj1Z3j7I53VG3nwZhKzoBZmbdRNnb6WRdFlwl7tSDuZGw== + +bindings@^1.5.0: + version "1.5.0" + resolved "https://registry.yarnpkg.com/bindings/-/bindings-1.5.0.tgz#10353c9e945334bc0511a6d90b38fbc7c9c504df" + integrity sha512-p2q/t/mhvuOj/UeLlV6566GD/guowlr0hHxClI0W9m7MWYkL1F0hLo+0Aexs9HSPCtR1SXQ0TD3MMKrXZajbiQ== + dependencies: + file-uri-to-path "1.0.0" + +bl@^4.0.1: + version "4.0.2" + resolved "https://registry.yarnpkg.com/bl/-/bl-4.0.2.tgz#52b71e9088515d0606d9dd9cc7aa48dc1f98e73a" + integrity sha512-j4OH8f6Qg2bGuWfRiltT2HYGx0e1QcBTrK9KAHNMwMZdQnDZFk0ZSYIpADjYCB3U12nicC5tVJwSIhwOWjb4RQ== + dependencies: + buffer "^5.5.0" + inherits "^2.0.4" + readable-stream "^3.4.0" + +bn.js@^4.4.0: + version "4.11.8" + resolved "https://registry.yarnpkg.com/bn.js/-/bn.js-4.11.8.tgz#2cde09eb5ee341f484746bb0309b3253b1b1442f" + integrity sha512-ItfYfPLkWHUjckQCk8xC+LwxgK8NYcXywGigJgSwOP8Y2iyWT4f2vsZnoOXTTbo+o5yXmIUJ4gn5538SO5S3gA== + +brace-expansion@^1.1.7: + version "1.1.11" + resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-1.1.11.tgz#3c7fcbf529d87226f3d2f52b966ff5271eb441dd" + integrity sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA== + dependencies: + balanced-match "^1.0.0" + concat-map "0.0.1" + +braces@^2.3.1, braces@^2.3.2: + version "2.3.2" + resolved "https://registry.yarnpkg.com/braces/-/braces-2.3.2.tgz#5979fd3f14cd531565e5fa2df1abfff1dfaee729" + integrity sha512-aNdbnj9P8PjdXU4ybaWLK2IF3jc/EoDYbC7AazW6to3TRsfXxscC9UXOB5iDiEQrkyIbWp2SLQda4+QAa7nc3w== + dependencies: + arr-flatten "^1.1.0" + array-unique "^0.3.2" + extend-shallow "^2.0.1" + fill-range "^4.0.0" + isobject "^3.0.1" + repeat-element "^1.1.2" + snapdragon "^0.8.1" + snapdragon-node "^2.0.1" + split-string "^3.0.2" + to-regex "^3.0.1" + +brorand@^1.0.1: + version "1.1.0" + resolved "https://registry.yarnpkg.com/brorand/-/brorand-1.1.0.tgz#12c25efe40a45e3c323eb8675a0a0ce57b22371f" + integrity sha1-EsJe/kCkXjwyPrhnWgoM5XsiNx8= + +browser-process-hrtime@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/browser-process-hrtime/-/browser-process-hrtime-1.0.0.tgz#3c9b4b7d782c8121e56f10106d84c0d0ffc94626" + integrity sha512-9o5UecI3GhkpM6DrXr69PblIuWxPKk9Y0jHBRhdocZ2y7YECBFCsHm79Pr3OyR2AvjhDkabFJaDJMYRazHgsow== + +browser-resolve@^1.11.3: + version "1.11.3" + resolved "https://registry.yarnpkg.com/browser-resolve/-/browser-resolve-1.11.3.tgz#9b7cbb3d0f510e4cb86bdbd796124d28b5890af6" + integrity sha512-exDi1BYWB/6raKHmDTCicQfTkqwN5fioMFV4j8BsfMU4R2DK/QfZfK7kOVkmWCNANf0snkBzqGqAJBao9gZMdQ== + dependencies: + resolve "1.1.7" + +browserslist@^4.11.1, browserslist@^4.8.5: + version "4.12.0" + resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-4.12.0.tgz#06c6d5715a1ede6c51fc39ff67fd647f740b656d" + integrity sha512-UH2GkcEDSI0k/lRkuDSzFl9ZZ87skSy9w2XAn1MsZnL+4c4rqbBd3e82UWHbYDpztABrPBhZsTEeuxVfHppqDg== + dependencies: + caniuse-lite "^1.0.30001043" + electron-to-chromium "^1.3.413" + node-releases "^1.1.53" + pkg-up "^2.0.0" + +bser@2.1.1: + version "2.1.1" + resolved "https://registry.yarnpkg.com/bser/-/bser-2.1.1.tgz#e6787da20ece9d07998533cfd9de6f5c38f4bc05" + integrity sha512-gQxTNE/GAfIIrmHLUE3oJyp5FO6HRBfhjnw4/wMmA63ZGDJnWBmgY/lyQBpnDUkGmAhbSe39tx2d/iTOAfglwQ== + dependencies: + node-int64 "^0.4.0" + +buffer-from@^1.0.0: + version "1.1.1" + resolved "https://registry.yarnpkg.com/buffer-from/-/buffer-from-1.1.1.tgz#32713bc028f75c02fdb710d7c7bcec1f2c6070ef" + integrity sha512-MQcXEUbCKtEo7bhqEs6560Hyd4XaovZlO/k9V3hjVUF/zwW7KBVdSK4gIt/bzwS9MbR5qob+F5jusZsb0YQK2A== + +buffer@^5.5.0: + version "5.6.0" + resolved "https://registry.yarnpkg.com/buffer/-/buffer-5.6.0.tgz#a31749dc7d81d84db08abf937b6b8c4033f62786" + integrity sha512-/gDYp/UtU0eA1ys8bOs9J6a+E/KWIY+DZ+Q2WESNUA0jFRsJOc0SNUO6xJ5SGA1xueg3NL65W6s+NY5l9cunuw== + dependencies: + base64-js "^1.0.2" + ieee754 "^1.1.4" + +cache-base@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/cache-base/-/cache-base-1.0.1.tgz#0a7f46416831c8b662ee36fe4e7c59d76f666ab2" + integrity sha512-AKcdTnFSWATd5/GCPRxr2ChwIJ85CeyrEyjRHlKxQ56d4XJMGym0uAiKn0xbLOGOl3+yRpOTi484dVCEc5AUzQ== + dependencies: + collection-visit "^1.0.0" + component-emitter "^1.2.1" + get-value "^2.0.6" + has-value "^1.0.0" + isobject "^3.0.1" + set-value "^2.0.0" + to-object-path "^0.3.0" + union-value "^1.0.0" + unset-value "^1.0.0" + +callsites@^3.0.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/callsites/-/callsites-3.1.0.tgz#b3630abd8943432f54b3f0519238e33cd7df2f73" + integrity sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ== + +camelcase@^5.0.0, camelcase@^5.3.1: + version "5.3.1" + resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-5.3.1.tgz#e3c9b31569e106811df242f715725a1f4c494320" + integrity sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg== + +caniuse-lite@^1.0.30001043: + version "1.0.30001048" + resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001048.tgz#4bb4f1bc2eb304e5e1154da80b93dee3f1cf447e" + integrity sha512-g1iSHKVxornw0K8LG9LLdf+Fxnv7T1Z+mMsf0/YYLclQX4Cd522Ap0Lrw6NFqHgezit78dtyWxzlV2Xfc7vgRg== + +capture-exit@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/capture-exit/-/capture-exit-2.0.0.tgz#fb953bfaebeb781f62898239dabb426d08a509a4" + integrity sha512-PiT/hQmTonHhl/HFGN+Lx3JJUznrVYJ3+AQsnthneZbvW7x+f08Tk7yLJTLEOUvBTbduLeeBkxEaYXUOUrRq6g== + dependencies: + rsvp "^4.8.4" + +caseless@~0.12.0: + version "0.12.0" + resolved "https://registry.yarnpkg.com/caseless/-/caseless-0.12.0.tgz#1b681c21ff84033c826543090689420d187151dc" + integrity sha1-G2gcIf+EAzyCZUMJBolCDRhxUdw= + +chalk@^2.0.0, chalk@^2.0.1, chalk@^2.1.0, chalk@^2.4.2: + version "2.4.2" + resolved "https://registry.yarnpkg.com/chalk/-/chalk-2.4.2.tgz#cd42541677a54333cf541a49108c1432b44c9424" + integrity sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ== + dependencies: + ansi-styles "^3.2.1" + escape-string-regexp "^1.0.5" + supports-color "^5.3.0" + +chalk@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/chalk/-/chalk-3.0.0.tgz#3f73c2bf526591f574cc492c51e2456349f844e4" + integrity sha512-4D3B6Wf41KOYRFdszmDqMCGq5VV/uMAB273JILmO+3jAlh8X4qDtdtgCR3fxtbLEMzSx22QdhnDcJvu2u1fVwg== + dependencies: + ansi-styles "^4.1.0" + supports-color "^7.1.0" + +chardet@^0.7.0: + version "0.7.0" + resolved "https://registry.yarnpkg.com/chardet/-/chardet-0.7.0.tgz#90094849f0937f2eedc2425d0d28a9e5f0cbad9e" + integrity sha512-mT8iDcrh03qDGRRmoA2hmBJnxpllMR+0/0qlzjqZES6NdiWDcZkCNAk4rPFZ9Q85r27unkiNNg8ZOiwZXBHwcA== + +chokidar@^2.1.8: + version "2.1.8" + resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-2.1.8.tgz#804b3a7b6a99358c3c5c61e71d8728f041cff917" + integrity sha512-ZmZUazfOzf0Nve7duiCKD23PFSCs4JPoYyccjUFF3aQkQadqBhfzhjkwBH2mNOG9cTBwhamM37EIsIkZw3nRgg== + dependencies: + anymatch "^2.0.0" + async-each "^1.0.1" + braces "^2.3.2" + glob-parent "^3.1.0" + inherits "^2.0.3" + is-binary-path "^1.0.0" + is-glob "^4.0.0" + normalize-path "^3.0.0" + path-is-absolute "^1.0.0" + readdirp "^2.2.1" + upath "^1.1.1" + optionalDependencies: + fsevents "^1.2.7" + +chownr@^1.1.1: + version "1.1.4" + resolved "https://registry.yarnpkg.com/chownr/-/chownr-1.1.4.tgz#6fc9d7b42d32a583596337666e7d08084da2cc6b" + integrity sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg== + +ci-info@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/ci-info/-/ci-info-2.0.0.tgz#67a9e964be31a51e15e5010d58e6f12834002f46" + integrity sha512-5tK7EtrZ0N+OLFMthtqOj4fI2Jeb88C4CAZPu25LDVUgXJ0A3Js4PMGqrn0JU1W0Mh1/Z8wZzYPxqUrXeBboCQ== + +class-utils@^0.3.5: + version "0.3.6" + resolved "https://registry.yarnpkg.com/class-utils/-/class-utils-0.3.6.tgz#f93369ae8b9a7ce02fd41faad0ca83033190c463" + integrity sha512-qOhPa/Fj7s6TY8H8esGu5QNpMMQxz79h+urzrNYN6mn+9BnxlDGf5QZ+XeCDsxSjPqsSR56XOZOJmpeurnLMeg== + dependencies: + arr-union "^3.1.0" + define-property "^0.2.5" + isobject "^3.0.0" + static-extend "^0.1.1" + +cli-cursor@^3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/cli-cursor/-/cli-cursor-3.1.0.tgz#264305a7ae490d1d03bf0c9ba7c925d1753af307" + integrity sha512-I/zHAwsKf9FqGoXM4WWRACob9+SNukZTd94DWF57E4toouRulbCxcUh6RKUEOQlYTHJnzkPMySvPNaaSLNfLZw== + dependencies: + restore-cursor "^3.1.0" + +cli-width@^2.0.0: + version "2.2.1" + resolved "https://registry.yarnpkg.com/cli-width/-/cli-width-2.2.1.tgz#b0433d0b4e9c847ef18868a4ef16fd5fc8271c48" + integrity sha512-GRMWDxpOB6Dgk2E5Uo+3eEBvtOOlimMmpbFiKuLFnQzYDavtLFY3K5ona41jgN/WdRZtG7utuVSVTL4HbZHGkw== + +cliui@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/cliui/-/cliui-5.0.0.tgz#deefcfdb2e800784aa34f46fa08e06851c7bbbc5" + integrity sha512-PYeGSEmmHM6zvoef2w8TPzlrnNpXIjTipYK780YswmIP9vjxmd6Y2a3CB2Ks6/AU8NHjZugXvo8w3oWM2qnwXA== + dependencies: + string-width "^3.1.0" + strip-ansi "^5.2.0" + wrap-ansi "^5.1.0" + +co@^4.6.0: + version "4.6.0" + resolved "https://registry.yarnpkg.com/co/-/co-4.6.0.tgz#6ea6bdf3d853ae54ccb8e47bfa0bf3f9031fb184" + integrity sha1-bqa989hTrlTMuOR7+gvz+QMfsYQ= + +collection-visit@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/collection-visit/-/collection-visit-1.0.0.tgz#4bc0373c164bc3291b4d368c829cf1a80a59dca0" + integrity sha1-S8A3PBZLwykbTTaMgpzxqApZ3KA= + dependencies: + map-visit "^1.0.0" + object-visit "^1.0.0" + +color-convert@^1.9.0: + version "1.9.3" + resolved "https://registry.yarnpkg.com/color-convert/-/color-convert-1.9.3.tgz#bb71850690e1f136567de629d2d5471deda4c1e8" + integrity sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg== + dependencies: + color-name "1.1.3" + +color-convert@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/color-convert/-/color-convert-2.0.1.tgz#72d3a68d598c9bdb3af2ad1e84f21d896abd4de3" + integrity sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ== + dependencies: + color-name "~1.1.4" + +color-name@1.1.3: + version "1.1.3" + resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.3.tgz#a7d0558bd89c42f795dd42328f740831ca53bc25" + integrity sha1-p9BVi9icQveV3UIyj3QIMcpTvCU= + +color-name@~1.1.4: + version "1.1.4" + resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.4.tgz#c2a09a87acbde69543de6f63fa3995c826c536a2" + integrity sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA== + +combined-stream@^1.0.6, combined-stream@~1.0.6: + version "1.0.8" + resolved "https://registry.yarnpkg.com/combined-stream/-/combined-stream-1.0.8.tgz#c3d45a8b34fd730631a110a8a2520682b31d5a7f" + integrity sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg== + dependencies: + delayed-stream "~1.0.0" + +commander@^4.0.1: + version "4.1.1" + resolved "https://registry.yarnpkg.com/commander/-/commander-4.1.1.tgz#9fd602bd936294e9e9ef46a3f4d6964044b18068" + integrity sha512-NOKm8xhkzAjzFx8B2v5OAHT+u5pRQc2UCa2Vq9jYL/31o2wi9mxBA7LIFs3sV5VSC49z6pEhfbMULvShKj26WA== + +commondir@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/commondir/-/commondir-1.0.1.tgz#ddd800da0c66127393cca5950ea968a3aaf1253b" + integrity sha1-3dgA2gxmEnOTzKWVDqloo6rxJTs= + +component-emitter@^1.2.1: + version "1.3.0" + resolved "https://registry.yarnpkg.com/component-emitter/-/component-emitter-1.3.0.tgz#16e4070fba8ae29b679f2215853ee181ab2eabc0" + integrity sha512-Rd3se6QB+sO1TwqZjscQrurpEPIfO0/yYnSin6Q/rD3mOutHvUrCAhJub3r90uNb+SESBuE0QYoB90YdfatsRg== + +concat-map@0.0.1: + version "0.0.1" + resolved "https://registry.yarnpkg.com/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b" + integrity sha1-2Klr13/Wjfd5OnMDajug1UBdR3s= + +concat-stream@~2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/concat-stream/-/concat-stream-2.0.0.tgz#414cf5af790a48c60ab9be4527d56d5e41133cb1" + integrity sha512-MWufYdFw53ccGjCA+Ol7XJYpAlW6/prSMzuPOTRnJGcGzuhLn4Scrz7qf6o8bROZ514ltazcIFJZevcfbo0x7A== + dependencies: + buffer-from "^1.0.0" + inherits "^2.0.3" + readable-stream "^3.0.2" + typedarray "^0.0.6" + +confusing-browser-globals@^1.0.9: + version "1.0.9" + resolved "https://registry.yarnpkg.com/confusing-browser-globals/-/confusing-browser-globals-1.0.9.tgz#72bc13b483c0276801681871d4898516f8f54fdd" + integrity sha512-KbS1Y0jMtyPgIxjO7ZzMAuUpAKMt1SzCL9fsrKsX6b0zJPTaT0SiSPmewwVZg9UAO83HVIlEhZF84LIjZ0lmAw== + +contains-path@^0.1.0: + version "0.1.0" + resolved "https://registry.yarnpkg.com/contains-path/-/contains-path-0.1.0.tgz#fe8cf184ff6670b6baef01a9d4861a5cbec4120a" + integrity sha1-/ozxhP9mcLa67wGp1IYaXL7EEgo= + +convert-source-map@^1.1.0, convert-source-map@^1.4.0, convert-source-map@^1.7.0: + version "1.7.0" + resolved "https://registry.yarnpkg.com/convert-source-map/-/convert-source-map-1.7.0.tgz#17a2cb882d7f77d3490585e2ce6c524424a3a442" + integrity sha512-4FJkXzKXEDB1snCFZlLP4gpC3JILicCpGbzG9f9G7tGqGCzETQ2hWPrcinA9oU4wtf2biUaEH5065UnMeR33oA== + dependencies: + safe-buffer "~5.1.1" + +copy-descriptor@^0.1.0: + version "0.1.1" + resolved "https://registry.yarnpkg.com/copy-descriptor/-/copy-descriptor-0.1.1.tgz#676f6eb3c39997c2ee1ac3a924fd6124748f578d" + integrity sha1-Z29us8OZl8LuGsOpJP1hJHSPV40= + +core-js-compat@^3.6.2: + version "3.6.5" + resolved "https://registry.yarnpkg.com/core-js-compat/-/core-js-compat-3.6.5.tgz#2a51d9a4e25dfd6e690251aa81f99e3c05481f1c" + integrity sha512-7ItTKOhOZbznhXAQ2g/slGg1PJV5zDO/WdkTwi7UEOJmkvsE32PWvx6mKtDjiMpjnR2CNf6BAD6sSxIlv7ptng== + dependencies: + browserslist "^4.8.5" + semver "7.0.0" + +core-js@^3.2.1: + version "3.6.5" + resolved "https://registry.yarnpkg.com/core-js/-/core-js-3.6.5.tgz#7395dc273af37fb2e50e9bd3d9fe841285231d1a" + integrity sha512-vZVEEwZoIsI+vPEuoF9Iqf5H7/M3eeQqWlQnYa8FSKKePuYTf5MWnxb5SDAzCa60b3JBRS5g9b+Dq7b1y/RCrA== + +core-util-is@1.0.2, core-util-is@~1.0.0: + version "1.0.2" + resolved "https://registry.yarnpkg.com/core-util-is/-/core-util-is-1.0.2.tgz#b5fd54220aa2bc5ab57aab7140c940754503c1a7" + integrity sha1-tf1UIgqivFq1eqtxQMlAdUUDwac= + +cross-spawn@^6.0.0, cross-spawn@^6.0.5: + version "6.0.5" + resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-6.0.5.tgz#4a5ec7c64dfae22c3a14124dbacdee846d80cbc4" + integrity sha512-eTVLrBSt7fjbDygz805pMnstIs2VTBNkRm0qxZd+M7A5XDdxVRWO5MxGBXZhjY4cqLYLdtrGqRf8mBPmzwSpWQ== + dependencies: + nice-try "^1.0.4" + path-key "^2.0.1" + semver "^5.5.0" + shebang-command "^1.2.0" + which "^1.2.9" + +crypto-js@4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/crypto-js/-/crypto-js-4.0.0.tgz#2904ab2677a9d042856a2ea2ef80de92e4a36dcc" + integrity sha512-bzHZN8Pn+gS7DQA6n+iUmBfl0hO5DJq++QP3U6uTucDtk/0iGpXd/Gg7CGR0p8tJhofJyaKoWBuJI4eAO00BBg== + +cssom@0.3.x, "cssom@>= 0.3.2 < 0.4.0": + version "0.3.8" + resolved "https://registry.yarnpkg.com/cssom/-/cssom-0.3.8.tgz#9f1276f5b2b463f2114d3f2c75250af8c1a36f4a" + integrity sha512-b0tGHbfegbhPJpxpiBPU2sCkigAqtM9O121le6bbOlgyV+NyGyCmVfJ6QW9eRjz8CpNfWEOYBIMIGRYkLwsIYg== + +cssstyle@^1.0.0: + version "1.4.0" + resolved "https://registry.yarnpkg.com/cssstyle/-/cssstyle-1.4.0.tgz#9d31328229d3c565c61e586b02041a28fccdccf1" + integrity sha512-GBrLZYZ4X4x6/QEoBnIrqb8B/f5l4+8me2dkom/j1Gtbxy0kBv6OGzKuAsGM75bkGwGAFkt56Iwg28S3XTZgSA== + dependencies: + cssom "0.3.x" + +dashdash@^1.12.0: + version "1.14.1" + resolved "https://registry.yarnpkg.com/dashdash/-/dashdash-1.14.1.tgz#853cfa0f7cbe2fed5de20326b8dd581035f6e2f0" + integrity sha1-hTz6D3y+L+1d4gMmuN1YEDX24vA= + dependencies: + assert-plus "^1.0.0" + +data-urls@^1.0.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/data-urls/-/data-urls-1.1.0.tgz#15ee0582baa5e22bb59c77140da8f9c76963bbfe" + integrity sha512-YTWYI9se1P55u58gL5GkQHW4P6VJBJ5iBT+B5a7i2Tjadhv52paJG0qHX4A0OR6/t52odI64KP2YvFpkDOi3eQ== + dependencies: + abab "^2.0.0" + whatwg-mimetype "^2.2.0" + whatwg-url "^7.0.0" + +debug@=3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/debug/-/debug-3.1.0.tgz#5bb5a0672628b64149566ba16819e61518c67261" + integrity sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g== + dependencies: + ms "2.0.0" + +debug@^2.2.0, debug@^2.3.3, debug@^2.6.9: + version "2.6.9" + resolved "https://registry.yarnpkg.com/debug/-/debug-2.6.9.tgz#5d128515df134ff327e90a4c93f4e077a536341f" + integrity sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA== + dependencies: + ms "2.0.0" + +debug@^4.0.1, debug@^4.1.0, debug@^4.1.1: + version "4.1.1" + resolved "https://registry.yarnpkg.com/debug/-/debug-4.1.1.tgz#3b72260255109c6b589cee050f1d516139664791" + integrity sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw== + dependencies: + ms "^2.1.1" + +decamelize@^1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/decamelize/-/decamelize-1.2.0.tgz#f6534d15148269b20352e7bee26f501f9a191290" + integrity sha1-9lNNFRSCabIDUue+4m9QH5oZEpA= + +decode-uri-component@^0.2.0: + version "0.2.0" + resolved "https://registry.yarnpkg.com/decode-uri-component/-/decode-uri-component-0.2.0.tgz#eb3913333458775cb84cd1a1fae062106bb87545" + integrity sha1-6zkTMzRYd1y4TNGh+uBiEGu4dUU= + +deep-is@~0.1.3: + version "0.1.3" + resolved "https://registry.yarnpkg.com/deep-is/-/deep-is-0.1.3.tgz#b369d6fb5dbc13eecf524f91b070feedc357cf34" + integrity sha1-s2nW+128E+7PUk+RsHD+7cNXzzQ= + +define-properties@^1.1.2, define-properties@^1.1.3: + version "1.1.3" + resolved "https://registry.yarnpkg.com/define-properties/-/define-properties-1.1.3.tgz#cf88da6cbee26fe6db7094f61d870cbd84cee9f1" + integrity sha512-3MqfYKj2lLzdMSf8ZIZE/V+Zuy+BgD6f164e8K2w7dgnpKArBDerGYpM46IYYcjnkdPNMjPk9A6VFB8+3SKlXQ== + dependencies: + object-keys "^1.0.12" + +define-property@^0.2.5: + version "0.2.5" + resolved "https://registry.yarnpkg.com/define-property/-/define-property-0.2.5.tgz#c35b1ef918ec3c990f9a5bc57be04aacec5c8116" + integrity sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY= + dependencies: + is-descriptor "^0.1.0" + +define-property@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/define-property/-/define-property-1.0.0.tgz#769ebaaf3f4a63aad3af9e8d304c9bbe79bfb0e6" + integrity sha1-dp66rz9KY6rTr56NMEybvnm/sOY= + dependencies: + is-descriptor "^1.0.0" + +define-property@^2.0.2: + version "2.0.2" + resolved "https://registry.yarnpkg.com/define-property/-/define-property-2.0.2.tgz#d459689e8d654ba77e02a817f8710d702cb16e9d" + integrity sha512-jwK2UV4cnPpbcG7+VRARKTZPUWowwXA8bzH5NP6ud0oeAxyYPuGZUAC7hMugpCdz4BeSZl2Dl9k66CHJ/46ZYQ== + dependencies: + is-descriptor "^1.0.2" + isobject "^3.0.1" + +delayed-stream@~1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/delayed-stream/-/delayed-stream-1.0.0.tgz#df3ae199acadfb7d440aaae0b29e2272b24ec619" + integrity sha1-3zrhmayt+31ECqrgsp4icrJOxhk= + +detect-newline@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/detect-newline/-/detect-newline-2.1.0.tgz#f41f1c10be4b00e87b5f13da680759f2c5bfd3e2" + integrity sha1-9B8cEL5LAOh7XxPaaAdZ8sW/0+I= + +diff-sequences@^24.9.0: + version "24.9.0" + resolved "https://registry.yarnpkg.com/diff-sequences/-/diff-sequences-24.9.0.tgz#5715d6244e2aa65f48bba0bc972db0b0b11e95b5" + integrity sha512-Dj6Wk3tWyTE+Fo1rW8v0Xhwk80um6yFYKbuAxc9c3EZxIHFDYwbi34Uk42u1CdnIiVorvt4RmlSDjIPyzGC2ew== + +docker-modem@^2.1.0: + version "2.1.3" + resolved "https://registry.yarnpkg.com/docker-modem/-/docker-modem-2.1.3.tgz#15432225f63db02eb5de4bb9a621b7293e5f264d" + integrity sha512-cwaRptBmYZwu/FyhGcqBm2MzXA77W2/E6eVkpOZVDk6PkI9Bjj84xPrXiHMA+OWjzNy+DFjgKh8Q+1hMR7/OHg== + dependencies: + debug "^4.1.1" + readable-stream "^3.5.0" + split-ca "^1.0.1" + ssh2 "^0.8.7" + +dockerode@^3.2.0: + version "3.2.0" + resolved "https://registry.yarnpkg.com/dockerode/-/dockerode-3.2.0.tgz#80e5bd9c2a988bdde4c995ae4aa2584fa0166c23" + integrity sha512-C+y/W4Kks7YLBsfUOTMkk1IVilb4cdj+rE+UZ5hnE+rpcn2frSs7kX+6H8GteTqHcv8sln+GyxuP1qdno3IzIw== + dependencies: + concat-stream "~2.0.0" + docker-modem "^2.1.0" + tar-fs "~2.0.1" + +doctrine@1.5.0: + version "1.5.0" + resolved "https://registry.yarnpkg.com/doctrine/-/doctrine-1.5.0.tgz#379dce730f6166f76cefa4e6707a159b02c5a6fa" + integrity sha1-N53Ocw9hZvds76TmcHoVmwLFpvo= + dependencies: + esutils "^2.0.2" + isarray "^1.0.0" + +doctrine@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/doctrine/-/doctrine-3.0.0.tgz#addebead72a6574db783639dc87a121773973961" + integrity sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w== + dependencies: + esutils "^2.0.2" + +domexception@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/domexception/-/domexception-1.0.1.tgz#937442644ca6a31261ef36e3ec677fe805582c90" + integrity sha512-raigMkn7CJNNo6Ihro1fzG7wr3fHuYVytzquZKX5n0yizGsTcYgzdIUwj1X9pK0VvjeihV+XiclP+DjwbsSKug== + dependencies: + webidl-conversions "^4.0.2" + +ecc-jsbn@~0.1.1: + version "0.1.2" + resolved "https://registry.yarnpkg.com/ecc-jsbn/-/ecc-jsbn-0.1.2.tgz#3a83a904e54353287874c564b7549386849a98c9" + integrity sha1-OoOpBOVDUyh4dMVkt1SThoSamMk= + dependencies: + jsbn "~0.1.0" + safer-buffer "^2.1.0" + +electron-to-chromium@^1.3.413: + version "1.3.427" + resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.3.427.tgz#ea43d02908a8c71f47ebb46e09de5a3cf8236f04" + integrity sha512-/rG5G7Opcw68/Yrb4qYkz07h3bESVRJjUl4X/FrKLXzoUJleKm6D7K7rTTz8V5LUWnd+BbTOyxJX2XprRqHD8A== + +elliptic@^6.5.2: + version "6.5.2" + resolved "https://registry.yarnpkg.com/elliptic/-/elliptic-6.5.2.tgz#05c5678d7173c049d8ca433552224a495d0e3762" + integrity sha512-f4x70okzZbIQl/NSRLkI/+tteV/9WqL98zx+SQ69KbXxmVrmjwsNUPn/gYJJ0sHvEak24cZgHIPegRePAtA/xw== + dependencies: + bn.js "^4.4.0" + brorand "^1.0.1" + hash.js "^1.0.0" + hmac-drbg "^1.0.0" + inherits "^2.0.1" + minimalistic-assert "^1.0.0" + minimalistic-crypto-utils "^1.0.0" + +emoji-regex@^7.0.1: + version "7.0.3" + resolved "https://registry.yarnpkg.com/emoji-regex/-/emoji-regex-7.0.3.tgz#933a04052860c85e83c122479c4748a8e4c72156" + integrity sha512-CwBLREIQ7LvYFB0WyRvwhq5N5qPhc6PMjD6bYggFlI5YyDgl+0vxq5VHbMOFqLg7hfWzmu8T5Z1QofhmTIhItA== + +emoji-regex@^8.0.0: + version "8.0.0" + resolved "https://registry.yarnpkg.com/emoji-regex/-/emoji-regex-8.0.0.tgz#e818fd69ce5ccfcb404594f842963bf53164cc37" + integrity sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A== + +end-of-stream@^1.1.0, end-of-stream@^1.4.1: + version "1.4.4" + resolved "https://registry.yarnpkg.com/end-of-stream/-/end-of-stream-1.4.4.tgz#5ae64a5f45057baf3626ec14da0ca5e4b2431eb0" + integrity sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q== + dependencies: + once "^1.4.0" + +error-ex@^1.2.0, error-ex@^1.3.1: + version "1.3.2" + resolved "https://registry.yarnpkg.com/error-ex/-/error-ex-1.3.2.tgz#b4ac40648107fdcdcfae242f428bea8a14d4f1bf" + integrity sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g== + dependencies: + is-arrayish "^0.2.1" + +es-abstract@^1.17.0, es-abstract@^1.17.0-next.1, es-abstract@^1.17.2, es-abstract@^1.17.5: + version "1.17.5" + resolved "https://registry.yarnpkg.com/es-abstract/-/es-abstract-1.17.5.tgz#d8c9d1d66c8981fb9200e2251d799eee92774ae9" + integrity sha512-BR9auzDbySxOcfog0tLECW8l28eRGpDpU3Dm3Hp4q/N+VtLTmyj4EUN088XZWQDW/hzj6sYRDXeOFsaAODKvpg== + dependencies: + es-to-primitive "^1.2.1" + function-bind "^1.1.1" + has "^1.0.3" + has-symbols "^1.0.1" + is-callable "^1.1.5" + is-regex "^1.0.5" + object-inspect "^1.7.0" + object-keys "^1.1.1" + object.assign "^4.1.0" + string.prototype.trimleft "^2.1.1" + string.prototype.trimright "^2.1.1" + +es-to-primitive@^1.2.1: + version "1.2.1" + resolved "https://registry.yarnpkg.com/es-to-primitive/-/es-to-primitive-1.2.1.tgz#e55cd4c9cdc188bcefb03b366c736323fc5c898a" + integrity sha512-QCOllgZJtaUo9miYBcLChTUaHNjJF3PYs1VidD7AwiEj1kYxKeQTctLAezAOH5ZKRH0g2IgPn6KwB4IT8iRpvA== + dependencies: + is-callable "^1.1.4" + is-date-object "^1.0.1" + is-symbol "^1.0.2" + +escape-string-regexp@^1.0.5: + version "1.0.5" + resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz#1b61c0562190a8dff6ae3bb2cf0200ca130b86d4" + integrity sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ= + +escodegen@^1.9.1: + version "1.14.1" + resolved "https://registry.yarnpkg.com/escodegen/-/escodegen-1.14.1.tgz#ba01d0c8278b5e95a9a45350142026659027a457" + integrity sha512-Bmt7NcRySdIfNPfU2ZoXDrrXsG9ZjvDxcAlMfDUgRBjLOWTuIACXPBFJH7Z+cLb40JeQco5toikyc9t9P8E9SQ== + dependencies: + esprima "^4.0.1" + estraverse "^4.2.0" + esutils "^2.0.2" + optionator "^0.8.1" + optionalDependencies: + source-map "~0.6.1" + +eslint-config-airbnb-base@^14.1.0: + version "14.1.0" + resolved "https://registry.yarnpkg.com/eslint-config-airbnb-base/-/eslint-config-airbnb-base-14.1.0.tgz#2ba4592dd6843258221d9bff2b6831bd77c874e4" + integrity sha512-+XCcfGyCnbzOnktDVhwsCAx+9DmrzEmuwxyHUJpw+kqBVT744OUBrB09khgFKlK1lshVww6qXGsYPZpavoNjJw== + dependencies: + confusing-browser-globals "^1.0.9" + object.assign "^4.1.0" + object.entries "^1.1.1" + +eslint-config-prettier@^6.10.1: + version "6.11.0" + resolved "https://registry.yarnpkg.com/eslint-config-prettier/-/eslint-config-prettier-6.11.0.tgz#f6d2238c1290d01c859a8b5c1f7d352a0b0da8b1" + integrity sha512-oB8cpLWSAjOVFEJhhyMZh6NOEOtBVziaqdDQ86+qhDHFbZXoRTM7pNSvFRfW/W/L/LrQ38C99J5CGuRBBzBsdA== + dependencies: + get-stdin "^6.0.0" + +eslint-import-resolver-node@^0.3.2: + version "0.3.3" + resolved "https://registry.yarnpkg.com/eslint-import-resolver-node/-/eslint-import-resolver-node-0.3.3.tgz#dbaa52b6b2816b50bc6711af75422de808e98404" + integrity sha512-b8crLDo0M5RSe5YG8Pu2DYBj71tSB6OvXkfzwbJU2w7y8P4/yo0MyF8jU26IEuEuHF2K5/gcAJE3LhQGqBBbVg== + dependencies: + debug "^2.6.9" + resolve "^1.13.1" + +eslint-module-utils@^2.4.1: + version "2.6.0" + resolved "https://registry.yarnpkg.com/eslint-module-utils/-/eslint-module-utils-2.6.0.tgz#579ebd094f56af7797d19c9866c9c9486629bfa6" + integrity sha512-6j9xxegbqe8/kZY8cYpcp0xhbK0EgJlg3g9mib3/miLaExuuwc3n5UEfSnU6hWMbT0FAYVvDbL9RrRgpUeQIvA== + dependencies: + debug "^2.6.9" + pkg-dir "^2.0.0" + +eslint-plugin-import@^2.20.2: + version "2.20.2" + resolved "https://registry.yarnpkg.com/eslint-plugin-import/-/eslint-plugin-import-2.20.2.tgz#91fc3807ce08be4837141272c8b99073906e588d" + integrity sha512-FObidqpXrR8OnCh4iNsxy+WACztJLXAHBO5hK79T1Hc77PgQZkyDGA5Ag9xAvRpglvLNxhH/zSmZ70/pZ31dHg== + dependencies: + array-includes "^3.0.3" + array.prototype.flat "^1.2.1" + contains-path "^0.1.0" + debug "^2.6.9" + doctrine "1.5.0" + eslint-import-resolver-node "^0.3.2" + eslint-module-utils "^2.4.1" + has "^1.0.3" + minimatch "^3.0.4" + object.values "^1.1.0" + read-pkg-up "^2.0.0" + resolve "^1.12.0" + +eslint-plugin-jest@^23.8.2: + version "23.8.2" + resolved "https://registry.yarnpkg.com/eslint-plugin-jest/-/eslint-plugin-jest-23.8.2.tgz#6f28b41c67ef635f803ebd9e168f6b73858eb8d4" + integrity sha512-xwbnvOsotSV27MtAe7s8uGWOori0nUsrXh2f1EnpmXua8sDfY6VZhHAhHg2sqK7HBNycRQExF074XSZ7DvfoFg== + dependencies: + "@typescript-eslint/experimental-utils" "^2.5.0" + +eslint-plugin-prettier@^3.1.3: + version "3.1.3" + resolved "https://registry.yarnpkg.com/eslint-plugin-prettier/-/eslint-plugin-prettier-3.1.3.tgz#ae116a0fc0e598fdae48743a4430903de5b4e6ca" + integrity sha512-+HG5jmu/dN3ZV3T6eCD7a4BlAySdN7mLIbJYo0z1cFQuI+r2DiTJEFeF68ots93PsnrMxbzIZ2S/ieX+mkrBeQ== + dependencies: + prettier-linter-helpers "^1.0.0" + +eslint-scope@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/eslint-scope/-/eslint-scope-5.0.0.tgz#e87c8887c73e8d1ec84f1ca591645c358bfc8fb9" + integrity sha512-oYrhJW7S0bxAFDvWqzvMPRm6pcgcnWc4QnofCAqRTRfQC0JcwenzGglTtsLyIuuWFfkqDG9vz67cnttSd53djw== + dependencies: + esrecurse "^4.1.0" + estraverse "^4.1.1" + +eslint-utils@^1.4.3: + version "1.4.3" + resolved "https://registry.yarnpkg.com/eslint-utils/-/eslint-utils-1.4.3.tgz#74fec7c54d0776b6f67e0251040b5806564e981f" + integrity sha512-fbBN5W2xdY45KulGXmLHZ3c3FHfVYmKg0IrAKGOkT/464PQsx2UeIzfz1RmEci+KLm1bBaAzZAh8+/E+XAeZ8Q== + dependencies: + eslint-visitor-keys "^1.1.0" + +eslint-utils@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/eslint-utils/-/eslint-utils-2.0.0.tgz#7be1cc70f27a72a76cd14aa698bcabed6890e1cd" + integrity sha512-0HCPuJv+7Wv1bACm8y5/ECVfYdfsAm9xmVb7saeFlxjPYALefjhbYoCkBjPdPzGH8wWyTpAez82Fh3VKYEZ8OA== + dependencies: + eslint-visitor-keys "^1.1.0" + +eslint-visitor-keys@^1.0.0, eslint-visitor-keys@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/eslint-visitor-keys/-/eslint-visitor-keys-1.1.0.tgz#e2a82cea84ff246ad6fb57f9bde5b46621459ec2" + integrity sha512-8y9YjtM1JBJU/A9Kc+SbaOV4y29sSWckBwMHa+FGtVj5gN/sbnKDf6xJUl+8g7FAij9LVaP8C24DUiH/f/2Z9A== + +eslint@^6.8.0: + version "6.8.0" + resolved "https://registry.yarnpkg.com/eslint/-/eslint-6.8.0.tgz#62262d6729739f9275723824302fb227c8c93ffb" + integrity sha512-K+Iayyo2LtyYhDSYwz5D5QdWw0hCacNzyq1Y821Xna2xSJj7cijoLLYmLxTQgcgZ9mC61nryMy9S7GRbYpI5Ig== + dependencies: + "@babel/code-frame" "^7.0.0" + ajv "^6.10.0" + chalk "^2.1.0" + cross-spawn "^6.0.5" + debug "^4.0.1" + doctrine "^3.0.0" + eslint-scope "^5.0.0" + eslint-utils "^1.4.3" + eslint-visitor-keys "^1.1.0" + espree "^6.1.2" + esquery "^1.0.1" + esutils "^2.0.2" + file-entry-cache "^5.0.1" + functional-red-black-tree "^1.0.1" + glob-parent "^5.0.0" + globals "^12.1.0" + ignore "^4.0.6" + import-fresh "^3.0.0" + imurmurhash "^0.1.4" + inquirer "^7.0.0" + is-glob "^4.0.0" + js-yaml "^3.13.1" + json-stable-stringify-without-jsonify "^1.0.1" + levn "^0.3.0" + lodash "^4.17.14" + minimatch "^3.0.4" + mkdirp "^0.5.1" + natural-compare "^1.4.0" + optionator "^0.8.3" + progress "^2.0.0" + regexpp "^2.0.1" + semver "^6.1.2" + strip-ansi "^5.2.0" + strip-json-comments "^3.0.1" + table "^5.2.3" + text-table "^0.2.0" + v8-compile-cache "^2.0.3" + +espree@^6.1.2: + version "6.2.1" + resolved "https://registry.yarnpkg.com/espree/-/espree-6.2.1.tgz#77fc72e1fd744a2052c20f38a5b575832e82734a" + integrity sha512-ysCxRQY3WaXJz9tdbWOwuWr5Y/XrPTGX9Kiz3yoUXwW0VZ4w30HTkQLaGx/+ttFjF8i+ACbArnB4ce68a9m5hw== + dependencies: + acorn "^7.1.1" + acorn-jsx "^5.2.0" + eslint-visitor-keys "^1.1.0" + +esprima@^4.0.0, esprima@^4.0.1: + version "4.0.1" + resolved "https://registry.yarnpkg.com/esprima/-/esprima-4.0.1.tgz#13b04cdb3e6c5d19df91ab6987a8695619b0aa71" + integrity sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A== + +esquery@^1.0.1: + version "1.3.1" + resolved "https://registry.yarnpkg.com/esquery/-/esquery-1.3.1.tgz#b78b5828aa8e214e29fb74c4d5b752e1c033da57" + integrity sha512-olpvt9QG0vniUBZspVRN6lwB7hOZoTRtT+jzR+tS4ffYx2mzbw+z0XCOk44aaLYKApNX5nMm+E+P6o25ip/DHQ== + dependencies: + estraverse "^5.1.0" + +esrecurse@^4.1.0: + version "4.2.1" + resolved "https://registry.yarnpkg.com/esrecurse/-/esrecurse-4.2.1.tgz#007a3b9fdbc2b3bb87e4879ea19c92fdbd3942cf" + integrity sha512-64RBB++fIOAXPw3P9cy89qfMlvZEXZkqqJkjqqXIvzP5ezRZjW+lPWjw35UX/3EhUPFYbg5ER4JYgDw4007/DQ== + dependencies: + estraverse "^4.1.0" + +estraverse@^4.1.0, estraverse@^4.1.1, estraverse@^4.2.0: + version "4.3.0" + resolved "https://registry.yarnpkg.com/estraverse/-/estraverse-4.3.0.tgz#398ad3f3c5a24948be7725e83d11a7de28cdbd1d" + integrity sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw== + +estraverse@^5.1.0: + version "5.1.0" + resolved "https://registry.yarnpkg.com/estraverse/-/estraverse-5.1.0.tgz#374309d39fd935ae500e7b92e8a6b4c720e59642" + integrity sha512-FyohXK+R0vE+y1nHLoBM7ZTyqRpqAlhdZHCWIWEviFLiGB8b04H6bQs8G+XTthacvT8VuwvteiP7RJSxMs8UEw== + +esutils@^2.0.2: + version "2.0.3" + resolved "https://registry.yarnpkg.com/esutils/-/esutils-2.0.3.tgz#74d2eb4de0b8da1293711910d50775b9b710ef64" + integrity sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g== + +events@^3.0.0, events@^3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/events/-/events-3.1.0.tgz#84279af1b34cb75aa88bf5ff291f6d0bd9b31a59" + integrity sha512-Rv+u8MLHNOdMjTAFeT3nCjHn2aGlx435FP/sDHNaRhDEMwyI/aB22Kj2qIN8R0cw3z28psEQLYwxVKLsKrMgWg== + +exec-sh@^0.3.2: + version "0.3.4" + resolved "https://registry.yarnpkg.com/exec-sh/-/exec-sh-0.3.4.tgz#3a018ceb526cc6f6df2bb504b2bfe8e3a4934ec5" + integrity sha512-sEFIkc61v75sWeOe72qyrqg2Qg0OuLESziUDk/O/z2qgS15y2gWVFrI6f2Qn/qw/0/NCfCEsmNA4zOjkwEZT1A== + +execa@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/execa/-/execa-1.0.0.tgz#c6236a5bb4df6d6f15e88e7f017798216749ddd8" + integrity sha512-adbxcyWV46qiHyvSp50TKt05tB4tK3HcmF7/nxfAdhnox83seTDbwnaqKO4sXRy7roHAIFqJP/Rw/AuEbX61LA== + dependencies: + cross-spawn "^6.0.0" + get-stream "^4.0.0" + is-stream "^1.1.0" + npm-run-path "^2.0.0" + p-finally "^1.0.0" + signal-exit "^3.0.0" + strip-eof "^1.0.0" + +exit@^0.1.2: + version "0.1.2" + resolved "https://registry.yarnpkg.com/exit/-/exit-0.1.2.tgz#0632638f8d877cc82107d30a0fff1a17cba1cd0c" + integrity sha1-BjJjj42HfMghB9MKD/8aF8uhzQw= + +expand-brackets@^2.1.4: + version "2.1.4" + resolved "https://registry.yarnpkg.com/expand-brackets/-/expand-brackets-2.1.4.tgz#b77735e315ce30f6b6eff0f83b04151a22449622" + integrity sha1-t3c14xXOMPa27/D4OwQVGiJEliI= + dependencies: + debug "^2.3.3" + define-property "^0.2.5" + extend-shallow "^2.0.1" + posix-character-classes "^0.1.0" + regex-not "^1.0.0" + snapdragon "^0.8.1" + to-regex "^3.0.1" + +expect@^24.9.0: + version "24.9.0" + resolved "https://registry.yarnpkg.com/expect/-/expect-24.9.0.tgz#b75165b4817074fa4a157794f46fe9f1ba15b6ca" + integrity sha512-wvVAx8XIol3Z5m9zvZXiyZOQ+sRJqNTIm6sGjdWlaZIeupQGO3WbYI+15D/AmEwZywL6wtJkbAbJtzkOfBuR0Q== + dependencies: + "@jest/types" "^24.9.0" + ansi-styles "^3.2.0" + jest-get-type "^24.9.0" + jest-matcher-utils "^24.9.0" + jest-message-util "^24.9.0" + jest-regex-util "^24.9.0" + +extend-shallow@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/extend-shallow/-/extend-shallow-2.0.1.tgz#51af7d614ad9a9f610ea1bafbb989d6b1c56890f" + integrity sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8= + dependencies: + is-extendable "^0.1.0" + +extend-shallow@^3.0.0, extend-shallow@^3.0.2: + version "3.0.2" + resolved "https://registry.yarnpkg.com/extend-shallow/-/extend-shallow-3.0.2.tgz#26a71aaf073b39fb2127172746131c2704028db8" + integrity sha1-Jqcarwc7OfshJxcnRhMcJwQCjbg= + dependencies: + assign-symbols "^1.0.0" + is-extendable "^1.0.1" + +extend@~3.0.2: + version "3.0.2" + resolved "https://registry.yarnpkg.com/extend/-/extend-3.0.2.tgz#f8b1136b4071fbd8eb140aff858b1019ec2915fa" + integrity sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g== + +external-editor@^3.0.3: + version "3.1.0" + resolved "https://registry.yarnpkg.com/external-editor/-/external-editor-3.1.0.tgz#cb03f740befae03ea4d283caed2741a83f335495" + integrity sha512-hMQ4CX1p1izmuLYyZqLMO/qGNw10wSv9QDCPfzXfyFrOaCSSoRfqE1Kf1s5an66J5JZC62NewG+mK49jOCtQew== + dependencies: + chardet "^0.7.0" + iconv-lite "^0.4.24" + tmp "^0.0.33" + +extglob@^2.0.4: + version "2.0.4" + resolved "https://registry.yarnpkg.com/extglob/-/extglob-2.0.4.tgz#ad00fe4dc612a9232e8718711dc5cb5ab0285543" + integrity sha512-Nmb6QXkELsuBr24CJSkilo6UHHgbekK5UiZgfE6UHD3Eb27YC6oD+bhcT+tJ6cl8dmsgdQxnWlcry8ksBIBLpw== + dependencies: + array-unique "^0.3.2" + define-property "^1.0.0" + expand-brackets "^2.1.4" + extend-shallow "^2.0.1" + fragment-cache "^0.2.1" + regex-not "^1.0.0" + snapdragon "^0.8.1" + to-regex "^3.0.1" + +extsprintf@1.3.0: + version "1.3.0" + resolved "https://registry.yarnpkg.com/extsprintf/-/extsprintf-1.3.0.tgz#96918440e3041a7a414f8c52e3c574eb3c3e1e05" + integrity sha1-lpGEQOMEGnpBT4xS48V06zw+HgU= + +extsprintf@^1.2.0: + version "1.4.0" + resolved "https://registry.yarnpkg.com/extsprintf/-/extsprintf-1.4.0.tgz#e2689f8f356fad62cca65a3a91c5df5f9551692f" + integrity sha1-4mifjzVvrWLMplo6kcXfX5VRaS8= + +fast-deep-equal@^3.1.1: + version "3.1.1" + resolved "https://registry.yarnpkg.com/fast-deep-equal/-/fast-deep-equal-3.1.1.tgz#545145077c501491e33b15ec408c294376e94ae4" + integrity sha512-8UEa58QDLauDNfpbrX55Q9jrGHThw2ZMdOky5Gl1CDtVeJDPVrG4Jxx1N8jw2gkWaff5UUuX1KJd+9zGe2B+ZA== + +fast-diff@^1.1.2: + version "1.2.0" + resolved "https://registry.yarnpkg.com/fast-diff/-/fast-diff-1.2.0.tgz#73ee11982d86caaf7959828d519cfe927fac5f03" + integrity sha512-xJuoT5+L99XlZ8twedaRf6Ax2TgQVxvgZOYoPKqZufmJib0tL2tegPBOZb1pVNgIhlqDlA0eO0c3wBvQcmzx4w== + +fast-json-stable-stringify@^2.0.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz#874bf69c6f404c2b5d99c481341399fd55892633" + integrity sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw== + +fast-levenshtein@~2.0.6: + version "2.0.6" + resolved "https://registry.yarnpkg.com/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz#3d8a5c66883a16a30ca8643e851f19baa7797917" + integrity sha1-PYpcZog6FqMMqGQ+hR8Zuqd5eRc= + +fb-watchman@^2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/fb-watchman/-/fb-watchman-2.0.1.tgz#fc84fb39d2709cf3ff6d743706157bb5708a8a85" + integrity sha512-DkPJKQeY6kKwmuMretBhr7G6Vodr7bFwDYTXIkfG1gjvNpaxBTQV3PbXg6bR1c1UP4jPOX0jHUbbHANL9vRjVg== + dependencies: + bser "2.1.1" + +figures@^3.0.0: + version "3.2.0" + resolved "https://registry.yarnpkg.com/figures/-/figures-3.2.0.tgz#625c18bd293c604dc4a8ddb2febf0c88341746af" + integrity sha512-yaduQFRKLXYOGgEn6AZau90j3ggSOyiqXU0F9JZfeXYhNa+Jk4X+s45A2zg5jns87GAFa34BBm2kXw4XpNcbdg== + dependencies: + escape-string-regexp "^1.0.5" + +file-entry-cache@^5.0.1: + version "5.0.1" + resolved "https://registry.yarnpkg.com/file-entry-cache/-/file-entry-cache-5.0.1.tgz#ca0f6efa6dd3d561333fb14515065c2fafdf439c" + integrity sha512-bCg29ictuBaKUwwArK4ouCaqDgLZcysCFLmM/Yn/FDoqndh/9vNuQfXRDvTuXKLxfD/JtZQGKFT8MGcJBK644g== + dependencies: + flat-cache "^2.0.1" + +file-uri-to-path@1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/file-uri-to-path/-/file-uri-to-path-1.0.0.tgz#553a7b8446ff6f684359c445f1e37a05dacc33dd" + integrity sha512-0Zt+s3L7Vf1biwWZ29aARiVYLx7iMGnEUl9x33fbB/j3jR81u/O2LbqK+Bm1CDSNDKVtJ/YjwY7TUd5SkeLQLw== + +fill-range@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/fill-range/-/fill-range-4.0.0.tgz#d544811d428f98eb06a63dc402d2403c328c38f7" + integrity sha1-1USBHUKPmOsGpj3EAtJAPDKMOPc= + dependencies: + extend-shallow "^2.0.1" + is-number "^3.0.0" + repeat-string "^1.6.1" + to-regex-range "^2.1.0" + +find-cache-dir@^2.0.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/find-cache-dir/-/find-cache-dir-2.1.0.tgz#8d0f94cd13fe43c6c7c261a0d86115ca918c05f7" + integrity sha512-Tq6PixE0w/VMFfCgbONnkiQIVol/JJL7nRMi20fqzA4NRs9AfeqMGeRdPi3wIhYkxjeBaWh2rxwapn5Tu3IqOQ== + dependencies: + commondir "^1.0.1" + make-dir "^2.0.0" + pkg-dir "^3.0.0" + +find-up@^2.0.0, find-up@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/find-up/-/find-up-2.1.0.tgz#45d1b7e506c717ddd482775a2b77920a3c0c57a7" + integrity sha1-RdG35QbHF93UgndaK3eSCjwMV6c= + dependencies: + locate-path "^2.0.0" + +find-up@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/find-up/-/find-up-3.0.0.tgz#49169f1d7993430646da61ecc5ae355c21c97b73" + integrity sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg== + dependencies: + locate-path "^3.0.0" + +flat-cache@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/flat-cache/-/flat-cache-2.0.1.tgz#5d296d6f04bda44a4630a301413bdbc2ec085ec0" + integrity sha512-LoQe6yDuUMDzQAEH8sgmh4Md6oZnc/7PjtwjNFSzveXqSHt6ka9fPBuso7IGf9Rz4uqnSnWiFH2B/zj24a5ReA== + dependencies: + flatted "^2.0.0" + rimraf "2.6.3" + write "1.0.3" + +flatted@^2.0.0: + version "2.0.2" + resolved "https://registry.yarnpkg.com/flatted/-/flatted-2.0.2.tgz#4575b21e2bcee7434aa9be662f4b7b5f9c2b5138" + integrity sha512-r5wGx7YeOwNWNlCA0wQ86zKyDLMQr+/RB8xy74M4hTphfmjlijTSSXGuH8rnvKZnfT9i+75zmd8jcKdMR4O6jA== + +follow-redirects@1.5.10: + version "1.5.10" + resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.5.10.tgz#7b7a9f9aea2fdff36786a94ff643ed07f4ff5e2a" + integrity sha512-0V5l4Cizzvqt5D44aTXbFZz+FtyXV1vrDN6qrelxtfYQKW0KO0W2T/hkE8xvGa/540LkZlkaUjO4ailYTFtHVQ== + dependencies: + debug "=3.1.0" + +for-in@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/for-in/-/for-in-1.0.2.tgz#81068d295a8142ec0ac726c6e2200c30fb6d5e80" + integrity sha1-gQaNKVqBQuwKxybG4iAMMPttXoA= + +forever-agent@~0.6.1: + version "0.6.1" + resolved "https://registry.yarnpkg.com/forever-agent/-/forever-agent-0.6.1.tgz#fbc71f0c41adeb37f96c577ad1ed42d8fdacca91" + integrity sha1-+8cfDEGt6zf5bFd60e1C2P2sypE= + +form-data@~2.3.2: + version "2.3.3" + resolved "https://registry.yarnpkg.com/form-data/-/form-data-2.3.3.tgz#dcce52c05f644f298c6a7ab936bd724ceffbf3a6" + integrity sha512-1lLKB2Mu3aGP1Q/2eCOx0fNbRMe7XdwktwOruhfqqd0rIJWwN4Dh+E3hrPSlDCXnSR7UtZ1N38rVXm+6+MEhJQ== + dependencies: + asynckit "^0.4.0" + combined-stream "^1.0.6" + mime-types "^2.1.12" + +fragment-cache@^0.2.1: + version "0.2.1" + resolved "https://registry.yarnpkg.com/fragment-cache/-/fragment-cache-0.2.1.tgz#4290fad27f13e89be7f33799c6bc5a0abfff0d19" + integrity sha1-QpD60n8T6Jvn8zeZxrxaCr//DRk= + dependencies: + map-cache "^0.2.2" + +fs-constants@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/fs-constants/-/fs-constants-1.0.0.tgz#6be0de9be998ce16af8afc24497b9ee9b7ccd9ad" + integrity sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow== + +fs-readdir-recursive@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/fs-readdir-recursive/-/fs-readdir-recursive-1.1.0.tgz#e32fc030a2ccee44a6b5371308da54be0b397d27" + integrity sha512-GNanXlVr2pf02+sPN40XN8HG+ePaNcvM0q5mZBd668Obwb0yD5GiUbZOFgwn8kGMY6I3mdyDJzieUy3PTYyTRA== + +fs.realpath@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/fs.realpath/-/fs.realpath-1.0.0.tgz#1504ad2523158caa40db4a2787cb01411994ea4f" + integrity sha1-FQStJSMVjKpA20onh8sBQRmU6k8= + +fsevents@^1.2.7: + version "1.2.12" + resolved "https://registry.yarnpkg.com/fsevents/-/fsevents-1.2.12.tgz#db7e0d8ec3b0b45724fd4d83d43554a8f1f0de5c" + integrity sha512-Ggd/Ktt7E7I8pxZRbGIs7vwqAPscSESMrCSkx2FtWeqmheJgCo2R74fTsZFCifr0VTPwqRpPv17+6b8Zp7th0Q== + dependencies: + bindings "^1.5.0" + nan "^2.12.1" + +function-bind@^1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/function-bind/-/function-bind-1.1.1.tgz#a56899d3ea3c9bab874bb9773b7c5ede92f4895d" + integrity sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A== + +functional-red-black-tree@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/functional-red-black-tree/-/functional-red-black-tree-1.0.1.tgz#1b0ab3bd553b2a0d6399d29c0e3ea0b252078327" + integrity sha1-GwqzvVU7Kg1jmdKcDj6gslIHgyc= + +gensync@^1.0.0-beta.1: + version "1.0.0-beta.1" + resolved "https://registry.yarnpkg.com/gensync/-/gensync-1.0.0-beta.1.tgz#58f4361ff987e5ff6e1e7a210827aa371eaac269" + integrity sha512-r8EC6NO1sngH/zdD9fiRDLdcgnbayXah+mLgManTaIZJqEC1MZstmnox8KpnI2/fxQwrp5OpCOYWLp4rBl4Jcg== + +get-caller-file@^2.0.1: + version "2.0.5" + resolved "https://registry.yarnpkg.com/get-caller-file/-/get-caller-file-2.0.5.tgz#4f94412a82db32f36e3b0b9741f8a97feb031f7e" + integrity sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg== + +get-stdin@^6.0.0: + version "6.0.0" + resolved "https://registry.yarnpkg.com/get-stdin/-/get-stdin-6.0.0.tgz#9e09bf712b360ab9225e812048f71fde9c89657b" + integrity sha512-jp4tHawyV7+fkkSKyvjuLZswblUtz+SQKzSWnBbii16BuZksJlU1wuBYXY75r+duh/llF1ur6oNwi+2ZzjKZ7g== + +get-stream@^4.0.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/get-stream/-/get-stream-4.1.0.tgz#c1b255575f3dc21d59bfc79cd3d2b46b1c3a54b5" + integrity sha512-GMat4EJ5161kIy2HevLlr4luNjBgvmj413KaQA7jt4V8B4RDsfpHk7WQ9GVqfYyyx8OS/L66Kox+rJRNklLK7w== + dependencies: + pump "^3.0.0" + +get-value@^2.0.3, get-value@^2.0.6: + version "2.0.6" + resolved "https://registry.yarnpkg.com/get-value/-/get-value-2.0.6.tgz#dc15ca1c672387ca76bd37ac0a395ba2042a2c28" + integrity sha1-3BXKHGcjh8p2vTesCjlbogQqLCg= + +getpass@^0.1.1: + version "0.1.7" + resolved "https://registry.yarnpkg.com/getpass/-/getpass-0.1.7.tgz#5eff8e3e684d569ae4cb2b1282604e8ba62149fa" + integrity sha1-Xv+OPmhNVprkyysSgmBOi6YhSfo= + dependencies: + assert-plus "^1.0.0" + +glob-parent@^3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/glob-parent/-/glob-parent-3.1.0.tgz#9e6af6299d8d3bd2bd40430832bd113df906c5ae" + integrity sha1-nmr2KZ2NO9K9QEMIMr0RPfkGxa4= + dependencies: + is-glob "^3.1.0" + path-dirname "^1.0.0" + +glob-parent@^5.0.0: + version "5.1.1" + resolved "https://registry.yarnpkg.com/glob-parent/-/glob-parent-5.1.1.tgz#b6c1ef417c4e5663ea498f1c45afac6916bbc229" + integrity sha512-FnI+VGOpnlGHWZxthPGR+QhR78fuiK0sNLkHQv+bL9fQi57lNNdquIbna/WrfROrolq8GK5Ek6BiMwqL/voRYQ== + dependencies: + is-glob "^4.0.1" + +glob@^7.0.0, glob@^7.1.1, glob@^7.1.2, glob@^7.1.3, glob@^7.1.6: + version "7.1.6" + resolved "https://registry.yarnpkg.com/glob/-/glob-7.1.6.tgz#141f33b81a7c2492e125594307480c46679278a6" + integrity sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA== + dependencies: + fs.realpath "^1.0.0" + inflight "^1.0.4" + inherits "2" + minimatch "^3.0.4" + once "^1.3.0" + path-is-absolute "^1.0.0" + +globals@^11.1.0: + version "11.12.0" + resolved "https://registry.yarnpkg.com/globals/-/globals-11.12.0.tgz#ab8795338868a0babd8525758018c2a7eb95c42e" + integrity sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA== + +globals@^12.1.0: + version "12.4.0" + resolved "https://registry.yarnpkg.com/globals/-/globals-12.4.0.tgz#a18813576a41b00a24a97e7f815918c2e19925f8" + integrity sha512-BWICuzzDvDoH54NHKCseDanAhE3CeDorgDL5MT6LMXXj2WCnd9UC2szdk4AWLfjdgNBCXLUanXYcpBBKOSWGwg== + dependencies: + type-fest "^0.8.1" + +graceful-fs@^4.1.11, graceful-fs@^4.1.15, graceful-fs@^4.1.2: + version "4.2.4" + resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.4.tgz#2256bde14d3632958c465ebc96dc467ca07a29fb" + integrity sha512-WjKPNJF79dtJAVniUlGGWHYGz2jWxT6VhN/4m1NdkbZ2nOsEF+cI1Edgql5zCRhs/VsQYRvrXctxktVXZUkixw== + +growly@^1.3.0: + version "1.3.0" + resolved "https://registry.yarnpkg.com/growly/-/growly-1.3.0.tgz#f10748cbe76af964b7c96c93c6bcc28af120c081" + integrity sha1-8QdIy+dq+WS3yWyTxrzCivEgwIE= + +har-schema@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/har-schema/-/har-schema-2.0.0.tgz#a94c2224ebcac04782a0d9035521f24735b7ec92" + integrity sha1-qUwiJOvKwEeCoNkDVSHyRzW37JI= + +har-validator@~5.1.3: + version "5.1.3" + resolved "https://registry.yarnpkg.com/har-validator/-/har-validator-5.1.3.tgz#1ef89ebd3e4996557675eed9893110dc350fa080" + integrity sha512-sNvOCzEQNr/qrvJgc3UG/kD4QtlHycrzwS+6mfTrrSq97BvaYcPZZI1ZSqGSPR73Cxn4LKTD4PttRwfU7jWq5g== + dependencies: + ajv "^6.5.5" + har-schema "^2.0.0" + +has-flag@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-3.0.0.tgz#b5d454dc2199ae225699f3467e5a07f3b955bafd" + integrity sha1-tdRU3CGZriJWmfNGfloH87lVuv0= + +has-flag@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-4.0.0.tgz#944771fd9c81c81265c4d6941860da06bb59479b" + integrity sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ== + +has-symbols@^1.0.0, has-symbols@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/has-symbols/-/has-symbols-1.0.1.tgz#9f5214758a44196c406d9bd76cebf81ec2dd31e8" + integrity sha512-PLcsoqu++dmEIZB+6totNFKq/7Do+Z0u4oT0zKOJNl3lYK6vGwwu2hjHs+68OEZbTjiUE9bgOABXbP/GvrS0Kg== + +has-value@^0.3.1: + version "0.3.1" + resolved "https://registry.yarnpkg.com/has-value/-/has-value-0.3.1.tgz#7b1f58bada62ca827ec0a2078025654845995e1f" + integrity sha1-ex9YutpiyoJ+wKIHgCVlSEWZXh8= + dependencies: + get-value "^2.0.3" + has-values "^0.1.4" + isobject "^2.0.0" + +has-value@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/has-value/-/has-value-1.0.0.tgz#18b281da585b1c5c51def24c930ed29a0be6b177" + integrity sha1-GLKB2lhbHFxR3vJMkw7SmgvmsXc= + dependencies: + get-value "^2.0.6" + has-values "^1.0.0" + isobject "^3.0.0" + +has-values@^0.1.4: + version "0.1.4" + resolved "https://registry.yarnpkg.com/has-values/-/has-values-0.1.4.tgz#6d61de95d91dfca9b9a02089ad384bff8f62b771" + integrity sha1-bWHeldkd/Km5oCCJrThL/49it3E= + +has-values@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/has-values/-/has-values-1.0.0.tgz#95b0b63fec2146619a6fe57fe75628d5a39efe4f" + integrity sha1-lbC2P+whRmGab+V/51Yo1aOe/k8= + dependencies: + is-number "^3.0.0" + kind-of "^4.0.0" + +has@^1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/has/-/has-1.0.3.tgz#722d7cbfc1f6aa8241f16dd814e011e1f41e8796" + integrity sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw== + dependencies: + function-bind "^1.1.1" + +hash-base@^3.0.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/hash-base/-/hash-base-3.1.0.tgz#55c381d9e06e1d2997a883b4a3fddfe7f0d3af33" + integrity sha512-1nmYp/rhMDiE7AYkDw+lLwlAzz0AntGIe51F3RfFfEqyQ3feY2eI/NcwC6umIQVOASPMsWJLJScWKSSvzL9IVA== + dependencies: + inherits "^2.0.4" + readable-stream "^3.6.0" + safe-buffer "^5.2.0" + +hash.js@^1.0.0, hash.js@^1.0.3: + version "1.1.7" + resolved "https://registry.yarnpkg.com/hash.js/-/hash.js-1.1.7.tgz#0babca538e8d4ee4a0f8988d68866537a003cf42" + integrity sha512-taOaskGt4z4SOANNseOviYDvjEJinIkRgmp7LbKP2YTTmVxWBl87s/uzK9r+44BclBSp2X7K1hqeNfz9JbBeXA== + dependencies: + inherits "^2.0.3" + minimalistic-assert "^1.0.1" + +hmac-drbg@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/hmac-drbg/-/hmac-drbg-1.0.1.tgz#d2745701025a6c775a6c545793ed502fc0c649a1" + integrity sha1-0nRXAQJabHdabFRXk+1QL8DGSaE= + dependencies: + hash.js "^1.0.3" + minimalistic-assert "^1.0.0" + minimalistic-crypto-utils "^1.0.1" + +homedir-polyfill@^1.0.1: + version "1.0.3" + resolved "https://registry.yarnpkg.com/homedir-polyfill/-/homedir-polyfill-1.0.3.tgz#743298cef4e5af3e194161fbadcc2151d3a058e8" + integrity sha512-eSmmWE5bZTK2Nou4g0AI3zZ9rswp7GRKoKXS1BLUkvPviOqs4YTN1djQIqrXy9k5gEtdLPy86JjRwsNM9tnDcA== + dependencies: + parse-passwd "^1.0.0" + +hosted-git-info@^2.1.4: + version "2.8.8" + resolved "https://registry.yarnpkg.com/hosted-git-info/-/hosted-git-info-2.8.8.tgz#7539bd4bc1e0e0a895815a2e0262420b12858488" + integrity sha512-f/wzC2QaWBs7t9IYqB4T3sR1xviIViXJRJTWBlx2Gf3g0Xi5vI7Yy4koXQ1c9OYDGHN9sBy1DQ2AB8fqZBWhUg== + +html-encoding-sniffer@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/html-encoding-sniffer/-/html-encoding-sniffer-1.0.2.tgz#e70d84b94da53aa375e11fe3a351be6642ca46f8" + integrity sha512-71lZziiDnsuabfdYiUeWdCVyKuqwWi23L8YeIgV9jSSZHCtb6wB1BKWooH7L3tn4/FuZJMVWyNaIDr4RGmaSYw== + dependencies: + whatwg-encoding "^1.0.1" + +html-escaper@^2.0.0: + version "2.0.2" + resolved "https://registry.yarnpkg.com/html-escaper/-/html-escaper-2.0.2.tgz#dfd60027da36a36dfcbe236262c00a5822681453" + integrity sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg== + +http-signature@~1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/http-signature/-/http-signature-1.2.0.tgz#9aecd925114772f3d95b65a60abb8f7c18fbace1" + integrity sha1-muzZJRFHcvPZW2WmCruPfBj7rOE= + dependencies: + assert-plus "^1.0.0" + jsprim "^1.2.2" + sshpk "^1.7.0" + +iconv-lite@0.4.24, iconv-lite@^0.4.24: + version "0.4.24" + resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.4.24.tgz#2022b4b25fbddc21d2f524974a474aafe733908b" + integrity sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA== + dependencies: + safer-buffer ">= 2.1.2 < 3" + +ieee754@^1.1.4: + version "1.1.13" + resolved "https://registry.yarnpkg.com/ieee754/-/ieee754-1.1.13.tgz#ec168558e95aa181fd87d37f55c32bbcb6708b84" + integrity sha512-4vf7I2LYV/HaWerSo3XmlMkp5eZ83i+/CDluXi/IGTs/O1sejBNhTtnxzmRZfvOUqj7lZjqHkeTvpgSFDlWZTg== + +ignore@^4.0.6: + version "4.0.6" + resolved "https://registry.yarnpkg.com/ignore/-/ignore-4.0.6.tgz#750e3db5862087b4737ebac8207ffd1ef27b25fc" + integrity sha512-cyFDKrqc/YdcWFniJhzI42+AzS+gNwmUzOSFcRCQYwySuBBBy/KjuxWLZ/FHEH6Moq1NizMOBWyTcv8O4OZIMg== + +import-fresh@^3.0.0: + version "3.2.1" + resolved "https://registry.yarnpkg.com/import-fresh/-/import-fresh-3.2.1.tgz#633ff618506e793af5ac91bf48b72677e15cbe66" + integrity sha512-6e1q1cnWP2RXD9/keSkxHScg508CdXqXWgWBaETNhyuBFz+kUZlKboh+ISK+bU++DmbHimVBrOz/zzPe0sZ3sQ== + dependencies: + parent-module "^1.0.0" + resolve-from "^4.0.0" + +import-local@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/import-local/-/import-local-2.0.0.tgz#55070be38a5993cf18ef6db7e961f5bee5c5a09d" + integrity sha512-b6s04m3O+s3CGSbqDIyP4R6aAwAeYlVq9+WUWep6iHa8ETRf9yei1U48C5MmfJmV9AiLYYBKPMq/W+/WRpQmCQ== + dependencies: + pkg-dir "^3.0.0" + resolve-cwd "^2.0.0" + +imurmurhash@^0.1.4: + version "0.1.4" + resolved "https://registry.yarnpkg.com/imurmurhash/-/imurmurhash-0.1.4.tgz#9218b9b2b928a238b13dc4fb6b6d576f231453ea" + integrity sha1-khi5srkoojixPcT7a21XbyMUU+o= + +inflight@^1.0.4: + version "1.0.6" + resolved "https://registry.yarnpkg.com/inflight/-/inflight-1.0.6.tgz#49bd6331d7d02d0c09bc910a1075ba8165b56df9" + integrity sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk= + dependencies: + once "^1.3.0" + wrappy "1" + +inherits@2, inherits@^2.0.1, inherits@^2.0.3, inherits@^2.0.4, inherits@~2.0.3: + version "2.0.4" + resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.4.tgz#0fa2c64f932917c3433a0ded55363aae37416b7c" + integrity sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ== + +inherits@2.0.3: + version "2.0.3" + resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.3.tgz#633c2c83e3da42a502f52466022480f4208261de" + integrity sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4= + +inquirer@^7.0.0: + version "7.1.0" + resolved "https://registry.yarnpkg.com/inquirer/-/inquirer-7.1.0.tgz#1298a01859883e17c7264b82870ae1034f92dd29" + integrity sha512-5fJMWEmikSYu0nv/flMc475MhGbB7TSPd/2IpFV4I4rMklboCH2rQjYY5kKiYGHqUF9gvaambupcJFFG9dvReg== + dependencies: + ansi-escapes "^4.2.1" + chalk "^3.0.0" + cli-cursor "^3.1.0" + cli-width "^2.0.0" + external-editor "^3.0.3" + figures "^3.0.0" + lodash "^4.17.15" + mute-stream "0.0.8" + run-async "^2.4.0" + rxjs "^6.5.3" + string-width "^4.1.0" + strip-ansi "^6.0.0" + through "^2.3.6" + +invariant@^2.2.2, invariant@^2.2.4: + version "2.2.4" + resolved "https://registry.yarnpkg.com/invariant/-/invariant-2.2.4.tgz#610f3c92c9359ce1db616e538008d23ff35158e6" + integrity sha512-phJfQVBuaJM5raOpJjSfkiD6BpbCE4Ns//LaXl6wGYtUBY83nWS6Rf9tXm2e8VaK60JEjYldbPif/A2B1C2gNA== + dependencies: + loose-envify "^1.0.0" + +is-accessor-descriptor@^0.1.6: + version "0.1.6" + resolved "https://registry.yarnpkg.com/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz#a9e12cb3ae8d876727eeef3843f8a0897b5c98d6" + integrity sha1-qeEss66Nh2cn7u84Q/igiXtcmNY= + dependencies: + kind-of "^3.0.2" + +is-accessor-descriptor@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz#169c2f6d3df1f992618072365c9b0ea1f6878656" + integrity sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ== + dependencies: + kind-of "^6.0.0" + +is-arrayish@^0.2.1: + version "0.2.1" + resolved "https://registry.yarnpkg.com/is-arrayish/-/is-arrayish-0.2.1.tgz#77c99840527aa8ecb1a8ba697b80645a7a926a9d" + integrity sha1-d8mYQFJ6qOyxqLppe4BkWnqSap0= + +is-binary-path@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/is-binary-path/-/is-binary-path-1.0.1.tgz#75f16642b480f187a711c814161fd3a4a7655898" + integrity sha1-dfFmQrSA8YenEcgUFh/TpKdlWJg= + dependencies: + binary-extensions "^1.0.0" + +is-buffer@^1.1.5: + version "1.1.6" + resolved "https://registry.yarnpkg.com/is-buffer/-/is-buffer-1.1.6.tgz#efaa2ea9daa0d7ab2ea13a97b2b8ad51fefbe8be" + integrity sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w== + +is-callable@^1.1.4, is-callable@^1.1.5: + version "1.1.5" + resolved "https://registry.yarnpkg.com/is-callable/-/is-callable-1.1.5.tgz#f7e46b596890456db74e7f6e976cb3273d06faab" + integrity sha512-ESKv5sMCJB2jnHTWZ3O5itG+O128Hsus4K4Qh1h2/cgn2vbgnLSVqfV46AeJA9D5EeeLa9w81KUXMtn34zhX+Q== + +is-ci@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/is-ci/-/is-ci-2.0.0.tgz#6bc6334181810e04b5c22b3d589fdca55026404c" + integrity sha512-YfJT7rkpQB0updsdHLGWrvhBJfcfzNNawYDNIyQXJz0IViGf75O8EBPKSdvw2rF+LGCsX4FZ8tcr3b19LcZq4w== + dependencies: + ci-info "^2.0.0" + +is-data-descriptor@^0.1.4: + version "0.1.4" + resolved "https://registry.yarnpkg.com/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz#0b5ee648388e2c860282e793f1856fec3f301b56" + integrity sha1-C17mSDiOLIYCgueT8YVv7D8wG1Y= + dependencies: + kind-of "^3.0.2" + +is-data-descriptor@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz#d84876321d0e7add03990406abbbbd36ba9268c7" + integrity sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ== + dependencies: + kind-of "^6.0.0" + +is-date-object@^1.0.1: + version "1.0.2" + resolved "https://registry.yarnpkg.com/is-date-object/-/is-date-object-1.0.2.tgz#bda736f2cd8fd06d32844e7743bfa7494c3bfd7e" + integrity sha512-USlDT524woQ08aoZFzh3/Z6ch9Y/EWXEHQ/AaRN0SkKq4t2Jw2R2339tSXmwuVoY7LLlBCbOIlx2myP/L5zk0g== + +is-descriptor@^0.1.0: + version "0.1.6" + resolved "https://registry.yarnpkg.com/is-descriptor/-/is-descriptor-0.1.6.tgz#366d8240dde487ca51823b1ab9f07a10a78251ca" + integrity sha512-avDYr0SB3DwO9zsMov0gKCESFYqCnE4hq/4z3TdUlukEy5t9C0YRq7HLrsN52NAcqXKaepeCD0n+B0arnVG3Hg== + dependencies: + is-accessor-descriptor "^0.1.6" + is-data-descriptor "^0.1.4" + kind-of "^5.0.0" + +is-descriptor@^1.0.0, is-descriptor@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/is-descriptor/-/is-descriptor-1.0.2.tgz#3b159746a66604b04f8c81524ba365c5f14d86ec" + integrity sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg== + dependencies: + is-accessor-descriptor "^1.0.0" + is-data-descriptor "^1.0.0" + kind-of "^6.0.2" + +is-extendable@^0.1.0, is-extendable@^0.1.1: + version "0.1.1" + resolved "https://registry.yarnpkg.com/is-extendable/-/is-extendable-0.1.1.tgz#62b110e289a471418e3ec36a617d472e301dfc89" + integrity sha1-YrEQ4omkcUGOPsNqYX1HLjAd/Ik= + +is-extendable@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/is-extendable/-/is-extendable-1.0.1.tgz#a7470f9e426733d81bd81e1155264e3a3507cab4" + integrity sha512-arnXMxT1hhoKo9k1LZdmlNyJdDDfy2v0fXjFlmok4+i8ul/6WlbVge9bhM74OpNPQPMGUToDtz+KXa1PneJxOA== + dependencies: + is-plain-object "^2.0.4" + +is-extglob@^2.1.0, is-extglob@^2.1.1: + version "2.1.1" + resolved "https://registry.yarnpkg.com/is-extglob/-/is-extglob-2.1.1.tgz#a88c02535791f02ed37c76a1b9ea9773c833f8c2" + integrity sha1-qIwCU1eR8C7TfHahueqXc8gz+MI= + +is-fullwidth-code-point@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz#a3b30a5c4f199183167aaab93beefae3ddfb654f" + integrity sha1-o7MKXE8ZkYMWeqq5O+764937ZU8= + +is-fullwidth-code-point@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz#f116f8064fe90b3f7844a38997c0b75051269f1d" + integrity sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg== + +is-generator-fn@^2.0.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/is-generator-fn/-/is-generator-fn-2.1.0.tgz#7d140adc389aaf3011a8f2a2a4cfa6faadffb118" + integrity sha512-cTIB4yPYL/Grw0EaSzASzg6bBy9gqCofvWN8okThAYIxKJZC+udlRAmGbM0XLeniEJSs8uEgHPGuHSe1XsOLSQ== + +is-glob@^3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/is-glob/-/is-glob-3.1.0.tgz#7ba5ae24217804ac70707b96922567486cc3e84a" + integrity sha1-e6WuJCF4BKxwcHuWkiVnSGzD6Eo= + dependencies: + is-extglob "^2.1.0" + +is-glob@^4.0.0, is-glob@^4.0.1: + version "4.0.1" + resolved "https://registry.yarnpkg.com/is-glob/-/is-glob-4.0.1.tgz#7567dbe9f2f5e2467bc77ab83c4a29482407a5dc" + integrity sha512-5G0tKtBTFImOqDnLB2hG6Bp2qcKEFduo4tZu9MT/H6NQv/ghhy30o55ufafxJ/LdH79LLs2Kfrn85TLKyA7BUg== + dependencies: + is-extglob "^2.1.1" + +is-number@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/is-number/-/is-number-3.0.0.tgz#24fd6201a4782cf50561c810276afc7d12d71195" + integrity sha1-JP1iAaR4LPUFYcgQJ2r8fRLXEZU= + dependencies: + kind-of "^3.0.2" + +is-plain-object@^2.0.3, is-plain-object@^2.0.4: + version "2.0.4" + resolved "https://registry.yarnpkg.com/is-plain-object/-/is-plain-object-2.0.4.tgz#2c163b3fafb1b606d9d17928f05c2a1c38e07677" + integrity sha512-h5PpgXkWitc38BBMYawTYMWJHFZJVnBquFE57xFpjB8pJFiF6gZ+bU+WyI/yqXiFR5mdLsgYNaPe8uao6Uv9Og== + dependencies: + isobject "^3.0.1" + +is-regex@^1.0.5: + version "1.0.5" + resolved "https://registry.yarnpkg.com/is-regex/-/is-regex-1.0.5.tgz#39d589a358bf18967f726967120b8fc1aed74eae" + integrity sha512-vlKW17SNq44owv5AQR3Cq0bQPEb8+kF3UKZ2fiZNOWtztYE5i0CzCZxFDwO58qAOWtxdBRVO/V5Qin1wjCqFYQ== + dependencies: + has "^1.0.3" + +is-stream@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/is-stream/-/is-stream-1.1.0.tgz#12d4a3dd4e68e0b79ceb8dbc84173ae80d91ca44" + integrity sha1-EtSj3U5o4Lec6428hBc66A2RykQ= + +is-string@^1.0.5: + version "1.0.5" + resolved "https://registry.yarnpkg.com/is-string/-/is-string-1.0.5.tgz#40493ed198ef3ff477b8c7f92f644ec82a5cd3a6" + integrity sha512-buY6VNRjhQMiF1qWDouloZlQbRhDPCebwxSjxMjxgemYT46YMd2NR0/H+fBhEfWX4A/w9TBJ+ol+okqJKFE6vQ== + +is-symbol@^1.0.2: + version "1.0.3" + resolved "https://registry.yarnpkg.com/is-symbol/-/is-symbol-1.0.3.tgz#38e1014b9e6329be0de9d24a414fd7441ec61937" + integrity sha512-OwijhaRSgqvhm/0ZdAcXNZt9lYdKFpcRDT5ULUuYXPoT794UNOdU+gpT6Rzo7b4V2HUl/op6GqY894AZwv9faQ== + dependencies: + has-symbols "^1.0.1" + +is-typedarray@~1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/is-typedarray/-/is-typedarray-1.0.0.tgz#e479c80858df0c1b11ddda6940f96011fcda4a9a" + integrity sha1-5HnICFjfDBsR3dppQPlgEfzaSpo= + +is-windows@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/is-windows/-/is-windows-1.0.2.tgz#d1850eb9791ecd18e6182ce12a30f396634bb19d" + integrity sha512-eXK1UInq2bPmjyX6e3VHIzMLobc4J94i4AWn+Hpq3OU5KkrRC96OAcR3PRJ/pGu6m8TRnBHP9dkXQVsT/COVIA== + +is-wsl@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/is-wsl/-/is-wsl-1.1.0.tgz#1f16e4aa22b04d1336b66188a66af3c600c3a66d" + integrity sha1-HxbkqiKwTRM2tmGIpmrzxgDDpm0= + +isarray@1.0.0, isarray@^1.0.0, isarray@~1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/isarray/-/isarray-1.0.0.tgz#bb935d48582cba168c06834957a54a3e07124f11" + integrity sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE= + +isexe@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/isexe/-/isexe-2.0.0.tgz#e8fbf374dc556ff8947a10dcb0572d633f2cfa10" + integrity sha1-6PvzdNxVb/iUehDcsFctYz8s+hA= + +isobject@^2.0.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/isobject/-/isobject-2.1.0.tgz#f065561096a3f1da2ef46272f815c840d87e0c89" + integrity sha1-8GVWEJaj8dou9GJy+BXIQNh+DIk= + dependencies: + isarray "1.0.0" + +isobject@^3.0.0, isobject@^3.0.1: + version "3.0.1" + resolved "https://registry.yarnpkg.com/isobject/-/isobject-3.0.1.tgz#4e431e92b11a9731636aa1f9c8d1ccbcfdab78df" + integrity sha1-TkMekrEalzFjaqH5yNHMvP2reN8= + +isstream@~0.1.2: + version "0.1.2" + resolved "https://registry.yarnpkg.com/isstream/-/isstream-0.1.2.tgz#47e63f7af55afa6f92e1500e690eb8b8529c099a" + integrity sha1-R+Y/evVa+m+S4VAOaQ64uFKcCZo= + +istanbul-lib-coverage@^2.0.2, istanbul-lib-coverage@^2.0.5: + version "2.0.5" + resolved "https://registry.yarnpkg.com/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.5.tgz#675f0ab69503fad4b1d849f736baaca803344f49" + integrity sha512-8aXznuEPCJvGnMSRft4udDRDtb1V3pkQkMMI5LI+6HuQz5oQ4J2UFn1H82raA3qJtyOLkkwVqICBQkjnGtn5mA== + +istanbul-lib-instrument@^3.0.1, istanbul-lib-instrument@^3.3.0: + version "3.3.0" + resolved "https://registry.yarnpkg.com/istanbul-lib-instrument/-/istanbul-lib-instrument-3.3.0.tgz#a5f63d91f0bbc0c3e479ef4c5de027335ec6d630" + integrity sha512-5nnIN4vo5xQZHdXno/YDXJ0G+I3dAm4XgzfSVTPLQpj/zAV2dV6Juy0yaf10/zrJOJeHoN3fraFe+XRq2bFVZA== + dependencies: + "@babel/generator" "^7.4.0" + "@babel/parser" "^7.4.3" + "@babel/template" "^7.4.0" + "@babel/traverse" "^7.4.3" + "@babel/types" "^7.4.0" + istanbul-lib-coverage "^2.0.5" + semver "^6.0.0" + +istanbul-lib-report@^2.0.4: + version "2.0.8" + resolved "https://registry.yarnpkg.com/istanbul-lib-report/-/istanbul-lib-report-2.0.8.tgz#5a8113cd746d43c4889eba36ab10e7d50c9b4f33" + integrity sha512-fHBeG573EIihhAblwgxrSenp0Dby6tJMFR/HvlerBsrCTD5bkUuoNtn3gVh29ZCS824cGGBPn7Sg7cNk+2xUsQ== + dependencies: + istanbul-lib-coverage "^2.0.5" + make-dir "^2.1.0" + supports-color "^6.1.0" + +istanbul-lib-source-maps@^3.0.1: + version "3.0.6" + resolved "https://registry.yarnpkg.com/istanbul-lib-source-maps/-/istanbul-lib-source-maps-3.0.6.tgz#284997c48211752ec486253da97e3879defba8c8" + integrity sha512-R47KzMtDJH6X4/YW9XTx+jrLnZnscW4VpNN+1PViSYTejLVPWv7oov+Duf8YQSPyVRUvueQqz1TcsC6mooZTXw== + dependencies: + debug "^4.1.1" + istanbul-lib-coverage "^2.0.5" + make-dir "^2.1.0" + rimraf "^2.6.3" + source-map "^0.6.1" + +istanbul-reports@^2.2.6: + version "2.2.7" + resolved "https://registry.yarnpkg.com/istanbul-reports/-/istanbul-reports-2.2.7.tgz#5d939f6237d7b48393cc0959eab40cd4fd056931" + integrity sha512-uu1F/L1o5Y6LzPVSVZXNOoD/KXpJue9aeLRd0sM9uMXfZvzomB0WxVamWb5ue8kA2vVWEmW7EG+A5n3f1kqHKg== + dependencies: + html-escaper "^2.0.0" + +jest-changed-files@^24.9.0: + version "24.9.0" + resolved "https://registry.yarnpkg.com/jest-changed-files/-/jest-changed-files-24.9.0.tgz#08d8c15eb79a7fa3fc98269bc14b451ee82f8039" + integrity sha512-6aTWpe2mHF0DhL28WjdkO8LyGjs3zItPET4bMSeXU6T3ub4FPMw+mcOcbdGXQOAfmLcxofD23/5Bl9Z4AkFwqg== + dependencies: + "@jest/types" "^24.9.0" + execa "^1.0.0" + throat "^4.0.0" + +jest-cli@^24.9.0: + version "24.9.0" + resolved "https://registry.yarnpkg.com/jest-cli/-/jest-cli-24.9.0.tgz#ad2de62d07472d419c6abc301fc432b98b10d2af" + integrity sha512-+VLRKyitT3BWoMeSUIHRxV/2g8y9gw91Jh5z2UmXZzkZKpbC08CSehVxgHUwTpy+HwGcns/tqafQDJW7imYvGg== + dependencies: + "@jest/core" "^24.9.0" + "@jest/test-result" "^24.9.0" + "@jest/types" "^24.9.0" + chalk "^2.0.1" + exit "^0.1.2" + import-local "^2.0.0" + is-ci "^2.0.0" + jest-config "^24.9.0" + jest-util "^24.9.0" + jest-validate "^24.9.0" + prompts "^2.0.1" + realpath-native "^1.1.0" + yargs "^13.3.0" + +jest-config@^24.9.0: + version "24.9.0" + resolved "https://registry.yarnpkg.com/jest-config/-/jest-config-24.9.0.tgz#fb1bbc60c73a46af03590719efa4825e6e4dd1b5" + integrity sha512-RATtQJtVYQrp7fvWg6f5y3pEFj9I+H8sWw4aKxnDZ96mob5i5SD6ZEGWgMLXQ4LE8UurrjbdlLWdUeo+28QpfQ== + dependencies: + "@babel/core" "^7.1.0" + "@jest/test-sequencer" "^24.9.0" + "@jest/types" "^24.9.0" + babel-jest "^24.9.0" + chalk "^2.0.1" + glob "^7.1.1" + jest-environment-jsdom "^24.9.0" + jest-environment-node "^24.9.0" + jest-get-type "^24.9.0" + jest-jasmine2 "^24.9.0" + jest-regex-util "^24.3.0" + jest-resolve "^24.9.0" + jest-util "^24.9.0" + jest-validate "^24.9.0" + micromatch "^3.1.10" + pretty-format "^24.9.0" + realpath-native "^1.1.0" + +jest-diff@^24.9.0: + version "24.9.0" + resolved "https://registry.yarnpkg.com/jest-diff/-/jest-diff-24.9.0.tgz#931b7d0d5778a1baf7452cb816e325e3724055da" + integrity sha512-qMfrTs8AdJE2iqrTp0hzh7kTd2PQWrsFyj9tORoKmu32xjPjeE4NyjVRDz8ybYwqS2ik8N4hsIpiVTyFeo2lBQ== + dependencies: + chalk "^2.0.1" + diff-sequences "^24.9.0" + jest-get-type "^24.9.0" + pretty-format "^24.9.0" + +jest-docblock@^24.3.0: + version "24.9.0" + resolved "https://registry.yarnpkg.com/jest-docblock/-/jest-docblock-24.9.0.tgz#7970201802ba560e1c4092cc25cbedf5af5a8ce2" + integrity sha512-F1DjdpDMJMA1cN6He0FNYNZlo3yYmOtRUnktrT9Q37njYzC5WEaDdmbynIgy0L/IvXvvgsG8OsqhLPXTpfmZAA== + dependencies: + detect-newline "^2.1.0" + +jest-each@^24.9.0: + version "24.9.0" + resolved "https://registry.yarnpkg.com/jest-each/-/jest-each-24.9.0.tgz#eb2da602e2a610898dbc5f1f6df3ba86b55f8b05" + integrity sha512-ONi0R4BvW45cw8s2Lrx8YgbeXL1oCQ/wIDwmsM3CqM/nlblNCPmnC3IPQlMbRFZu3wKdQ2U8BqM6lh3LJ5Bsog== + dependencies: + "@jest/types" "^24.9.0" + chalk "^2.0.1" + jest-get-type "^24.9.0" + jest-util "^24.9.0" + pretty-format "^24.9.0" + +jest-environment-jsdom@^24.9.0: + version "24.9.0" + resolved "https://registry.yarnpkg.com/jest-environment-jsdom/-/jest-environment-jsdom-24.9.0.tgz#4b0806c7fc94f95edb369a69cc2778eec2b7375b" + integrity sha512-Zv9FV9NBRzLuALXjvRijO2351DRQeLYXtpD4xNvfoVFw21IOKNhZAEUKcbiEtjTkm2GsJ3boMVgkaR7rN8qetA== + dependencies: + "@jest/environment" "^24.9.0" + "@jest/fake-timers" "^24.9.0" + "@jest/types" "^24.9.0" + jest-mock "^24.9.0" + jest-util "^24.9.0" + jsdom "^11.5.1" + +jest-environment-node@^24.9.0: + version "24.9.0" + resolved "https://registry.yarnpkg.com/jest-environment-node/-/jest-environment-node-24.9.0.tgz#333d2d2796f9687f2aeebf0742b519f33c1cbfd3" + integrity sha512-6d4V2f4nxzIzwendo27Tr0aFm+IXWa0XEUnaH6nU0FMaozxovt+sfRvh4J47wL1OvF83I3SSTu0XK+i4Bqe7uA== + dependencies: + "@jest/environment" "^24.9.0" + "@jest/fake-timers" "^24.9.0" + "@jest/types" "^24.9.0" + jest-mock "^24.9.0" + jest-util "^24.9.0" + +jest-get-type@^24.9.0: + version "24.9.0" + resolved "https://registry.yarnpkg.com/jest-get-type/-/jest-get-type-24.9.0.tgz#1684a0c8a50f2e4901b6644ae861f579eed2ef0e" + integrity sha512-lUseMzAley4LhIcpSP9Jf+fTrQ4a1yHQwLNeeVa2cEmbCGeoZAtYPOIv8JaxLD/sUpKxetKGP+gsHl8f8TSj8Q== + +jest-haste-map@^24.9.0: + version "24.9.0" + resolved "https://registry.yarnpkg.com/jest-haste-map/-/jest-haste-map-24.9.0.tgz#b38a5d64274934e21fa417ae9a9fbeb77ceaac7d" + integrity sha512-kfVFmsuWui2Sj1Rp1AJ4D9HqJwE4uwTlS/vO+eRUaMmd54BFpli2XhMQnPC2k4cHFVbB2Q2C+jtI1AGLgEnCjQ== + dependencies: + "@jest/types" "^24.9.0" + anymatch "^2.0.0" + fb-watchman "^2.0.0" + graceful-fs "^4.1.15" + invariant "^2.2.4" + jest-serializer "^24.9.0" + jest-util "^24.9.0" + jest-worker "^24.9.0" + micromatch "^3.1.10" + sane "^4.0.3" + walker "^1.0.7" + optionalDependencies: + fsevents "^1.2.7" + +jest-jasmine2@^24.9.0: + version "24.9.0" + resolved "https://registry.yarnpkg.com/jest-jasmine2/-/jest-jasmine2-24.9.0.tgz#1f7b1bd3242c1774e62acabb3646d96afc3be6a0" + integrity sha512-Cq7vkAgaYKp+PsX+2/JbTarrk0DmNhsEtqBXNwUHkdlbrTBLtMJINADf2mf5FkowNsq8evbPc07/qFO0AdKTzw== + dependencies: + "@babel/traverse" "^7.1.0" + "@jest/environment" "^24.9.0" + "@jest/test-result" "^24.9.0" + "@jest/types" "^24.9.0" + chalk "^2.0.1" + co "^4.6.0" + expect "^24.9.0" + is-generator-fn "^2.0.0" + jest-each "^24.9.0" + jest-matcher-utils "^24.9.0" + jest-message-util "^24.9.0" + jest-runtime "^24.9.0" + jest-snapshot "^24.9.0" + jest-util "^24.9.0" + pretty-format "^24.9.0" + throat "^4.0.0" + +jest-leak-detector@^24.9.0: + version "24.9.0" + resolved "https://registry.yarnpkg.com/jest-leak-detector/-/jest-leak-detector-24.9.0.tgz#b665dea7c77100c5c4f7dfcb153b65cf07dcf96a" + integrity sha512-tYkFIDsiKTGwb2FG1w8hX9V0aUb2ot8zY/2nFg087dUageonw1zrLMP4W6zsRO59dPkTSKie+D4rhMuP9nRmrA== + dependencies: + jest-get-type "^24.9.0" + pretty-format "^24.9.0" + +jest-matcher-utils@^24.9.0: + version "24.9.0" + resolved "https://registry.yarnpkg.com/jest-matcher-utils/-/jest-matcher-utils-24.9.0.tgz#f5b3661d5e628dffe6dd65251dfdae0e87c3a073" + integrity sha512-OZz2IXsu6eaiMAwe67c1T+5tUAtQyQx27/EMEkbFAGiw52tB9em+uGbzpcgYVpA8wl0hlxKPZxrly4CXU/GjHA== + dependencies: + chalk "^2.0.1" + jest-diff "^24.9.0" + jest-get-type "^24.9.0" + pretty-format "^24.9.0" + +jest-message-util@^24.9.0: + version "24.9.0" + resolved "https://registry.yarnpkg.com/jest-message-util/-/jest-message-util-24.9.0.tgz#527f54a1e380f5e202a8d1149b0ec872f43119e3" + integrity sha512-oCj8FiZ3U0hTP4aSui87P4L4jC37BtQwUMqk+zk/b11FR19BJDeZsZAvIHutWnmtw7r85UmR3CEWZ0HWU2mAlw== + dependencies: + "@babel/code-frame" "^7.0.0" + "@jest/test-result" "^24.9.0" + "@jest/types" "^24.9.0" + "@types/stack-utils" "^1.0.1" + chalk "^2.0.1" + micromatch "^3.1.10" + slash "^2.0.0" + stack-utils "^1.0.1" + +jest-mock@^24.9.0: + version "24.9.0" + resolved "https://registry.yarnpkg.com/jest-mock/-/jest-mock-24.9.0.tgz#c22835541ee379b908673ad51087a2185c13f1c6" + integrity sha512-3BEYN5WbSq9wd+SyLDES7AHnjH9A/ROBwmz7l2y+ol+NtSFO8DYiEBzoO1CeFc9a8DYy10EO4dDFVv/wN3zl1w== + dependencies: + "@jest/types" "^24.9.0" + +jest-pnp-resolver@^1.2.1: + version "1.2.1" + resolved "https://registry.yarnpkg.com/jest-pnp-resolver/-/jest-pnp-resolver-1.2.1.tgz#ecdae604c077a7fbc70defb6d517c3c1c898923a" + integrity sha512-pgFw2tm54fzgYvc/OHrnysABEObZCUNFnhjoRjaVOCN8NYc032/gVjPaHD4Aq6ApkSieWtfKAFQtmDKAmhupnQ== + +jest-regex-util@^24.3.0, jest-regex-util@^24.9.0: + version "24.9.0" + resolved "https://registry.yarnpkg.com/jest-regex-util/-/jest-regex-util-24.9.0.tgz#c13fb3380bde22bf6575432c493ea8fe37965636" + integrity sha512-05Cmb6CuxaA+Ys6fjr3PhvV3bGQmO+2p2La4hFbU+W5uOc479f7FdLXUWXw4pYMAhhSZIuKHwSXSu6CsSBAXQA== + +jest-resolve-dependencies@^24.9.0: + version "24.9.0" + resolved "https://registry.yarnpkg.com/jest-resolve-dependencies/-/jest-resolve-dependencies-24.9.0.tgz#ad055198959c4cfba8a4f066c673a3f0786507ab" + integrity sha512-Fm7b6AlWnYhT0BXy4hXpactHIqER7erNgIsIozDXWl5dVm+k8XdGVe1oTg1JyaFnOxarMEbax3wyRJqGP2Pq+g== + dependencies: + "@jest/types" "^24.9.0" + jest-regex-util "^24.3.0" + jest-snapshot "^24.9.0" + +jest-resolve@^24.9.0: + version "24.9.0" + resolved "https://registry.yarnpkg.com/jest-resolve/-/jest-resolve-24.9.0.tgz#dff04c7687af34c4dd7e524892d9cf77e5d17321" + integrity sha512-TaLeLVL1l08YFZAt3zaPtjiVvyy4oSA6CRe+0AFPPVX3Q/VI0giIWWoAvoS5L96vj9Dqxj4fB5p2qrHCmTU/MQ== + dependencies: + "@jest/types" "^24.9.0" + browser-resolve "^1.11.3" + chalk "^2.0.1" + jest-pnp-resolver "^1.2.1" + realpath-native "^1.1.0" + +jest-runner@^24.8.0, jest-runner@^24.9.0: + version "24.9.0" + resolved "https://registry.yarnpkg.com/jest-runner/-/jest-runner-24.9.0.tgz#574fafdbd54455c2b34b4bdf4365a23857fcdf42" + integrity sha512-KksJQyI3/0mhcfspnxxEOBueGrd5E4vV7ADQLT9ESaCzz02WnbdbKWIf5Mkaucoaj7obQckYPVX6JJhgUcoWWg== + dependencies: + "@jest/console" "^24.7.1" + "@jest/environment" "^24.9.0" + "@jest/test-result" "^24.9.0" + "@jest/types" "^24.9.0" + chalk "^2.4.2" + exit "^0.1.2" + graceful-fs "^4.1.15" + jest-config "^24.9.0" + jest-docblock "^24.3.0" + jest-haste-map "^24.9.0" + jest-jasmine2 "^24.9.0" + jest-leak-detector "^24.9.0" + jest-message-util "^24.9.0" + jest-resolve "^24.9.0" + jest-runtime "^24.9.0" + jest-util "^24.9.0" + jest-worker "^24.6.0" + source-map-support "^0.5.6" + throat "^4.0.0" + +jest-runtime@^24.9.0: + version "24.9.0" + resolved "https://registry.yarnpkg.com/jest-runtime/-/jest-runtime-24.9.0.tgz#9f14583af6a4f7314a6a9d9f0226e1a781c8e4ac" + integrity sha512-8oNqgnmF3v2J6PVRM2Jfuj8oX3syKmaynlDMMKQ4iyzbQzIG6th5ub/lM2bCMTmoTKM3ykcUYI2Pw9xwNtjMnw== + dependencies: + "@jest/console" "^24.7.1" + "@jest/environment" "^24.9.0" + "@jest/source-map" "^24.3.0" + "@jest/transform" "^24.9.0" + "@jest/types" "^24.9.0" + "@types/yargs" "^13.0.0" + chalk "^2.0.1" + exit "^0.1.2" + glob "^7.1.3" + graceful-fs "^4.1.15" + jest-config "^24.9.0" + jest-haste-map "^24.9.0" + jest-message-util "^24.9.0" + jest-mock "^24.9.0" + jest-regex-util "^24.3.0" + jest-resolve "^24.9.0" + jest-snapshot "^24.9.0" + jest-util "^24.9.0" + jest-validate "^24.9.0" + realpath-native "^1.1.0" + slash "^2.0.0" + strip-bom "^3.0.0" + yargs "^13.3.0" + +"jest-serial-runner@^1.1.0 ": + version "1.1.0" + resolved "https://registry.yarnpkg.com/jest-serial-runner/-/jest-serial-runner-1.1.0.tgz#867fcd3ce0284afdf742a7306a9cbfd998631aaf" + integrity sha512-QSCMVZMYPAB8ALys43sxgVt4R6slizz7wj2rbCQPvczMh/AOImKeRil6T0dqaXQUTT9UXYzq00zb1bdK5uGEVQ== + dependencies: + jest-runner "^24.8.0" + +jest-serializer@^24.9.0: + version "24.9.0" + resolved "https://registry.yarnpkg.com/jest-serializer/-/jest-serializer-24.9.0.tgz#e6d7d7ef96d31e8b9079a714754c5d5c58288e73" + integrity sha512-DxYipDr8OvfrKH3Kel6NdED3OXxjvxXZ1uIY2I9OFbGg+vUkkg7AGvi65qbhbWNPvDckXmzMPbK3u3HaDO49bQ== + +jest-snapshot@^24.9.0: + version "24.9.0" + resolved "https://registry.yarnpkg.com/jest-snapshot/-/jest-snapshot-24.9.0.tgz#ec8e9ca4f2ec0c5c87ae8f925cf97497b0e951ba" + integrity sha512-uI/rszGSs73xCM0l+up7O7a40o90cnrk429LOiK3aeTvfC0HHmldbd81/B7Ix81KSFe1lwkbl7GnBGG4UfuDew== + dependencies: + "@babel/types" "^7.0.0" + "@jest/types" "^24.9.0" + chalk "^2.0.1" + expect "^24.9.0" + jest-diff "^24.9.0" + jest-get-type "^24.9.0" + jest-matcher-utils "^24.9.0" + jest-message-util "^24.9.0" + jest-resolve "^24.9.0" + mkdirp "^0.5.1" + natural-compare "^1.4.0" + pretty-format "^24.9.0" + semver "^6.2.0" + +jest-util@^24.9.0: + version "24.9.0" + resolved "https://registry.yarnpkg.com/jest-util/-/jest-util-24.9.0.tgz#7396814e48536d2e85a37de3e4c431d7cb140162" + integrity sha512-x+cZU8VRmOJxbA1K5oDBdxQmdq0OIdADarLxk0Mq+3XS4jgvhG/oKGWcIDCtPG0HgjxOYvF+ilPJQsAyXfbNOg== + dependencies: + "@jest/console" "^24.9.0" + "@jest/fake-timers" "^24.9.0" + "@jest/source-map" "^24.9.0" + "@jest/test-result" "^24.9.0" + "@jest/types" "^24.9.0" + callsites "^3.0.0" + chalk "^2.0.1" + graceful-fs "^4.1.15" + is-ci "^2.0.0" + mkdirp "^0.5.1" + slash "^2.0.0" + source-map "^0.6.0" + +jest-validate@^24.9.0: + version "24.9.0" + resolved "https://registry.yarnpkg.com/jest-validate/-/jest-validate-24.9.0.tgz#0775c55360d173cd854e40180756d4ff52def8ab" + integrity sha512-HPIt6C5ACwiqSiwi+OfSSHbK8sG7akG8eATl+IPKaeIjtPOeBUd/g3J7DghugzxrGjI93qS/+RPKe1H6PqvhRQ== + dependencies: + "@jest/types" "^24.9.0" + camelcase "^5.3.1" + chalk "^2.0.1" + jest-get-type "^24.9.0" + leven "^3.1.0" + pretty-format "^24.9.0" + +jest-watcher@^24.9.0: + version "24.9.0" + resolved "https://registry.yarnpkg.com/jest-watcher/-/jest-watcher-24.9.0.tgz#4b56e5d1ceff005f5b88e528dc9afc8dd4ed2b3b" + integrity sha512-+/fLOfKPXXYJDYlks62/4R4GoT+GU1tYZed99JSCOsmzkkF7727RqKrjNAxtfO4YpGv11wybgRvCjR73lK2GZw== + dependencies: + "@jest/test-result" "^24.9.0" + "@jest/types" "^24.9.0" + "@types/yargs" "^13.0.0" + ansi-escapes "^3.0.0" + chalk "^2.0.1" + jest-util "^24.9.0" + string-length "^2.0.0" + +jest-worker@^24.6.0, jest-worker@^24.9.0: + version "24.9.0" + resolved "https://registry.yarnpkg.com/jest-worker/-/jest-worker-24.9.0.tgz#5dbfdb5b2d322e98567898238a9697bcce67b3e5" + integrity sha512-51PE4haMSXcHohnSMdM42anbvZANYTqMrr52tVKPqqsPJMzoP6FYYDVqahX/HrAoKEKz3uUPzSvKs9A3qR4iVw== + dependencies: + merge-stream "^2.0.0" + supports-color "^6.1.0" + +jest@24: + version "24.9.0" + resolved "https://registry.yarnpkg.com/jest/-/jest-24.9.0.tgz#987d290c05a08b52c56188c1002e368edb007171" + integrity sha512-YvkBL1Zm7d2B1+h5fHEOdyjCG+sGMz4f8D86/0HiqJ6MB4MnDc8FgP5vdWsGnemOQro7lnYo8UakZ3+5A0jxGw== + dependencies: + import-local "^2.0.0" + jest-cli "^24.9.0" + +"js-tokens@^3.0.0 || ^4.0.0", js-tokens@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/js-tokens/-/js-tokens-4.0.0.tgz#19203fb59991df98e3a287050d4647cdeaf32499" + integrity sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ== + +js-yaml@^3.13.1: + version "3.13.1" + resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-3.13.1.tgz#aff151b30bfdfa8e49e05da22e7415e9dfa37847" + integrity sha512-YfbcO7jXDdyj0DGxYVSlSeQNHbD7XPWvrVWeVUujrQEoZzWJIRrCPoyk6kL6IAjAG2IolMK4T0hNUe0HOUs5Jw== + dependencies: + argparse "^1.0.7" + esprima "^4.0.0" + +jsbn@~0.1.0: + version "0.1.1" + resolved "https://registry.yarnpkg.com/jsbn/-/jsbn-0.1.1.tgz#a5e654c2e5a2deb5f201d96cefbca80c0ef2f513" + integrity sha1-peZUwuWi3rXyAdls77yoDA7y9RM= + +jsdom@^11.5.1: + version "11.12.0" + resolved "https://registry.yarnpkg.com/jsdom/-/jsdom-11.12.0.tgz#1a80d40ddd378a1de59656e9e6dc5a3ba8657bc8" + integrity sha512-y8Px43oyiBM13Zc1z780FrfNLJCXTL40EWlty/LXUtcjykRBNgLlCjWXpfSPBl2iv+N7koQN+dvqszHZgT/Fjw== + dependencies: + abab "^2.0.0" + acorn "^5.5.3" + acorn-globals "^4.1.0" + array-equal "^1.0.0" + cssom ">= 0.3.2 < 0.4.0" + cssstyle "^1.0.0" + data-urls "^1.0.0" + domexception "^1.0.1" + escodegen "^1.9.1" + html-encoding-sniffer "^1.0.2" + left-pad "^1.3.0" + nwsapi "^2.0.7" + parse5 "4.0.0" + pn "^1.1.0" + request "^2.87.0" + request-promise-native "^1.0.5" + sax "^1.2.4" + symbol-tree "^3.2.2" + tough-cookie "^2.3.4" + w3c-hr-time "^1.0.1" + webidl-conversions "^4.0.2" + whatwg-encoding "^1.0.3" + whatwg-mimetype "^2.1.0" + whatwg-url "^6.4.1" + ws "^5.2.0" + xml-name-validator "^3.0.0" + +jsesc@^2.5.1: + version "2.5.2" + resolved "https://registry.yarnpkg.com/jsesc/-/jsesc-2.5.2.tgz#80564d2e483dacf6e8ef209650a67df3f0c283a4" + integrity sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA== + +jsesc@~0.5.0: + version "0.5.0" + resolved "https://registry.yarnpkg.com/jsesc/-/jsesc-0.5.0.tgz#e7dee66e35d6fc16f710fe91d5cf69f70f08911d" + integrity sha1-597mbjXW/Bb3EP6R1c9p9w8IkR0= + +json-parse-better-errors@^1.0.1: + version "1.0.2" + resolved "https://registry.yarnpkg.com/json-parse-better-errors/-/json-parse-better-errors-1.0.2.tgz#bb867cfb3450e69107c131d1c514bab3dc8bcaa9" + integrity sha512-mrqyZKfX5EhL7hvqcV6WG1yYjnjeuYDzDhhcAAUrq8Po85NBQBJP+ZDUT75qZQ98IkUoBqdkExkukOU7Ts2wrw== + +json-schema-traverse@^0.4.1: + version "0.4.1" + resolved "https://registry.yarnpkg.com/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz#69f6a87d9513ab8bb8fe63bdb0979c448e684660" + integrity sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg== + +json-schema@0.2.3: + version "0.2.3" + resolved "https://registry.yarnpkg.com/json-schema/-/json-schema-0.2.3.tgz#b480c892e59a2f05954ce727bd3f2a4e882f9e13" + integrity sha1-tIDIkuWaLwWVTOcnvT8qTogvnhM= + +json-stable-stringify-without-jsonify@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz#9db7b59496ad3f3cfef30a75142d2d930ad72651" + integrity sha1-nbe1lJatPzz+8wp1FC0tkwrXJlE= + +json-stringify-safe@~5.0.1: + version "5.0.1" + resolved "https://registry.yarnpkg.com/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz#1296a2d58fd45f19a0f6ce01d65701e2c735b6eb" + integrity sha1-Epai1Y/UXxmg9s4B1lcB4sc1tus= + +json5@^2.1.2: + version "2.1.3" + resolved "https://registry.yarnpkg.com/json5/-/json5-2.1.3.tgz#c9b0f7fa9233bfe5807fe66fcf3a5617ed597d43" + integrity sha512-KXPvOm8K9IJKFM0bmdn8QXh7udDh1g/giieX0NLCaMnb4hEiVFqnop2ImTXCc5e0/oHz3LTqmHGtExn5hfMkOA== + dependencies: + minimist "^1.2.5" + +jsprim@^1.2.2: + version "1.4.1" + resolved "https://registry.yarnpkg.com/jsprim/-/jsprim-1.4.1.tgz#313e66bc1e5cc06e438bc1b7499c2e5c56acb6a2" + integrity sha1-MT5mvB5cwG5Di8G3SZwuXFastqI= + dependencies: + assert-plus "1.0.0" + extsprintf "1.3.0" + json-schema "0.2.3" + verror "1.10.0" + +kind-of@^3.0.2, kind-of@^3.0.3, kind-of@^3.2.0: + version "3.2.2" + resolved "https://registry.yarnpkg.com/kind-of/-/kind-of-3.2.2.tgz#31ea21a734bab9bbb0f32466d893aea51e4a3c64" + integrity sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ= + dependencies: + is-buffer "^1.1.5" + +kind-of@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/kind-of/-/kind-of-4.0.0.tgz#20813df3d712928b207378691a45066fae72dd57" + integrity sha1-IIE989cSkosgc3hpGkUGb65y3Vc= + dependencies: + is-buffer "^1.1.5" + +kind-of@^5.0.0: + version "5.1.0" + resolved "https://registry.yarnpkg.com/kind-of/-/kind-of-5.1.0.tgz#729c91e2d857b7a419a1f9aa65685c4c33f5845d" + integrity sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw== + +kind-of@^6.0.0, kind-of@^6.0.2: + version "6.0.3" + resolved "https://registry.yarnpkg.com/kind-of/-/kind-of-6.0.3.tgz#07c05034a6c349fa06e24fa35aa76db4580ce4dd" + integrity sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw== + +kleur@^3.0.3: + version "3.0.3" + resolved "https://registry.yarnpkg.com/kleur/-/kleur-3.0.3.tgz#a79c9ecc86ee1ce3fa6206d1216c501f147fc07e" + integrity sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w== + +ledger-cosmos-js@^2: + version "2.1.7" + resolved "https://registry.yarnpkg.com/ledger-cosmos-js/-/ledger-cosmos-js-2.1.7.tgz#362d1139ac2504ccb56029abda053b2c5b290e8d" + integrity sha512-RyaP+6lRhllQTgk5X14PiZtsUE8bYP7mOiFkOMAzPQvUDdy8IZr17mDaH9whqHtE0wZd95YPN89VAhbfV+Re8w== + dependencies: + "@babel/runtime" "^7.7.4" + "@ledgerhq/hw-transport" "^4.77.0" + bech32 "^1.1.3" + ripemd160 "^2.0.2" + +left-pad@^1.3.0: + version "1.3.0" + resolved "https://registry.yarnpkg.com/left-pad/-/left-pad-1.3.0.tgz#5b8a3a7765dfe001261dde915589e782f8c94d1e" + integrity sha512-XI5MPzVNApjAyhQzphX8BkmKsKUxD4LdyK24iZeQGinBN9yTQT3bFlCBy/aVx2HrNcqQGsdot8ghrjyrvMCoEA== + +leven@^3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/leven/-/leven-3.1.0.tgz#77891de834064cccba82ae7842bb6b14a13ed7f2" + integrity sha512-qsda+H8jTaUaN/x5vzW2rzc+8Rw4TAQ/4KjB46IwK5VH+IlVeeeje/EoZRpiXvIqjFgK84QffqPztGI3VBLG1A== + +levenary@^1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/levenary/-/levenary-1.1.1.tgz#842a9ee98d2075aa7faeedbe32679e9205f46f77" + integrity sha512-mkAdOIt79FD6irqjYSs4rdbnlT5vRonMEvBVPVb3XmevfS8kgRXwfes0dhPdEtzTWD/1eNE/Bm/G1iRt6DcnQQ== + dependencies: + leven "^3.1.0" + +levn@^0.3.0, levn@~0.3.0: + version "0.3.0" + resolved "https://registry.yarnpkg.com/levn/-/levn-0.3.0.tgz#3b09924edf9f083c0490fdd4c0bc4421e04764ee" + integrity sha1-OwmSTt+fCDwEkP3UwLxEIeBHZO4= + dependencies: + prelude-ls "~1.1.2" + type-check "~0.3.2" + +load-json-file@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/load-json-file/-/load-json-file-2.0.0.tgz#7947e42149af80d696cbf797bcaabcfe1fe29ca8" + integrity sha1-eUfkIUmvgNaWy/eXvKq8/h/inKg= + dependencies: + graceful-fs "^4.1.2" + parse-json "^2.2.0" + pify "^2.0.0" + strip-bom "^3.0.0" + +load-json-file@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/load-json-file/-/load-json-file-4.0.0.tgz#2f5f45ab91e33216234fd53adab668eb4ec0993b" + integrity sha1-L19Fq5HjMhYjT9U62rZo607AmTs= + dependencies: + graceful-fs "^4.1.2" + parse-json "^4.0.0" + pify "^3.0.0" + strip-bom "^3.0.0" + +locate-path@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/locate-path/-/locate-path-2.0.0.tgz#2b568b265eec944c6d9c0de9c3dbbbca0354cd8e" + integrity sha1-K1aLJl7slExtnA3pw9u7ygNUzY4= + dependencies: + p-locate "^2.0.0" + path-exists "^3.0.0" + +locate-path@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/locate-path/-/locate-path-3.0.0.tgz#dbec3b3ab759758071b58fe59fc41871af21400e" + integrity sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A== + dependencies: + p-locate "^3.0.0" + path-exists "^3.0.0" + +lodash.sortby@^4.7.0: + version "4.7.0" + resolved "https://registry.yarnpkg.com/lodash.sortby/-/lodash.sortby-4.7.0.tgz#edd14c824e2cc9c1e0b0a1b42bb5210516a42438" + integrity sha1-7dFMgk4sycHgsKG0K7UhBRakJDg= + +lodash@^4.17.13, lodash@^4.17.14, lodash@^4.17.15: + version "4.17.15" + resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.15.tgz#b447f6670a0455bbfeedd11392eff330ea097548" + integrity sha512-8xOcRHvCjnocdS5cpwXQXVzmmh5e5+saE2QGoeQmbKmRS6J3VQppPOIt0MnmE+4xlZoumy0GPG0D0MVIQbNA1A== + +loose-envify@^1.0.0: + version "1.4.0" + resolved "https://registry.yarnpkg.com/loose-envify/-/loose-envify-1.4.0.tgz#71ee51fa7be4caec1a63839f7e682d8132d30caf" + integrity sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q== + dependencies: + js-tokens "^3.0.0 || ^4.0.0" + +make-dir@^2.0.0, make-dir@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/make-dir/-/make-dir-2.1.0.tgz#5f0310e18b8be898cc07009295a30ae41e91e6f5" + integrity sha512-LS9X+dc8KLxXCb8dni79fLIIUA5VyZoyjSMCwTluaXA0o27cCK0bhXkpgw+sTXVpPy/lSO57ilRixqk0vDmtRA== + dependencies: + pify "^4.0.1" + semver "^5.6.0" + +makeerror@1.0.x: + version "1.0.11" + resolved "https://registry.yarnpkg.com/makeerror/-/makeerror-1.0.11.tgz#e01a5c9109f2af79660e4e8b9587790184f5a96c" + integrity sha1-4BpckQnyr3lmDk6LlYd5AYT1qWw= + dependencies: + tmpl "1.0.x" + +map-cache@^0.2.2: + version "0.2.2" + resolved "https://registry.yarnpkg.com/map-cache/-/map-cache-0.2.2.tgz#c32abd0bd6525d9b051645bb4f26ac5dc98a0dbf" + integrity sha1-wyq9C9ZSXZsFFkW7TyasXcmKDb8= + +map-visit@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/map-visit/-/map-visit-1.0.0.tgz#ecdca8f13144e660f1b5bd41f12f3479d98dfb8f" + integrity sha1-7Nyo8TFE5mDxtb1B8S80edmN+48= + dependencies: + object-visit "^1.0.0" + +merge-stream@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/merge-stream/-/merge-stream-2.0.0.tgz#52823629a14dd00c9770fb6ad47dc6310f2c1f60" + integrity sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w== + +micromatch@^3.1.10, micromatch@^3.1.4: + version "3.1.10" + resolved "https://registry.yarnpkg.com/micromatch/-/micromatch-3.1.10.tgz#70859bc95c9840952f359a068a3fc49f9ecfac23" + integrity sha512-MWikgl9n9M3w+bpsY3He8L+w9eF9338xRl8IAO5viDizwSzziFEyUzo2xrrloB64ADbTf8uA8vRqqttDTOmccg== + dependencies: + arr-diff "^4.0.0" + array-unique "^0.3.2" + braces "^2.3.1" + define-property "^2.0.2" + extend-shallow "^3.0.2" + extglob "^2.0.4" + fragment-cache "^0.2.1" + kind-of "^6.0.2" + nanomatch "^1.2.9" + object.pick "^1.3.0" + regex-not "^1.0.0" + snapdragon "^0.8.1" + to-regex "^3.0.2" + +mime-db@1.44.0: + version "1.44.0" + resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.44.0.tgz#fa11c5eb0aca1334b4233cb4d52f10c5a6272f92" + integrity sha512-/NOTfLrsPBVeH7YtFPgsVWveuL+4SjjYxaQ1xtM1KMFj7HdxlBlxeyNLzhyJVx7r4rZGJAZ/6lkKCitSc/Nmpg== + +mime-types@^2.1.12, mime-types@~2.1.19: + version "2.1.27" + resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-2.1.27.tgz#47949f98e279ea53119f5722e0f34e529bec009f" + integrity sha512-JIhqnCasI9yD+SsmkquHBxTSEuZdQX5BuQnS2Vc7puQQQ+8yiP5AY5uWhpdv4YL4VM5c6iliiYWPgJ/nJQLp7w== + dependencies: + mime-db "1.44.0" + +mimic-fn@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/mimic-fn/-/mimic-fn-2.1.0.tgz#7ed2c2ccccaf84d3ffcb7a69b57711fc2083401b" + integrity sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg== + +minimalistic-assert@^1.0.0, minimalistic-assert@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/minimalistic-assert/-/minimalistic-assert-1.0.1.tgz#2e194de044626d4a10e7f7fbc00ce73e83e4d5c7" + integrity sha512-UtJcAD4yEaGtjPezWuO9wC4nwUnVH/8/Im3yEHQP4b67cXlD/Qr9hdITCU1xDbSEXg2XKNaP8jsReV7vQd00/A== + +minimalistic-crypto-utils@^1.0.0, minimalistic-crypto-utils@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/minimalistic-crypto-utils/-/minimalistic-crypto-utils-1.0.1.tgz#f6c00c1c0b082246e5c4d99dfb8c7c083b2b582a" + integrity sha1-9sAMHAsIIkblxNmd+4x8CDsrWCo= + +minimatch@^3.0.4: + version "3.0.4" + resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.0.4.tgz#5166e286457f03306064be5497e8dbb0c3d32083" + integrity sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA== + dependencies: + brace-expansion "^1.1.7" + +minimist@^1.1.1, minimist@^1.2.0, minimist@^1.2.5: + version "1.2.5" + resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.5.tgz#67d66014b66a6a8aaa0c083c5fd58df4e4e97602" + integrity sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw== + +mixin-deep@^1.2.0: + version "1.3.2" + resolved "https://registry.yarnpkg.com/mixin-deep/-/mixin-deep-1.3.2.tgz#1120b43dc359a785dce65b55b82e257ccf479566" + integrity sha512-WRoDn//mXBiJ1H40rqa3vH0toePwSsGb45iInWlTySa+Uu4k3tYUSxa2v1KqAiLtvlrSzaExqS1gtk96A9zvEA== + dependencies: + for-in "^1.0.2" + is-extendable "^1.0.1" + +mkdirp-classic@^0.5.2: + version "0.5.2" + resolved "https://registry.yarnpkg.com/mkdirp-classic/-/mkdirp-classic-0.5.2.tgz#54c441ce4c96cd7790e10b41a87aa51068ecab2b" + integrity sha512-ejdnDQcR75gwknmMw/tx02AuRs8jCtqFoFqDZMjiNxsu85sRIJVXDKHuLYvUUPRBUtV2FpSZa9bL1BUa3BdR2g== + +mkdirp@^0.5.1: + version "0.5.5" + resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-0.5.5.tgz#d91cefd62d1436ca0f41620e251288d420099def" + integrity sha512-NKmAlESf6jMGym1++R0Ra7wvhV+wFW63FaSOFPwRahvea0gMUcGUhVeAg/0BC0wiv9ih5NYPB1Wn1UEI1/L+xQ== + dependencies: + minimist "^1.2.5" + +ms@2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/ms/-/ms-2.0.0.tgz#5608aeadfc00be6c2901df5f9861788de0d597c8" + integrity sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g= + +ms@^2.1.1: + version "2.1.2" + resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.2.tgz#d09d1f357b443f493382a8eb3ccd183872ae6009" + integrity sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w== + +mute-stream@0.0.8: + version "0.0.8" + resolved "https://registry.yarnpkg.com/mute-stream/-/mute-stream-0.0.8.tgz#1630c42b2251ff81e2a283de96a5497ea92e5e0d" + integrity sha512-nnbWWOkoWyUsTjKrhgD0dcz22mdkSnpYqbEjIm2nhwhuxlSkpywJmBo8h0ZqJdkp73mb90SssHkN4rsRaBAfAA== + +nan@^2.12.1, nan@^2.13.2: + version "2.14.1" + resolved "https://registry.yarnpkg.com/nan/-/nan-2.14.1.tgz#d7be34dfa3105b91494c3147089315eff8874b01" + integrity sha512-isWHgVjnFjh2x2yuJ/tj3JbwoHu3UC2dX5G/88Cm24yB6YopVgxvBObDY7n5xW6ExmFhJpSEQqFPvq9zaXc8Jw== + +nanomatch@^1.2.9: + version "1.2.13" + resolved "https://registry.yarnpkg.com/nanomatch/-/nanomatch-1.2.13.tgz#b87a8aa4fc0de8fe6be88895b38983ff265bd119" + integrity sha512-fpoe2T0RbHwBTBUOftAfBPaDEi06ufaUai0mE6Yn1kacc3SnTErfb/h+X94VXzI64rKFHYImXSvdwGGCmwOqCA== + dependencies: + arr-diff "^4.0.0" + array-unique "^0.3.2" + define-property "^2.0.2" + extend-shallow "^3.0.2" + fragment-cache "^0.2.1" + is-windows "^1.0.2" + kind-of "^6.0.2" + object.pick "^1.3.0" + regex-not "^1.0.0" + snapdragon "^0.8.1" + to-regex "^3.0.1" + +natural-compare@^1.4.0: + version "1.4.0" + resolved "https://registry.yarnpkg.com/natural-compare/-/natural-compare-1.4.0.tgz#4abebfeed7541f2c27acfb29bdbbd15c8d5ba4f7" + integrity sha1-Sr6/7tdUHywnrPspvbvRXI1bpPc= + +nice-try@^1.0.4: + version "1.0.5" + resolved "https://registry.yarnpkg.com/nice-try/-/nice-try-1.0.5.tgz#a3378a7696ce7d223e88fc9b764bd7ef1089e366" + integrity sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ== + +node-addon-api@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/node-addon-api/-/node-addon-api-2.0.0.tgz#f9afb8d777a91525244b01775ea0ddbe1125483b" + integrity sha512-ASCL5U13as7HhOExbT6OlWJJUV/lLzL2voOSP1UVehpRD8FbSrSDjfScK/KwAvVTI5AS6r4VwbOMlIqtvRidnA== + +node-environment-flags@^1.0.5: + version "1.0.6" + resolved "https://registry.yarnpkg.com/node-environment-flags/-/node-environment-flags-1.0.6.tgz#a30ac13621f6f7d674260a54dede048c3982c088" + integrity sha512-5Evy2epuL+6TM0lCQGpFIj6KwiEsGh1SrHUhTbNX+sLbBtjidPZFAnVK9y5yU1+h//RitLbRHTIMyxQPtxMdHw== + dependencies: + object.getownpropertydescriptors "^2.0.3" + semver "^5.7.0" + +node-gyp-build@^4.2.0: + version "4.2.2" + resolved "https://registry.yarnpkg.com/node-gyp-build/-/node-gyp-build-4.2.2.tgz#3f44b65adaafd42fb6c3d81afd630e45c847eb66" + integrity sha512-Lqh7mrByWCM8Cf9UPqpeoVBBo5Ugx+RKu885GAzmLBVYjeywScxHXPGLa4JfYNZmcNGwzR0Glu5/9GaQZMFqyA== + +node-int64@^0.4.0: + version "0.4.0" + resolved "https://registry.yarnpkg.com/node-int64/-/node-int64-0.4.0.tgz#87a9065cdb355d3182d8f94ce11188b825c68a3b" + integrity sha1-h6kGXNs1XTGC2PlM4RGIuCXGijs= + +node-modules-regexp@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/node-modules-regexp/-/node-modules-regexp-1.0.0.tgz#8d9dbe28964a4ac5712e9131642107c71e90ec40" + integrity sha1-jZ2+KJZKSsVxLpExZCEHxx6Q7EA= + +node-notifier@^5.4.2: + version "5.4.3" + resolved "https://registry.yarnpkg.com/node-notifier/-/node-notifier-5.4.3.tgz#cb72daf94c93904098e28b9c590fd866e464bd50" + integrity sha512-M4UBGcs4jeOK9CjTsYwkvH6/MzuUmGCyTW+kCY7uO+1ZVr0+FHGdPdIf5CCLqAaxnRrWidyoQlNkMIIVwbKB8Q== + dependencies: + growly "^1.3.0" + is-wsl "^1.1.0" + semver "^5.5.0" + shellwords "^0.1.1" + which "^1.3.0" + +node-releases@^1.1.53: + version "1.1.53" + resolved "https://registry.yarnpkg.com/node-releases/-/node-releases-1.1.53.tgz#2d821bfa499ed7c5dffc5e2f28c88e78a08ee3f4" + integrity sha512-wp8zyQVwef2hpZ/dJH7SfSrIPD6YoJz6BDQDpGEkcA0s3LpAQoxBIYmfIq6QAhC1DhwsyCgTaTTcONwX8qzCuQ== + +normalize-package-data@^2.3.2: + version "2.5.0" + resolved "https://registry.yarnpkg.com/normalize-package-data/-/normalize-package-data-2.5.0.tgz#e66db1838b200c1dfc233225d12cb36520e234a8" + integrity sha512-/5CMN3T0R4XTj4DcGaexo+roZSdSFW/0AOOTROrjxzCG1wrWXEsGbRKevjlIL+ZDE4sZlJr5ED4YW0yqmkK+eA== + dependencies: + hosted-git-info "^2.1.4" + resolve "^1.10.0" + semver "2 || 3 || 4 || 5" + validate-npm-package-license "^3.0.1" + +normalize-path@^2.1.1: + version "2.1.1" + resolved "https://registry.yarnpkg.com/normalize-path/-/normalize-path-2.1.1.tgz#1ab28b556e198363a8c1a6f7e6fa20137fe6aed9" + integrity sha1-GrKLVW4Zg2Oowab35vogE3/mrtk= + dependencies: + remove-trailing-separator "^1.0.1" + +normalize-path@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/normalize-path/-/normalize-path-3.0.0.tgz#0dcd69ff23a1c9b11fd0978316644a0388216a65" + integrity sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA== + +npm-run-path@^2.0.0: + version "2.0.2" + resolved "https://registry.yarnpkg.com/npm-run-path/-/npm-run-path-2.0.2.tgz#35a9232dfa35d7067b4cb2ddf2357b1871536c5f" + integrity sha1-NakjLfo11wZ7TLLd8jV7GHFTbF8= + dependencies: + path-key "^2.0.0" + +nwsapi@^2.0.7: + version "2.2.0" + resolved "https://registry.yarnpkg.com/nwsapi/-/nwsapi-2.2.0.tgz#204879a9e3d068ff2a55139c2c772780681a38b7" + integrity sha512-h2AatdwYH+JHiZpv7pt/gSX1XoRGb7L/qSIeuqA6GwYoF9w1vP1cw42TO0aI2pNyshRK5893hNSl+1//vHK7hQ== + +oauth-sign@~0.9.0: + version "0.9.0" + resolved "https://registry.yarnpkg.com/oauth-sign/-/oauth-sign-0.9.0.tgz#47a7b016baa68b5fa0ecf3dee08a85c679ac6455" + integrity sha512-fexhUFFPTGV8ybAtSIGbV6gOkSv8UtRbDBnAyLQw4QPKkgNlsH2ByPGtMUqdWkos6YCRmAqViwgZrJc/mRDzZQ== + +object-copy@^0.1.0: + version "0.1.0" + resolved "https://registry.yarnpkg.com/object-copy/-/object-copy-0.1.0.tgz#7e7d858b781bd7c991a41ba975ed3812754e998c" + integrity sha1-fn2Fi3gb18mRpBupde04EnVOmYw= + dependencies: + copy-descriptor "^0.1.0" + define-property "^0.2.5" + kind-of "^3.0.3" + +object-inspect@^1.7.0: + version "1.7.0" + resolved "https://registry.yarnpkg.com/object-inspect/-/object-inspect-1.7.0.tgz#f4f6bd181ad77f006b5ece60bd0b6f398ff74a67" + integrity sha512-a7pEHdh1xKIAgTySUGgLMx/xwDZskN1Ud6egYYN3EdRW4ZMPNEDUTF+hwy2LUC+Bl+SyLXANnwz/jyh/qutKUw== + +object-keys@^1.0.11, object-keys@^1.0.12, object-keys@^1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/object-keys/-/object-keys-1.1.1.tgz#1c47f272df277f3b1daf061677d9c82e2322c60e" + integrity sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA== + +object-visit@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/object-visit/-/object-visit-1.0.1.tgz#f79c4493af0c5377b59fe39d395e41042dd045bb" + integrity sha1-95xEk68MU3e1n+OdOV5BBC3QRbs= + dependencies: + isobject "^3.0.0" + +object.assign@^4.1.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/object.assign/-/object.assign-4.1.0.tgz#968bf1100d7956bb3ca086f006f846b3bc4008da" + integrity sha512-exHJeq6kBKj58mqGyTQ9DFvrZC/eR6OwxzoM9YRoGBqrXYonaFyGiFMuc9VZrXf7DarreEwMpurG3dd+CNyW5w== + dependencies: + define-properties "^1.1.2" + function-bind "^1.1.1" + has-symbols "^1.0.0" + object-keys "^1.0.11" + +object.entries@^1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/object.entries/-/object.entries-1.1.1.tgz#ee1cf04153de02bb093fec33683900f57ce5399b" + integrity sha512-ilqR7BgdyZetJutmDPfXCDffGa0/Yzl2ivVNpbx/g4UeWrCdRnFDUBrKJGLhGieRHDATnyZXWBeCb29k9CJysQ== + dependencies: + define-properties "^1.1.3" + es-abstract "^1.17.0-next.1" + function-bind "^1.1.1" + has "^1.0.3" + +object.getownpropertydescriptors@^2.0.3, object.getownpropertydescriptors@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/object.getownpropertydescriptors/-/object.getownpropertydescriptors-2.1.0.tgz#369bf1f9592d8ab89d712dced5cb81c7c5352649" + integrity sha512-Z53Oah9A3TdLoblT7VKJaTDdXdT+lQO+cNpKVnya5JDe9uLvzu1YyY1yFDFrcxrlRgWrEFH0jJtD/IbuwjcEVg== + dependencies: + define-properties "^1.1.3" + es-abstract "^1.17.0-next.1" + +object.pick@^1.3.0: + version "1.3.0" + resolved "https://registry.yarnpkg.com/object.pick/-/object.pick-1.3.0.tgz#87a10ac4c1694bd2e1cbf53591a66141fb5dd747" + integrity sha1-h6EKxMFpS9Lhy/U1kaZhQftd10c= + dependencies: + isobject "^3.0.1" + +object.values@^1.1.0: + version "1.1.1" + resolved "https://registry.yarnpkg.com/object.values/-/object.values-1.1.1.tgz#68a99ecde356b7e9295a3c5e0ce31dc8c953de5e" + integrity sha512-WTa54g2K8iu0kmS/us18jEmdv1a4Wi//BZ/DTVYEcH0XhLM5NYdpDHja3gt57VrZLcNAO2WGA+KpWsDBaHt6eA== + dependencies: + define-properties "^1.1.3" + es-abstract "^1.17.0-next.1" + function-bind "^1.1.1" + has "^1.0.3" + +once@^1.3.0, once@^1.3.1, once@^1.4.0: + version "1.4.0" + resolved "https://registry.yarnpkg.com/once/-/once-1.4.0.tgz#583b1aa775961d4b113ac17d9c50baef9dd76bd1" + integrity sha1-WDsap3WWHUsROsF9nFC6753Xa9E= + dependencies: + wrappy "1" + +onetime@^5.1.0: + version "5.1.0" + resolved "https://registry.yarnpkg.com/onetime/-/onetime-5.1.0.tgz#fff0f3c91617fe62bb50189636e99ac8a6df7be5" + integrity sha512-5NcSkPHhwTVFIQN+TUqXoS5+dlElHXdpAWu9I0HP20YOtIi+aZ0Ct82jdlILDxjLEAWwvm+qj1m6aEtsDVmm6Q== + dependencies: + mimic-fn "^2.1.0" + +optionator@^0.8.1, optionator@^0.8.3: + version "0.8.3" + resolved "https://registry.yarnpkg.com/optionator/-/optionator-0.8.3.tgz#84fa1d036fe9d3c7e21d99884b601167ec8fb495" + integrity sha512-+IW9pACdk3XWmmTXG8m3upGUJst5XRGzxMRjXzAuJ1XnIFNvfhjjIuYkDvysnPQ7qzqVzLt78BCruntqRhWQbA== + dependencies: + deep-is "~0.1.3" + fast-levenshtein "~2.0.6" + levn "~0.3.0" + prelude-ls "~1.1.2" + type-check "~0.3.2" + word-wrap "~1.2.3" + +os-tmpdir@~1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/os-tmpdir/-/os-tmpdir-1.0.2.tgz#bbe67406c79aa85c5cfec766fe5734555dfa1274" + integrity sha1-u+Z0BseaqFxc/sdm/lc0VV36EnQ= + +p-each-series@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/p-each-series/-/p-each-series-1.0.0.tgz#930f3d12dd1f50e7434457a22cd6f04ac6ad7f71" + integrity sha1-kw89Et0fUOdDRFeiLNbwSsatf3E= + dependencies: + p-reduce "^1.0.0" + +p-finally@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/p-finally/-/p-finally-1.0.0.tgz#3fbcfb15b899a44123b34b6dcc18b724336a2cae" + integrity sha1-P7z7FbiZpEEjs0ttzBi3JDNqLK4= + +p-limit@^1.1.0: + version "1.3.0" + resolved "https://registry.yarnpkg.com/p-limit/-/p-limit-1.3.0.tgz#b86bd5f0c25690911c7590fcbfc2010d54b3ccb8" + integrity sha512-vvcXsLAJ9Dr5rQOPk7toZQZJApBl2K4J6dANSsEuh6QI41JYcsS/qhTGa9ErIUUgK3WNQoJYvylxvjqmiqEA9Q== + dependencies: + p-try "^1.0.0" + +p-limit@^2.0.0: + version "2.3.0" + resolved "https://registry.yarnpkg.com/p-limit/-/p-limit-2.3.0.tgz#3dd33c647a214fdfffd835933eb086da0dc21db1" + integrity sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w== + dependencies: + p-try "^2.0.0" + +p-locate@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/p-locate/-/p-locate-2.0.0.tgz#20a0103b222a70c8fd39cc2e580680f3dde5ec43" + integrity sha1-IKAQOyIqcMj9OcwuWAaA893l7EM= + dependencies: + p-limit "^1.1.0" + +p-locate@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/p-locate/-/p-locate-3.0.0.tgz#322d69a05c0264b25997d9f40cd8a891ab0064a4" + integrity sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ== + dependencies: + p-limit "^2.0.0" + +p-reduce@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/p-reduce/-/p-reduce-1.0.0.tgz#18c2b0dd936a4690a529f8231f58a0fdb6a47dfa" + integrity sha1-GMKw3ZNqRpClKfgjH1ig/bakffo= + +p-try@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/p-try/-/p-try-1.0.0.tgz#cbc79cdbaf8fd4228e13f621f2b1a237c1b207b3" + integrity sha1-y8ec26+P1CKOE/Yh8rGiN8GyB7M= + +p-try@^2.0.0: + version "2.2.0" + resolved "https://registry.yarnpkg.com/p-try/-/p-try-2.2.0.tgz#cb2868540e313d61de58fafbe35ce9004d5540e6" + integrity sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ== + +parent-module@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/parent-module/-/parent-module-1.0.1.tgz#691d2709e78c79fae3a156622452d00762caaaa2" + integrity sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g== + dependencies: + callsites "^3.0.0" + +parse-json@^2.2.0: + version "2.2.0" + resolved "https://registry.yarnpkg.com/parse-json/-/parse-json-2.2.0.tgz#f480f40434ef80741f8469099f8dea18f55a4dc9" + integrity sha1-9ID0BDTvgHQfhGkJn43qGPVaTck= + dependencies: + error-ex "^1.2.0" + +parse-json@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/parse-json/-/parse-json-4.0.0.tgz#be35f5425be1f7f6c747184f98a788cb99477ee0" + integrity sha1-vjX1Qlvh9/bHRxhPmKeIy5lHfuA= + dependencies: + error-ex "^1.3.1" + json-parse-better-errors "^1.0.1" + +parse-passwd@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/parse-passwd/-/parse-passwd-1.0.0.tgz#6d5b934a456993b23d37f40a382d6f1666a8e5c6" + integrity sha1-bVuTSkVpk7I9N/QKOC1vFmao5cY= + +parse5@4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/parse5/-/parse5-4.0.0.tgz#6d78656e3da8d78b4ec0b906f7c08ef1dfe3f608" + integrity sha512-VrZ7eOd3T1Fk4XWNXMgiGBK/z0MG48BWG2uQNU4I72fkQuKUTZpl+u9k+CxEG0twMVzSmXEEz12z5Fnw1jIQFA== + +pascalcase@^0.1.1: + version "0.1.1" + resolved "https://registry.yarnpkg.com/pascalcase/-/pascalcase-0.1.1.tgz#b363e55e8006ca6fe21784d2db22bd15d7917f14" + integrity sha1-s2PlXoAGym/iF4TS2yK9FdeRfxQ= + +path-dirname@^1.0.0: + version "1.0.2" + resolved "https://registry.yarnpkg.com/path-dirname/-/path-dirname-1.0.2.tgz#cc33d24d525e099a5388c0336c6e32b9160609e0" + integrity sha1-zDPSTVJeCZpTiMAzbG4yuRYGCeA= + +path-exists@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/path-exists/-/path-exists-3.0.0.tgz#ce0ebeaa5f78cb18925ea7d810d7b59b010fd515" + integrity sha1-zg6+ql94yxiSXqfYENe1mwEP1RU= + +path-is-absolute@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/path-is-absolute/-/path-is-absolute-1.0.1.tgz#174b9268735534ffbc7ace6bf53a5a9e1b5c5f5f" + integrity sha1-F0uSaHNVNP+8es5r9TpanhtcX18= + +path-key@^2.0.0, path-key@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/path-key/-/path-key-2.0.1.tgz#411cadb574c5a140d3a4b1910d40d80cc9f40b40" + integrity sha1-QRyttXTFoUDTpLGRDUDYDMn0C0A= + +path-parse@^1.0.6: + version "1.0.6" + resolved "https://registry.yarnpkg.com/path-parse/-/path-parse-1.0.6.tgz#d62dbb5679405d72c4737ec58600e9ddcf06d24c" + integrity sha512-GSmOT2EbHrINBf9SR7CDELwlJ8AENk3Qn7OikK4nFYAu3Ote2+JYNVvkpAEQm3/TLNEJFD/xZJjzyxg3KBWOzw== + +path-type@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/path-type/-/path-type-2.0.0.tgz#f012ccb8415b7096fc2daa1054c3d72389594c73" + integrity sha1-8BLMuEFbcJb8LaoQVMPXI4lZTHM= + dependencies: + pify "^2.0.0" + +path-type@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/path-type/-/path-type-3.0.0.tgz#cef31dc8e0a1a3bb0d105c0cd97cf3bf47f4e36f" + integrity sha512-T2ZUsdZFHgA3u4e5PfPbjd7HDDpxPnQb5jN0SrDsjNSuVXHJqtwTnWqG0B1jZrgmJ/7lj1EmVIByWt1gxGkWvg== + dependencies: + pify "^3.0.0" + +path@^0.12.7: + version "0.12.7" + resolved "https://registry.yarnpkg.com/path/-/path-0.12.7.tgz#d4dc2a506c4ce2197eb481ebfcd5b36c0140b10f" + integrity sha1-1NwqUGxM4hl+tIHr/NWzbAFAsQ8= + dependencies: + process "^0.11.1" + util "^0.10.3" + +performance-now@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/performance-now/-/performance-now-2.1.0.tgz#6309f4e0e5fa913ec1c69307ae364b4b377c9e7b" + integrity sha1-Ywn04OX6kT7BxpMHrjZLSzd8nns= + +pify@^2.0.0: + version "2.3.0" + resolved "https://registry.yarnpkg.com/pify/-/pify-2.3.0.tgz#ed141a6ac043a849ea588498e7dca8b15330e90c" + integrity sha1-7RQaasBDqEnqWISY59yosVMw6Qw= + +pify@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/pify/-/pify-3.0.0.tgz#e5a4acd2c101fdf3d9a4d07f0dbc4db49dd28176" + integrity sha1-5aSs0sEB/fPZpNB/DbxNtJ3SgXY= + +pify@^4.0.1: + version "4.0.1" + resolved "https://registry.yarnpkg.com/pify/-/pify-4.0.1.tgz#4b2cd25c50d598735c50292224fd8c6df41e3231" + integrity sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g== + +pirates@^4.0.0, pirates@^4.0.1: + version "4.0.1" + resolved "https://registry.yarnpkg.com/pirates/-/pirates-4.0.1.tgz#643a92caf894566f91b2b986d2c66950a8e2fb87" + integrity sha512-WuNqLTbMI3tmfef2TKxlQmAiLHKtFhlsCZnPIpuv2Ow0RDVO8lfy1Opf4NUzlMXLjPl+Men7AuVdX6TA+s+uGA== + dependencies: + node-modules-regexp "^1.0.0" + +pkg-dir@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/pkg-dir/-/pkg-dir-2.0.0.tgz#f6d5d1109e19d63edf428e0bd57e12777615334b" + integrity sha1-9tXREJ4Z1j7fQo4L1X4Sd3YVM0s= + dependencies: + find-up "^2.1.0" + +pkg-dir@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/pkg-dir/-/pkg-dir-3.0.0.tgz#2749020f239ed990881b1f71210d51eb6523bea3" + integrity sha512-/E57AYkoeQ25qkxMj5PBOVgF8Kiu/h7cYS30Z5+R7WaiCCBfLq58ZI/dSeaEKb9WVJV5n/03QwrN3IeWIFllvw== + dependencies: + find-up "^3.0.0" + +pkg-up@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/pkg-up/-/pkg-up-2.0.0.tgz#c819ac728059a461cab1c3889a2be3c49a004d7f" + integrity sha1-yBmscoBZpGHKscOImivjxJoATX8= + dependencies: + find-up "^2.1.0" + +pn@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/pn/-/pn-1.1.0.tgz#e2f4cef0e219f463c179ab37463e4e1ecdccbafb" + integrity sha512-2qHaIQr2VLRFoxe2nASzsV6ef4yOOH+Fi9FBOVH6cqeSgUnoyySPZkxzLuzd+RYOQTRpROA0ztTMqxROKSb/nA== + +pngjs@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/pngjs/-/pngjs-5.0.0.tgz#e79dd2b215767fd9c04561c01236df960bce7fbb" + integrity sha512-40QW5YalBNfQo5yRYmiw7Yz6TKKVr3h6970B2YE+3fQpsWcrbj1PzJgxeJ19DRQjhMbKPIuMY8rFaXc8moolVw== + +posix-character-classes@^0.1.0: + version "0.1.1" + resolved "https://registry.yarnpkg.com/posix-character-classes/-/posix-character-classes-0.1.1.tgz#01eac0fe3b5af71a2a6c02feabb8c1fef7e00eab" + integrity sha1-AerA/jta9xoqbAL+q7jB/vfgDqs= + +prelude-ls@~1.1.2: + version "1.1.2" + resolved "https://registry.yarnpkg.com/prelude-ls/-/prelude-ls-1.1.2.tgz#21932a549f5e52ffd9a827f570e04be62a97da54" + integrity sha1-IZMqVJ9eUv/ZqCf1cOBL5iqX2lQ= + +prettier-linter-helpers@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/prettier-linter-helpers/-/prettier-linter-helpers-1.0.0.tgz#d23d41fe1375646de2d0104d3454a3008802cf7b" + integrity sha512-GbK2cP9nraSSUF9N2XwUwqfzlAFlMNYYl+ShE/V+H8a9uNl/oUqB1w2EL54Jh0OlyRSd8RfWYJ3coVS4TROP2w== + dependencies: + fast-diff "^1.1.2" + +pretty-format@^24.9.0: + version "24.9.0" + resolved "https://registry.yarnpkg.com/pretty-format/-/pretty-format-24.9.0.tgz#12fac31b37019a4eea3c11aa9a959eb7628aa7c9" + integrity sha512-00ZMZUiHaJrNfk33guavqgvfJS30sLYf0f8+Srklv0AMPodGGHcoHgksZ3OThYnIvOd+8yMCn0YiEOogjlgsnA== + dependencies: + "@jest/types" "^24.9.0" + ansi-regex "^4.0.0" + ansi-styles "^3.2.0" + react-is "^16.8.4" + +private@^0.1.8: + version "0.1.8" + resolved "https://registry.yarnpkg.com/private/-/private-0.1.8.tgz#2381edb3689f7a53d653190060fcf822d2f368ff" + integrity sha512-VvivMrbvd2nKkiG38qjULzlc+4Vx4wm/whI9pQD35YrARNnhxeiRktSOhSukRLFNlzg6Br/cJPet5J/u19r/mg== + +process-nextick-args@~2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/process-nextick-args/-/process-nextick-args-2.0.1.tgz#7820d9b16120cc55ca9ae7792680ae7dba6d7fe2" + integrity sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag== + +process@^0.11.1: + version "0.11.10" + resolved "https://registry.yarnpkg.com/process/-/process-0.11.10.tgz#7332300e840161bda3e69a1d1d91a7d4bc16f182" + integrity sha1-czIwDoQBYb2j5podHZGn1LwW8YI= + +progress@^2.0.0: + version "2.0.3" + resolved "https://registry.yarnpkg.com/progress/-/progress-2.0.3.tgz#7e8cf8d8f5b8f239c1bc68beb4eb78567d572ef8" + integrity sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA== + +prompts@^2.0.1: + version "2.3.2" + resolved "https://registry.yarnpkg.com/prompts/-/prompts-2.3.2.tgz#480572d89ecf39566d2bd3fe2c9fccb7c4c0b068" + integrity sha512-Q06uKs2CkNYVID0VqwfAl9mipo99zkBv/n2JtWY89Yxa3ZabWSrs0e2KTudKVa3peLUvYXMefDqIleLPVUBZMA== + dependencies: + kleur "^3.0.3" + sisteransi "^1.0.4" + +psl@^1.1.28: + version "1.8.0" + resolved "https://registry.yarnpkg.com/psl/-/psl-1.8.0.tgz#9326f8bcfb013adcc005fdff056acce020e51c24" + integrity sha512-RIdOzyoavK+hA18OGGWDqUTsCLhtA7IcZ/6NCs4fFJaHBDab+pDDmDIByWFRQJq2Cd7r1OoQxBGKOaztq+hjIQ== + +pump@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/pump/-/pump-3.0.0.tgz#b4a2116815bde2f4e1ea602354e8c75565107a64" + integrity sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww== + dependencies: + end-of-stream "^1.1.0" + once "^1.3.1" + +punycode@^2.1.0, punycode@^2.1.1: + version "2.1.1" + resolved "https://registry.yarnpkg.com/punycode/-/punycode-2.1.1.tgz#b58b010ac40c22c5657616c8d2c2c02c7bf479ec" + integrity sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A== + +qs@~6.5.2: + version "6.5.2" + resolved "https://registry.yarnpkg.com/qs/-/qs-6.5.2.tgz#cb3ae806e8740444584ef154ce8ee98d403f3e36" + integrity sha512-N5ZAX4/LxJmF+7wN74pUD6qAh9/wnvdQcjq9TZjevvXzSUo7bfmw91saqMjzGS2xq91/odN2dW/WOl7qQHNDGA== + +randomstring@^1.1.5: + version "1.1.5" + resolved "https://registry.yarnpkg.com/randomstring/-/randomstring-1.1.5.tgz#6df0628f75cbd5932930d9fe3ab4e956a18518c3" + integrity sha1-bfBij3XL1ZMpMNn+OrTpVqGFGMM= + dependencies: + array-uniq "1.0.2" + +react-is@^16.8.4: + version "16.13.1" + resolved "https://registry.yarnpkg.com/react-is/-/react-is-16.13.1.tgz#789729a4dc36de2999dc156dd6c1d9c18cea56a4" + integrity sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ== + +read-pkg-up@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/read-pkg-up/-/read-pkg-up-2.0.0.tgz#6b72a8048984e0c41e79510fd5e9fa99b3b549be" + integrity sha1-a3KoBImE4MQeeVEP1en6mbO1Sb4= + dependencies: + find-up "^2.0.0" + read-pkg "^2.0.0" + +read-pkg-up@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/read-pkg-up/-/read-pkg-up-4.0.0.tgz#1b221c6088ba7799601c808f91161c66e58f8978" + integrity sha512-6etQSH7nJGsK0RbG/2TeDzZFa8shjQ1um+SwQQ5cwKy0dhSXdOncEhb1CPpvQG4h7FyOV6EB6YlV0yJvZQNAkA== + dependencies: + find-up "^3.0.0" + read-pkg "^3.0.0" + +read-pkg@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/read-pkg/-/read-pkg-2.0.0.tgz#8ef1c0623c6a6db0dc6713c4bfac46332b2368f8" + integrity sha1-jvHAYjxqbbDcZxPEv6xGMysjaPg= + dependencies: + load-json-file "^2.0.0" + normalize-package-data "^2.3.2" + path-type "^2.0.0" + +read-pkg@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/read-pkg/-/read-pkg-3.0.0.tgz#9cbc686978fee65d16c00e2b19c237fcf6e38389" + integrity sha1-nLxoaXj+5l0WwA4rGcI3/Pbjg4k= + dependencies: + load-json-file "^4.0.0" + normalize-package-data "^2.3.2" + path-type "^3.0.0" + +readable-stream@^2.0.2: + version "2.3.7" + resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-2.3.7.tgz#1eca1cf711aef814c04f62252a36a62f6cb23b57" + integrity sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw== + dependencies: + core-util-is "~1.0.0" + inherits "~2.0.3" + isarray "~1.0.0" + process-nextick-args "~2.0.0" + safe-buffer "~5.1.1" + string_decoder "~1.1.1" + util-deprecate "~1.0.1" + +readable-stream@^3.0.2, readable-stream@^3.1.1, readable-stream@^3.4.0, readable-stream@^3.5.0, readable-stream@^3.6.0: + version "3.6.0" + resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-3.6.0.tgz#337bbda3adc0706bd3e024426a286d4b4b2c9198" + integrity sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA== + dependencies: + inherits "^2.0.3" + string_decoder "^1.1.1" + util-deprecate "^1.0.1" + +readdirp@^2.2.1: + version "2.2.1" + resolved "https://registry.yarnpkg.com/readdirp/-/readdirp-2.2.1.tgz#0e87622a3325aa33e892285caf8b4e846529a525" + integrity sha512-1JU/8q+VgFZyxwrJ+SVIOsh+KywWGpds3NTqikiKpDMZWScmAYyKIgqkO+ARvNWJfXeXR1zxz7aHF4u4CyH6vQ== + dependencies: + graceful-fs "^4.1.11" + micromatch "^3.1.10" + readable-stream "^2.0.2" + +realpath-native@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/realpath-native/-/realpath-native-1.1.0.tgz#2003294fea23fb0672f2476ebe22fcf498a2d65c" + integrity sha512-wlgPA6cCIIg9gKz0fgAPjnzh4yR/LnXovwuo9hvyGvx3h8nX4+/iLZplfUWasXpqD8BdnGnP5njOFjkUwPzvjA== + dependencies: + util.promisify "^1.0.0" + +regenerate-unicode-properties@^8.2.0: + version "8.2.0" + resolved "https://registry.yarnpkg.com/regenerate-unicode-properties/-/regenerate-unicode-properties-8.2.0.tgz#e5de7111d655e7ba60c057dbe9ff37c87e65cdec" + integrity sha512-F9DjY1vKLo/tPePDycuH3dn9H1OTPIkVD9Kz4LODu+F2C75mgjAJ7x/gwy6ZcSNRAAkhNlJSOHRe8k3p+K9WhA== + dependencies: + regenerate "^1.4.0" + +regenerate@^1.4.0: + version "1.4.0" + resolved "https://registry.yarnpkg.com/regenerate/-/regenerate-1.4.0.tgz#4a856ec4b56e4077c557589cae85e7a4c8869a11" + integrity sha512-1G6jJVDWrt0rK99kBjvEtziZNCICAuvIPkSiUFIQxVP06RCVpq3dmDo2oi6ABpYaDYaTRr67BEhL8r1wgEZZKg== + +regenerator-runtime@^0.13.4: + version "0.13.5" + resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.13.5.tgz#d878a1d094b4306d10b9096484b33ebd55e26697" + integrity sha512-ZS5w8CpKFinUzOwW3c83oPeVXoNsrLsaCoLtJvAClH135j/R77RuymhiSErhm2lKcwSCIpmvIWSbDkIfAqKQlA== + +regenerator-transform@^0.14.2: + version "0.14.4" + resolved "https://registry.yarnpkg.com/regenerator-transform/-/regenerator-transform-0.14.4.tgz#5266857896518d1616a78a0479337a30ea974cc7" + integrity sha512-EaJaKPBI9GvKpvUz2mz4fhx7WPgvwRLY9v3hlNHWmAuJHI13T4nwKnNvm5RWJzEdnI5g5UwtOww+S8IdoUC2bw== + dependencies: + "@babel/runtime" "^7.8.4" + private "^0.1.8" + +regex-not@^1.0.0, regex-not@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/regex-not/-/regex-not-1.0.2.tgz#1f4ece27e00b0b65e0247a6810e6a85d83a5752c" + integrity sha512-J6SDjUgDxQj5NusnOtdFxDwN/+HWykR8GELwctJ7mdqhcyy1xEc4SRFHUXvxTp661YaVKAjfRLZ9cCqS6tn32A== + dependencies: + extend-shallow "^3.0.2" + safe-regex "^1.1.0" + +regexpp@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/regexpp/-/regexpp-2.0.1.tgz#8d19d31cf632482b589049f8281f93dbcba4d07f" + integrity sha512-lv0M6+TkDVniA3aD1Eg0DVpfU/booSu7Eev3TDO/mZKHBfVjgCGTV4t4buppESEYDtkArYFOxTJWv6S5C+iaNw== + +regexpu-core@^4.7.0: + version "4.7.0" + resolved "https://registry.yarnpkg.com/regexpu-core/-/regexpu-core-4.7.0.tgz#fcbf458c50431b0bb7b45d6967b8192d91f3d938" + integrity sha512-TQ4KXRnIn6tz6tjnrXEkD/sshygKH/j5KzK86X8MkeHyZ8qst/LZ89j3X4/8HEIfHANTFIP/AbXakeRhWIl5YQ== + dependencies: + regenerate "^1.4.0" + regenerate-unicode-properties "^8.2.0" + regjsgen "^0.5.1" + regjsparser "^0.6.4" + unicode-match-property-ecmascript "^1.0.4" + unicode-match-property-value-ecmascript "^1.2.0" + +regjsgen@^0.5.1: + version "0.5.1" + resolved "https://registry.yarnpkg.com/regjsgen/-/regjsgen-0.5.1.tgz#48f0bf1a5ea205196929c0d9798b42d1ed98443c" + integrity sha512-5qxzGZjDs9w4tzT3TPhCJqWdCc3RLYwy9J2NB0nm5Lz+S273lvWcpjaTGHsT1dc6Hhfq41uSEOw8wBmxrKOuyg== + +regjsparser@^0.6.4: + version "0.6.4" + resolved "https://registry.yarnpkg.com/regjsparser/-/regjsparser-0.6.4.tgz#a769f8684308401a66e9b529d2436ff4d0666272" + integrity sha512-64O87/dPDgfk8/RQqC4gkZoGyyWFIEUTTh80CU6CWuK5vkCGyekIx+oKcEIYtP/RAxSQltCZHCNu/mdd7fqlJw== + dependencies: + jsesc "~0.5.0" + +remove-trailing-separator@^1.0.1: + version "1.1.0" + resolved "https://registry.yarnpkg.com/remove-trailing-separator/-/remove-trailing-separator-1.1.0.tgz#c24bce2a283adad5bc3f58e0d48249b92379d8ef" + integrity sha1-wkvOKig62tW8P1jg1IJJuSN52O8= + +repeat-element@^1.1.2: + version "1.1.3" + resolved "https://registry.yarnpkg.com/repeat-element/-/repeat-element-1.1.3.tgz#782e0d825c0c5a3bb39731f84efee6b742e6b1ce" + integrity sha512-ahGq0ZnV5m5XtZLMb+vP76kcAM5nkLqk0lpqAuojSKGgQtn4eRi4ZZGm2olo2zKFH+sMsWaqOCW1dqAnOru72g== + +repeat-string@^1.6.1: + version "1.6.1" + resolved "https://registry.yarnpkg.com/repeat-string/-/repeat-string-1.6.1.tgz#8dcae470e1c88abc2d600fff4a776286da75e637" + integrity sha1-jcrkcOHIirwtYA//Sndihtp15jc= + +request-promise-core@1.1.3: + version "1.1.3" + resolved "https://registry.yarnpkg.com/request-promise-core/-/request-promise-core-1.1.3.tgz#e9a3c081b51380dfea677336061fea879a829ee9" + integrity sha512-QIs2+ArIGQVp5ZYbWD5ZLCY29D5CfWizP8eWnm8FoGD1TX61veauETVQbrV60662V0oFBkrDOuaBI8XgtuyYAQ== + dependencies: + lodash "^4.17.15" + +request-promise-native@^1.0.5: + version "1.0.8" + resolved "https://registry.yarnpkg.com/request-promise-native/-/request-promise-native-1.0.8.tgz#a455b960b826e44e2bf8999af64dff2bfe58cb36" + integrity sha512-dapwLGqkHtwL5AEbfenuzjTYg35Jd6KPytsC2/TLkVMz8rm+tNt72MGUWT1RP/aYawMpN6HqbNGBQaRcBtjQMQ== + dependencies: + request-promise-core "1.1.3" + stealthy-require "^1.1.1" + tough-cookie "^2.3.3" + +request@^2.87.0: + version "2.88.2" + resolved "https://registry.yarnpkg.com/request/-/request-2.88.2.tgz#d73c918731cb5a87da047e207234146f664d12b3" + integrity sha512-MsvtOrfG9ZcrOwAW+Qi+F6HbD0CWXEh9ou77uOb7FM2WPhwT7smM833PzanhJLsgXjN89Ir6V2PczXNnMpwKhw== + dependencies: + aws-sign2 "~0.7.0" + aws4 "^1.8.0" + caseless "~0.12.0" + combined-stream "~1.0.6" + extend "~3.0.2" + forever-agent "~0.6.1" + form-data "~2.3.2" + har-validator "~5.1.3" + http-signature "~1.2.0" + is-typedarray "~1.0.0" + isstream "~0.1.2" + json-stringify-safe "~5.0.1" + mime-types "~2.1.19" + oauth-sign "~0.9.0" + performance-now "^2.1.0" + qs "~6.5.2" + safe-buffer "^5.1.2" + tough-cookie "~2.5.0" + tunnel-agent "^0.6.0" + uuid "^3.3.2" + +require-directory@^2.1.1: + version "2.1.1" + resolved "https://registry.yarnpkg.com/require-directory/-/require-directory-2.1.1.tgz#8c64ad5fd30dab1c976e2344ffe7f792a6a6df42" + integrity sha1-jGStX9MNqxyXbiNE/+f3kqam30I= + +require-main-filename@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/require-main-filename/-/require-main-filename-2.0.0.tgz#d0b329ecc7cc0f61649f62215be69af54aa8989b" + integrity sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg== + +resolve-cwd@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/resolve-cwd/-/resolve-cwd-2.0.0.tgz#00a9f7387556e27038eae232caa372a6a59b665a" + integrity sha1-AKn3OHVW4nA46uIyyqNypqWbZlo= + dependencies: + resolve-from "^3.0.0" + +resolve-from@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/resolve-from/-/resolve-from-3.0.0.tgz#b22c7af7d9d6881bc8b6e653335eebcb0a188748" + integrity sha1-six699nWiBvItuZTM17rywoYh0g= + +resolve-from@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/resolve-from/-/resolve-from-4.0.0.tgz#4abcd852ad32dd7baabfe9b40e00a36db5f392e6" + integrity sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g== + +resolve-url@^0.2.1: + version "0.2.1" + resolved "https://registry.yarnpkg.com/resolve-url/-/resolve-url-0.2.1.tgz#2c637fe77c893afd2a663fe21aa9080068e2052a" + integrity sha1-LGN/53yJOv0qZj/iGqkIAGjiBSo= + +resolve@1.1.7: + version "1.1.7" + resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.1.7.tgz#203114d82ad2c5ed9e8e0411b3932875e889e97b" + integrity sha1-IDEU2CrSxe2ejgQRs5ModeiJ6Xs= + +resolve@^1.10.0, resolve@^1.12.0, resolve@^1.13.1, resolve@^1.3.2, resolve@^1.8.1: + version "1.17.0" + resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.17.0.tgz#b25941b54968231cc2d1bb76a79cb7f2c0bf8444" + integrity sha512-ic+7JYiV8Vi2yzQGFWOkiZD5Z9z7O2Zhm9XMaTxdJExKasieFCr+yXZ/WmXsckHiKl12ar0y6XiXDx3m4RHn1w== + dependencies: + path-parse "^1.0.6" + +restore-cursor@^3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/restore-cursor/-/restore-cursor-3.1.0.tgz#39f67c54b3a7a58cea5236d95cf0034239631f7e" + integrity sha512-l+sSefzHpj5qimhFSE5a8nufZYAM3sBSVMAPtYkmC+4EH2anSGaEMXSD0izRQbu9nfyQ9y5JrVmp7E8oZrUjvA== + dependencies: + onetime "^5.1.0" + signal-exit "^3.0.2" + +ret@~0.1.10: + version "0.1.15" + resolved "https://registry.yarnpkg.com/ret/-/ret-0.1.15.tgz#b8a4825d5bdb1fc3f6f53c2bc33f81388681c7bc" + integrity sha512-TTlYpa+OL+vMMNG24xSlQGEJ3B/RzEfUlLct7b5G/ytav+wPrplCpVMFuwzXbkecJrb6IYo1iFb0S9v37754mg== + +rfb2@^0.2.2: + version "0.2.2" + resolved "https://registry.yarnpkg.com/rfb2/-/rfb2-0.2.2.tgz#f9444b8803e6a31848e57911ace562ce0fee5598" + integrity sha512-+Aw0oED0zsoNQYFE3FUsD+a/lm9y8YwdQaERlWHm7G5hey3tiSQGq7tfe5sFAw5fbN7Zms38bVEDxIUAOq3mRw== + +rimraf@2.6.3: + version "2.6.3" + resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-2.6.3.tgz#b2d104fe0d8fb27cf9e0a1cda8262dd3833c6cab" + integrity sha512-mwqeW5XsA2qAejG46gYdENaxXjx9onRNCfn7L0duuP4hCuTIi/QO7PDK07KJfp1d+izWPrzEJDcSqBa0OZQriA== + dependencies: + glob "^7.1.3" + +rimraf@^2.5.4, rimraf@^2.6.3: + version "2.7.1" + resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-2.7.1.tgz#35797f13a7fdadc566142c29d4f07ccad483e3ec" + integrity sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w== + dependencies: + glob "^7.1.3" + +ripemd160@^2.0.2: + version "2.0.2" + resolved "https://registry.yarnpkg.com/ripemd160/-/ripemd160-2.0.2.tgz#a1c1a6f624751577ba5d07914cbc92850585890c" + integrity sha512-ii4iagi25WusVoiC4B4lq7pbXfAp3D9v5CwfkY33vffw2+pkDjY1D8GaN7spsxvCSx8dkPqOZCEZyfxcmJG2IA== + dependencies: + hash-base "^3.0.0" + inherits "^2.0.1" + +rsvp@^4.8.4: + version "4.8.5" + resolved "https://registry.yarnpkg.com/rsvp/-/rsvp-4.8.5.tgz#c8f155311d167f68f21e168df71ec5b083113734" + integrity sha512-nfMOlASu9OnRJo1mbEk2cz0D56a1MBNrJ7orjRZQG10XDyuvwksKbuXNp6qa+kbn839HwjwhBzhFmdsaEAfauA== + +run-async@^2.4.0: + version "2.4.1" + resolved "https://registry.yarnpkg.com/run-async/-/run-async-2.4.1.tgz#8440eccf99ea3e70bd409d49aab88e10c189a455" + integrity sha512-tvVnVv01b8c1RrA6Ep7JkStj85Guv/YrMcwqYQnwjsAS2cTmmPGBBjAjpCW7RrSodNSoE2/qg9O4bceNvUuDgQ== + +rxjs@^6.5.3, rxjs@^6.5.5: + version "6.5.5" + resolved "https://registry.yarnpkg.com/rxjs/-/rxjs-6.5.5.tgz#c5c884e3094c8cfee31bf27eb87e54ccfc87f9ec" + integrity sha512-WfQI+1gohdf0Dai/Bbmk5L5ItH5tYqm3ki2c5GdWhKjalzjg93N3avFjVStyZZz+A2Em+ZxKH5bNghw9UeylGQ== + dependencies: + tslib "^1.9.0" + +safe-buffer@^5.0.1, safe-buffer@^5.1.2, safe-buffer@^5.2.0, safe-buffer@~5.2.0: + version "5.2.0" + resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.2.0.tgz#b74daec49b1148f88c64b68d49b1e815c1f2f519" + integrity sha512-fZEwUGbVl7kouZs1jCdMLdt95hdIv0ZeHg6L7qPeciMZhZ+/gdesW4wgTARkrFWEpspjEATAzUGPG8N2jJiwbg== + +safe-buffer@~5.1.0, safe-buffer@~5.1.1: + version "5.1.2" + resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.1.2.tgz#991ec69d296e0313747d59bdfd2b745c35f8828d" + integrity sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g== + +safe-regex@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/safe-regex/-/safe-regex-1.1.0.tgz#40a3669f3b077d1e943d44629e157dd48023bf2e" + integrity sha1-QKNmnzsHfR6UPURinhV91IAjvy4= + dependencies: + ret "~0.1.10" + +"safer-buffer@>= 2.1.2 < 3", safer-buffer@^2.0.2, safer-buffer@^2.1.0, safer-buffer@~2.1.0: + version "2.1.2" + resolved "https://registry.yarnpkg.com/safer-buffer/-/safer-buffer-2.1.2.tgz#44fa161b0187b9549dd84bb91802f9bd8385cd6a" + integrity sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg== + +sane@^4.0.3: + version "4.1.0" + resolved "https://registry.yarnpkg.com/sane/-/sane-4.1.0.tgz#ed881fd922733a6c461bc189dc2b6c006f3ffded" + integrity sha512-hhbzAgTIX8O7SHfp2c8/kREfEn4qO/9q8C9beyY6+tvZ87EpoZ3i1RIEvp27YBswnNbY9mWd6paKVmKbAgLfZA== + dependencies: + "@cnakazawa/watch" "^1.0.3" + anymatch "^2.0.0" + capture-exit "^2.0.0" + exec-sh "^0.3.2" + execa "^1.0.0" + fb-watchman "^2.0.0" + micromatch "^3.1.4" + minimist "^1.1.1" + walker "~1.0.5" + +sax@^1.2.4: + version "1.2.4" + resolved "https://registry.yarnpkg.com/sax/-/sax-1.2.4.tgz#2816234e2378bddc4e5354fab5caa895df7100d9" + integrity sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw== + +secp256k1@^4.0.1: + version "4.0.1" + resolved "https://registry.yarnpkg.com/secp256k1/-/secp256k1-4.0.1.tgz#b9570ca26ace9e74c3171512bba253da9c0b6d60" + integrity sha512-iGRjbGAKfXMqhtdkkuNxsgJQfJO8Oo78Rm7DAvsG3XKngq+nJIOGqrCSXcQqIVsmCj0wFanE5uTKFxV3T9j2wg== + dependencies: + elliptic "^6.5.2" + node-addon-api "^2.0.0" + node-gyp-build "^4.2.0" + +"semver@2 || 3 || 4 || 5", semver@^5.4.1, semver@^5.5.0, semver@^5.5.1, semver@^5.6.0, semver@^5.7.0: + version "5.7.1" + resolved "https://registry.yarnpkg.com/semver/-/semver-5.7.1.tgz#a954f931aeba508d307bbf069eff0c01c96116f7" + integrity sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ== + +semver@7.0.0: + version "7.0.0" + resolved "https://registry.yarnpkg.com/semver/-/semver-7.0.0.tgz#5f3ca35761e47e05b206c6daff2cf814f0316b8e" + integrity sha512-+GB6zVA9LWh6zovYQLALHwv5rb2PHGlJi3lfiqIHxR0uuwCgefcOJc59v9fv1w8GbStwxuuqqAjI9NMAOOgq1A== + +semver@^6.0.0, semver@^6.1.2, semver@^6.2.0, semver@^6.3.0: + version "6.3.0" + resolved "https://registry.yarnpkg.com/semver/-/semver-6.3.0.tgz#ee0a64c8af5e8ceea67687b133761e1becbd1d3d" + integrity sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw== + +set-blocking@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/set-blocking/-/set-blocking-2.0.0.tgz#045f9782d011ae9a6803ddd382b24392b3d890f7" + integrity sha1-BF+XgtARrppoA93TgrJDkrPYkPc= + +set-value@^2.0.0, set-value@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/set-value/-/set-value-2.0.1.tgz#a18d40530e6f07de4228c7defe4227af8cad005b" + integrity sha512-JxHc1weCN68wRY0fhCoXpyK55m/XPHafOmK4UWD7m2CI14GMcFypt4w/0+NV5f/ZMby2F6S2wwA7fgynh9gWSw== + dependencies: + extend-shallow "^2.0.1" + is-extendable "^0.1.1" + is-plain-object "^2.0.3" + split-string "^3.0.1" + +shebang-command@^1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/shebang-command/-/shebang-command-1.2.0.tgz#44aac65b695b03398968c39f363fee5deafdf1ea" + integrity sha1-RKrGW2lbAzmJaMOfNj/uXer98eo= + dependencies: + shebang-regex "^1.0.0" + +shebang-regex@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/shebang-regex/-/shebang-regex-1.0.0.tgz#da42f49740c0b42db2ca9728571cb190c98efea3" + integrity sha1-2kL0l0DAtC2yypcoVxyxkMmO/qM= + +shellwords@^0.1.1: + version "0.1.1" + resolved "https://registry.yarnpkg.com/shellwords/-/shellwords-0.1.1.tgz#d6b9181c1a48d397324c84871efbcfc73fc0654b" + integrity sha512-vFwSUfQvqybiICwZY5+DAWIPLKsWO31Q91JSKl3UYv+K5c2QRPzn0qzec6QPu1Qc9eHYItiP3NdJqNVqetYAww== + +signal-exit@^3.0.0, signal-exit@^3.0.2: + version "3.0.3" + resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-3.0.3.tgz#a1410c2edd8f077b08b4e253c8eacfcaf057461c" + integrity sha512-VUJ49FC8U1OxwZLxIbTTrDvLnf/6TDgxZcK8wxR8zs13xpx7xbG60ndBlhNrFi2EMuFRoeDoJO7wthSLq42EjA== + +sisteransi@^1.0.4: + version "1.0.5" + resolved "https://registry.yarnpkg.com/sisteransi/-/sisteransi-1.0.5.tgz#134d681297756437cc05ca01370d3a7a571075ed" + integrity sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg== + +slash@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/slash/-/slash-2.0.0.tgz#de552851a1759df3a8f206535442f5ec4ddeab44" + integrity sha512-ZYKh3Wh2z1PpEXWr0MpSBZ0V6mZHAQfYevttO11c51CaWjGTaadiKZ+wVt1PbMlDV5qhMFslpZCemhwOK7C89A== + +sleep@^6.1.0: + version "6.1.0" + resolved "https://registry.yarnpkg.com/sleep/-/sleep-6.1.0.tgz#5507b520556a82ffb983d39123c5459470fa2a9e" + integrity sha512-Z1x4JjJxsru75Tqn8F4tnOFeEu3HjtITTsumYUiuz54sGKdISgLCek9AUlXlVVrkhltRFhNUsJDJE76SFHTDIQ== + dependencies: + nan "^2.13.2" + +slice-ansi@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/slice-ansi/-/slice-ansi-2.1.0.tgz#cacd7693461a637a5788d92a7dd4fba068e81636" + integrity sha512-Qu+VC3EwYLldKa1fCxuuvULvSJOKEgk9pi8dZeCVK7TqBfUNTH4sFkk4joj8afVSfAYgJoSOetjx9QWOJ5mYoQ== + dependencies: + ansi-styles "^3.2.0" + astral-regex "^1.0.0" + is-fullwidth-code-point "^2.0.0" + +snapdragon-node@^2.0.1: + version "2.1.1" + resolved "https://registry.yarnpkg.com/snapdragon-node/-/snapdragon-node-2.1.1.tgz#6c175f86ff14bdb0724563e8f3c1b021a286853b" + integrity sha512-O27l4xaMYt/RSQ5TR3vpWCAB5Kb/czIcqUFOM/C4fYcLnbZUc1PkjTAMjof2pBWaSTwOUd6qUHcFGVGj7aIwnw== + dependencies: + define-property "^1.0.0" + isobject "^3.0.0" + snapdragon-util "^3.0.1" + +snapdragon-util@^3.0.1: + version "3.0.1" + resolved "https://registry.yarnpkg.com/snapdragon-util/-/snapdragon-util-3.0.1.tgz#f956479486f2acd79700693f6f7b805e45ab56e2" + integrity sha512-mbKkMdQKsjX4BAL4bRYTj21edOf8cN7XHdYUJEe+Zn99hVEYcMvKPct1IqNe7+AZPirn8BCDOQBHQZknqmKlZQ== + dependencies: + kind-of "^3.2.0" + +snapdragon@^0.8.1: + version "0.8.2" + resolved "https://registry.yarnpkg.com/snapdragon/-/snapdragon-0.8.2.tgz#64922e7c565b0e14204ba1aa7d6964278d25182d" + integrity sha512-FtyOnWN/wCHTVXOMwvSv26d+ko5vWlIDD6zoUJ7LW8vh+ZBC8QdljveRP+crNrtBwioEUWy/4dMtbBjA4ioNlg== + dependencies: + base "^0.11.1" + debug "^2.2.0" + define-property "^0.2.5" + extend-shallow "^2.0.1" + map-cache "^0.2.2" + source-map "^0.5.6" + source-map-resolve "^0.5.0" + use "^3.1.0" + +source-map-resolve@^0.5.0: + version "0.5.3" + resolved "https://registry.yarnpkg.com/source-map-resolve/-/source-map-resolve-0.5.3.tgz#190866bece7553e1f8f267a2ee82c606b5509a1a" + integrity sha512-Htz+RnsXWk5+P2slx5Jh3Q66vhQj1Cllm0zvnaY98+NFx+Dv2CF/f5O/t8x+KaNdrdIAsruNzoh/KpialbqAnw== + dependencies: + atob "^2.1.2" + decode-uri-component "^0.2.0" + resolve-url "^0.2.1" + source-map-url "^0.4.0" + urix "^0.1.0" + +source-map-support@^0.5.16, source-map-support@^0.5.6: + version "0.5.19" + resolved "https://registry.yarnpkg.com/source-map-support/-/source-map-support-0.5.19.tgz#a98b62f86dcaf4f67399648c085291ab9e8fed61" + integrity sha512-Wonm7zOCIJzBGQdB+thsPar0kYuCIzYvxZwlBa87yi/Mdjv7Tip2cyVbLj5o0cFPN4EVkuTwb3GDDyUx2DGnGw== + dependencies: + buffer-from "^1.0.0" + source-map "^0.6.0" + +source-map-url@^0.4.0: + version "0.4.0" + resolved "https://registry.yarnpkg.com/source-map-url/-/source-map-url-0.4.0.tgz#3e935d7ddd73631b97659956d55128e87b5084a3" + integrity sha1-PpNdfd1zYxuXZZlW1VEo6HtQhKM= + +source-map@^0.5.0, source-map@^0.5.6: + version "0.5.7" + resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.5.7.tgz#8a039d2d1021d22d1ea14c80d8ea468ba2ef3fcc" + integrity sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w= + +source-map@^0.6.0, source-map@^0.6.1, source-map@~0.6.1: + version "0.6.1" + resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.6.1.tgz#74722af32e9614e9c287a8d0bbde48b5e2f1a263" + integrity sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g== + +spdx-correct@^3.0.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/spdx-correct/-/spdx-correct-3.1.0.tgz#fb83e504445268f154b074e218c87c003cd31df4" + integrity sha512-lr2EZCctC2BNR7j7WzJ2FpDznxky1sjfxvvYEyzxNyb6lZXHODmEoJeFu4JupYlkfha1KZpJyoqiJ7pgA1qq8Q== + dependencies: + spdx-expression-parse "^3.0.0" + spdx-license-ids "^3.0.0" + +spdx-exceptions@^2.1.0: + version "2.3.0" + resolved "https://registry.yarnpkg.com/spdx-exceptions/-/spdx-exceptions-2.3.0.tgz#3f28ce1a77a00372683eade4a433183527a2163d" + integrity sha512-/tTrYOC7PPI1nUAgx34hUpqXuyJG+DTHJTnIULG4rDygi4xu/tfgmq1e1cIRwRzwZgo4NLySi+ricLkZkw4i5A== + +spdx-expression-parse@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/spdx-expression-parse/-/spdx-expression-parse-3.0.0.tgz#99e119b7a5da00e05491c9fa338b7904823b41d0" + integrity sha512-Yg6D3XpRD4kkOmTpdgbUiEJFKghJH03fiC1OPll5h/0sO6neh2jqRDVHOQ4o/LMea0tgCkbMgea5ip/e+MkWyg== + dependencies: + spdx-exceptions "^2.1.0" + spdx-license-ids "^3.0.0" + +spdx-license-ids@^3.0.0: + version "3.0.5" + resolved "https://registry.yarnpkg.com/spdx-license-ids/-/spdx-license-ids-3.0.5.tgz#3694b5804567a458d3c8045842a6358632f62654" + integrity sha512-J+FWzZoynJEXGphVIS+XEh3kFSjZX/1i9gFBaWQcB+/tmpe2qUsSBABpcxqxnAxFdiUFEgAX1bjYGQvIZmoz9Q== + +split-ca@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/split-ca/-/split-ca-1.0.1.tgz#6c83aff3692fa61256e0cd197e05e9de157691a6" + integrity sha1-bIOv82kvphJW4M0ZfgXp3hV2kaY= + +split-string@^3.0.1, split-string@^3.0.2: + version "3.1.0" + resolved "https://registry.yarnpkg.com/split-string/-/split-string-3.1.0.tgz#7cb09dda3a86585705c64b39a6466038682e8fe2" + integrity sha512-NzNVhJDYpwceVVii8/Hu6DKfD2G+NrQHlS/V/qgv763EYudVwEcMQNxd2lh+0VrUByXN/oJkl5grOhYWvQUYiw== + dependencies: + extend-shallow "^3.0.0" + +sprintf-js@~1.0.2: + version "1.0.3" + resolved "https://registry.yarnpkg.com/sprintf-js/-/sprintf-js-1.0.3.tgz#04e6926f662895354f3dd015203633b857297e2c" + integrity sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw= + +ssh2-streams@~0.4.10: + version "0.4.10" + resolved "https://registry.yarnpkg.com/ssh2-streams/-/ssh2-streams-0.4.10.tgz#48ef7e8a0e39d8f2921c30521d56dacb31d23a34" + integrity sha512-8pnlMjvnIZJvmTzUIIA5nT4jr2ZWNNVHwyXfMGdRJbug9TpI3kd99ffglgfSWqujVv/0gxwMsDn9j9RVst8yhQ== + dependencies: + asn1 "~0.2.0" + bcrypt-pbkdf "^1.0.2" + streamsearch "~0.1.2" + +ssh2@^0.8.7: + version "0.8.9" + resolved "https://registry.yarnpkg.com/ssh2/-/ssh2-0.8.9.tgz#54da3a6c4ba3daf0d8477a538a481326091815f3" + integrity sha512-GmoNPxWDMkVpMFa9LVVzQZHF6EW3WKmBwL+4/GeILf2hFmix5Isxm7Amamo8o7bHiU0tC+wXsGcUXOxp8ChPaw== + dependencies: + ssh2-streams "~0.4.10" + +sshpk@^1.7.0: + version "1.16.1" + resolved "https://registry.yarnpkg.com/sshpk/-/sshpk-1.16.1.tgz#fb661c0bef29b39db40769ee39fa70093d6f6877" + integrity sha512-HXXqVUq7+pcKeLqqZj6mHFUMvXtOJt1uoUx09pFW6011inTMxqI8BA8PM95myrIyyKwdnzjdFjLiE6KBPVtJIg== + dependencies: + asn1 "~0.2.3" + assert-plus "^1.0.0" + bcrypt-pbkdf "^1.0.0" + dashdash "^1.12.0" + ecc-jsbn "~0.1.1" + getpass "^0.1.1" + jsbn "~0.1.0" + safer-buffer "^2.0.2" + tweetnacl "~0.14.0" + +stack-utils@^1.0.1: + version "1.0.2" + resolved "https://registry.yarnpkg.com/stack-utils/-/stack-utils-1.0.2.tgz#33eba3897788558bebfc2db059dc158ec36cebb8" + integrity sha512-MTX+MeG5U994cazkjd/9KNAapsHnibjMLnfXodlkXw76JEea0UiNzrqidzo1emMwk7w5Qhc9jd4Bn9TBb1MFwA== + +static-extend@^0.1.1: + version "0.1.2" + resolved "https://registry.yarnpkg.com/static-extend/-/static-extend-0.1.2.tgz#60809c39cbff55337226fd5e0b520f341f1fb5c6" + integrity sha1-YICcOcv/VTNyJv1eC1IPNB8ftcY= + dependencies: + define-property "^0.2.5" + object-copy "^0.1.0" + +stealthy-require@^1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/stealthy-require/-/stealthy-require-1.1.1.tgz#35b09875b4ff49f26a777e509b3090a3226bf24b" + integrity sha1-NbCYdbT/SfJqd35QmzCQoyJr8ks= + +streamsearch@~0.1.2: + version "0.1.2" + resolved "https://registry.yarnpkg.com/streamsearch/-/streamsearch-0.1.2.tgz#808b9d0e56fc273d809ba57338e929919a1a9f1a" + integrity sha1-gIudDlb8Jz2Am6VzOOkpkZoanxo= + +string-length@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/string-length/-/string-length-2.0.0.tgz#d40dbb686a3ace960c1cffca562bf2c45f8363ed" + integrity sha1-1A27aGo6zpYMHP/KVivyxF+DY+0= + dependencies: + astral-regex "^1.0.0" + strip-ansi "^4.0.0" + +string-width@^3.0.0, string-width@^3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/string-width/-/string-width-3.1.0.tgz#22767be21b62af1081574306f69ac51b62203961" + integrity sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w== + dependencies: + emoji-regex "^7.0.1" + is-fullwidth-code-point "^2.0.0" + strip-ansi "^5.1.0" + +string-width@^4.1.0: + version "4.2.0" + resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.0.tgz#952182c46cc7b2c313d1596e623992bd163b72b5" + integrity sha512-zUz5JD+tgqtuDjMhwIg5uFVV3dtqZ9yQJlZVfq4I01/K5Paj5UHj7VyrQOJvzawSVlKpObApbfD0Ed6yJc+1eg== + dependencies: + emoji-regex "^8.0.0" + is-fullwidth-code-point "^3.0.0" + strip-ansi "^6.0.0" + +string.prototype.trimend@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/string.prototype.trimend/-/string.prototype.trimend-1.0.1.tgz#85812a6b847ac002270f5808146064c995fb6913" + integrity sha512-LRPxFUaTtpqYsTeNKaFOw3R4bxIzWOnbQ837QfBylo8jIxtcbK/A/sMV7Q+OAV/vWo+7s25pOE10KYSjaSO06g== + dependencies: + define-properties "^1.1.3" + es-abstract "^1.17.5" + +string.prototype.trimleft@^2.1.1: + version "2.1.2" + resolved "https://registry.yarnpkg.com/string.prototype.trimleft/-/string.prototype.trimleft-2.1.2.tgz#4408aa2e5d6ddd0c9a80739b087fbc067c03b3cc" + integrity sha512-gCA0tza1JBvqr3bfAIFJGqfdRTyPae82+KTnm3coDXkZN9wnuW3HjGgN386D7hfv5CHQYCI022/rJPVlqXyHSw== + dependencies: + define-properties "^1.1.3" + es-abstract "^1.17.5" + string.prototype.trimstart "^1.0.0" + +string.prototype.trimright@^2.1.1: + version "2.1.2" + resolved "https://registry.yarnpkg.com/string.prototype.trimright/-/string.prototype.trimright-2.1.2.tgz#c76f1cef30f21bbad8afeb8db1511496cfb0f2a3" + integrity sha512-ZNRQ7sY3KroTaYjRS6EbNiiHrOkjihL9aQE/8gfQ4DtAC/aEBRHFJa44OmoWxGGqXuJlfKkZW4WcXErGr+9ZFg== + dependencies: + define-properties "^1.1.3" + es-abstract "^1.17.5" + string.prototype.trimend "^1.0.0" + +string.prototype.trimstart@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/string.prototype.trimstart/-/string.prototype.trimstart-1.0.1.tgz#14af6d9f34b053f7cfc89b72f8f2ee14b9039a54" + integrity sha512-XxZn+QpvrBI1FOcg6dIpxUPgWCPuNXvMD72aaRaUQv1eD4e/Qy8i/hFTe0BUmD60p/QA6bh1avmuPTfNjqVWRw== + dependencies: + define-properties "^1.1.3" + es-abstract "^1.17.5" + +string_decoder@^1.1.1: + version "1.3.0" + resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-1.3.0.tgz#42f114594a46cf1a8e30b0a84f56c78c3edac21e" + integrity sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA== + dependencies: + safe-buffer "~5.2.0" + +string_decoder@~1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-1.1.1.tgz#9cf1611ba62685d7030ae9e4ba34149c3af03fc8" + integrity sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg== + dependencies: + safe-buffer "~5.1.0" + +strip-ansi@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-4.0.0.tgz#a8479022eb1ac368a871389b635262c505ee368f" + integrity sha1-qEeQIusaw2iocTibY1JixQXuNo8= + dependencies: + ansi-regex "^3.0.0" + +strip-ansi@^5.0.0, strip-ansi@^5.1.0, strip-ansi@^5.2.0: + version "5.2.0" + resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-5.2.0.tgz#8c9a536feb6afc962bdfa5b104a5091c1ad9c0ae" + integrity sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA== + dependencies: + ansi-regex "^4.1.0" + +strip-ansi@^6.0.0: + version "6.0.0" + resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.0.tgz#0b1571dd7669ccd4f3e06e14ef1eed26225ae532" + integrity sha512-AuvKTrTfQNYNIctbR1K/YGTR1756GycPsg7b9bdV9Duqur4gv6aKqHXah67Z8ImS7WEz5QVcOtlfW2rZEugt6w== + dependencies: + ansi-regex "^5.0.0" + +strip-bom@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/strip-bom/-/strip-bom-3.0.0.tgz#2334c18e9c759f7bdd56fdef7e9ae3d588e68ed3" + integrity sha1-IzTBjpx1n3vdVv3vfprj1YjmjtM= + +strip-eof@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/strip-eof/-/strip-eof-1.0.0.tgz#bb43ff5598a6eb05d89b59fcd129c983313606bf" + integrity sha1-u0P/VZim6wXYm1n80SnJgzE2Br8= + +strip-json-comments@^3.0.1: + version "3.1.0" + resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-3.1.0.tgz#7638d31422129ecf4457440009fba03f9f9ac180" + integrity sha512-e6/d0eBu7gHtdCqFt0xJr642LdToM5/cN4Qb9DbHjVx1CP5RyeM+zH7pbecEmDv/lBqb0QH+6Uqq75rxFPkM0w== + +supports-color@^5.3.0: + version "5.5.0" + resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-5.5.0.tgz#e2e69a44ac8772f78a1ec0b35b689df6530efc8f" + integrity sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow== + dependencies: + has-flag "^3.0.0" + +supports-color@^6.1.0: + version "6.1.0" + resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-6.1.0.tgz#0764abc69c63d5ac842dd4867e8d025e880df8f3" + integrity sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ== + dependencies: + has-flag "^3.0.0" + +supports-color@^7.1.0: + version "7.1.0" + resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-7.1.0.tgz#68e32591df73e25ad1c4b49108a2ec507962bfd1" + integrity sha512-oRSIpR8pxT1Wr2FquTNnGet79b3BWljqOuoW/h4oBhxJ/HUbX5nX6JSruTkvXDCFMwDPvsaTTbvMLKZWSy0R5g== + dependencies: + has-flag "^4.0.0" + +symbol-tree@^3.2.2: + version "3.2.4" + resolved "https://registry.yarnpkg.com/symbol-tree/-/symbol-tree-3.2.4.tgz#430637d248ba77e078883951fb9aa0eed7c63fa2" + integrity sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw== + +table@^5.2.3: + version "5.4.6" + resolved "https://registry.yarnpkg.com/table/-/table-5.4.6.tgz#1292d19500ce3f86053b05f0e8e7e4a3bb21079e" + integrity sha512-wmEc8m4fjnob4gt5riFRtTu/6+4rSe12TpAELNSqHMfF3IqnA+CH37USM6/YR3qRZv7e56kAEAtd6nKZaxe0Ug== + dependencies: + ajv "^6.10.2" + lodash "^4.17.14" + slice-ansi "^2.1.0" + string-width "^3.0.0" + +tar-fs@~2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/tar-fs/-/tar-fs-2.0.1.tgz#e44086c1c60d31a4f0cf893b1c4e155dabfae9e2" + integrity sha512-6tzWDMeroL87uF/+lin46k+Q+46rAJ0SyPGz7OW7wTgblI273hsBqk2C1j0/xNadNLKDTUL9BukSjB7cwgmlPA== + dependencies: + chownr "^1.1.1" + mkdirp-classic "^0.5.2" + pump "^3.0.0" + tar-stream "^2.0.0" + +tar-stream@^2.0.0: + version "2.1.2" + resolved "https://registry.yarnpkg.com/tar-stream/-/tar-stream-2.1.2.tgz#6d5ef1a7e5783a95ff70b69b97455a5968dc1325" + integrity sha512-UaF6FoJ32WqALZGOIAApXx+OdxhekNMChu6axLJR85zMMjXKWFGjbIRe+J6P4UnRGg9rAwWvbTT0oI7hD/Un7Q== + dependencies: + bl "^4.0.1" + end-of-stream "^1.4.1" + fs-constants "^1.0.0" + inherits "^2.0.3" + readable-stream "^3.1.1" + +test-exclude@^5.2.3: + version "5.2.3" + resolved "https://registry.yarnpkg.com/test-exclude/-/test-exclude-5.2.3.tgz#c3d3e1e311eb7ee405e092dac10aefd09091eac0" + integrity sha512-M+oxtseCFO3EDtAaGH7iiej3CBkzXqFMbzqYAACdzKui4eZA+pq3tZEwChvOdNfa7xxy8BfbmgJSIr43cC/+2g== + dependencies: + glob "^7.1.3" + minimatch "^3.0.4" + read-pkg-up "^4.0.0" + require-main-filename "^2.0.0" + +text-table@^0.2.0: + version "0.2.0" + resolved "https://registry.yarnpkg.com/text-table/-/text-table-0.2.0.tgz#7f5ee823ae805207c00af2df4a84ec3fcfa570b4" + integrity sha1-f17oI66AUgfACvLfSoTsP8+lcLQ= + +throat@^4.0.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/throat/-/throat-4.1.0.tgz#89037cbc92c56ab18926e6ba4cbb200e15672a6a" + integrity sha1-iQN8vJLFarGJJua6TLsgDhVnKmo= + +through@^2.3.6: + version "2.3.8" + resolved "https://registry.yarnpkg.com/through/-/through-2.3.8.tgz#0dd4c9ffaabc357960b1b724115d7e0e86a2e1f5" + integrity sha1-DdTJ/6q8NXlgsbckEV1+Doai4fU= + +tmp@^0.0.33: + version "0.0.33" + resolved "https://registry.yarnpkg.com/tmp/-/tmp-0.0.33.tgz#6d34335889768d21b2bcda0aa277ced3b1bfadf9" + integrity sha512-jRCJlojKnZ3addtTOjdIqoRuPEKBvNXcGYqzO6zWZX8KfKEpnGY5jfggJQ3EjKuu8D4bJRr0y+cYJFmYbImXGw== + dependencies: + os-tmpdir "~1.0.2" + +tmpl@1.0.x: + version "1.0.4" + resolved "https://registry.yarnpkg.com/tmpl/-/tmpl-1.0.4.tgz#23640dd7b42d00433911140820e5cf440e521dd1" + integrity sha1-I2QN17QtAEM5ERQIIOXPRA5SHdE= + +to-fast-properties@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/to-fast-properties/-/to-fast-properties-2.0.0.tgz#dc5e698cbd079265bc73e0377681a4e4e83f616e" + integrity sha1-3F5pjL0HkmW8c+A3doGk5Og/YW4= + +to-object-path@^0.3.0: + version "0.3.0" + resolved "https://registry.yarnpkg.com/to-object-path/-/to-object-path-0.3.0.tgz#297588b7b0e7e0ac08e04e672f85c1f4999e17af" + integrity sha1-KXWIt7Dn4KwI4E5nL4XB9JmeF68= + dependencies: + kind-of "^3.0.2" + +to-regex-range@^2.1.0: + version "2.1.1" + resolved "https://registry.yarnpkg.com/to-regex-range/-/to-regex-range-2.1.1.tgz#7c80c17b9dfebe599e27367e0d4dd5590141db38" + integrity sha1-fIDBe53+vlmeJzZ+DU3VWQFB2zg= + dependencies: + is-number "^3.0.0" + repeat-string "^1.6.1" + +to-regex@^3.0.1, to-regex@^3.0.2: + version "3.0.2" + resolved "https://registry.yarnpkg.com/to-regex/-/to-regex-3.0.2.tgz#13cfdd9b336552f30b51f33a8ae1b42a7a7599ce" + integrity sha512-FWtleNAtZ/Ki2qtqej2CXTOayOH9bHDQF+Q48VpWyDXjbYxA4Yz8iDB31zXOBUlOHHKidDbqGVrTUvQMPmBGBw== + dependencies: + define-property "^2.0.2" + extend-shallow "^3.0.2" + regex-not "^1.0.2" + safe-regex "^1.1.0" + +tough-cookie@^2.3.3, tough-cookie@^2.3.4, tough-cookie@~2.5.0: + version "2.5.0" + resolved "https://registry.yarnpkg.com/tough-cookie/-/tough-cookie-2.5.0.tgz#cd9fb2a0aa1d5a12b473bd9fb96fa3dcff65ade2" + integrity sha512-nlLsUzgm1kfLXSXfRZMc1KLAugd4hqJHDTvc2hDIwS3mZAfMEuMbc03SujMF+GEcpaX/qboeycw6iO8JwVv2+g== + dependencies: + psl "^1.1.28" + punycode "^2.1.1" + +tr46@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/tr46/-/tr46-1.0.1.tgz#a8b13fd6bfd2489519674ccde55ba3693b706d09" + integrity sha1-qLE/1r/SSJUZZ0zN5VujaTtwbQk= + dependencies: + punycode "^2.1.0" + +tslib@^1.8.1, tslib@^1.9.0: + version "1.11.1" + resolved "https://registry.yarnpkg.com/tslib/-/tslib-1.11.1.tgz#eb15d128827fbee2841549e171f45ed338ac7e35" + integrity sha512-aZW88SY8kQbU7gpV19lN24LtXh/yD4ZZg6qieAJDDg+YBsJcSmLGK9QpnUjAKVG/xefmvJGd1WUmfpT/g6AJGA== + +tsutils@^3.17.1: + version "3.17.1" + resolved "https://registry.yarnpkg.com/tsutils/-/tsutils-3.17.1.tgz#ed719917f11ca0dee586272b2ac49e015a2dd759" + integrity sha512-kzeQ5B8H3w60nFY2g8cJIuH7JDpsALXySGtwGJ0p2LSjLgay3NdIpqq5SoOBe46bKDW2iq25irHCr8wjomUS2g== + dependencies: + tslib "^1.8.1" + +tunnel-agent@^0.6.0: + version "0.6.0" + resolved "https://registry.yarnpkg.com/tunnel-agent/-/tunnel-agent-0.6.0.tgz#27a5dea06b36b04a0a9966774b290868f0fc40fd" + integrity sha1-J6XeoGs2sEoKmWZ3SykIaPD8QP0= + dependencies: + safe-buffer "^5.0.1" + +tweetnacl@^0.14.3, tweetnacl@~0.14.0: + version "0.14.5" + resolved "https://registry.yarnpkg.com/tweetnacl/-/tweetnacl-0.14.5.tgz#5ae68177f192d4456269d108afa93ff8743f4f64" + integrity sha1-WuaBd/GS1EViadEIr6k/+HQ/T2Q= + +type-check@~0.3.2: + version "0.3.2" + resolved "https://registry.yarnpkg.com/type-check/-/type-check-0.3.2.tgz#5884cab512cf1d355e3fb784f30804b2b520db72" + integrity sha1-WITKtRLPHTVeP7eE8wgEsrUg23I= + dependencies: + prelude-ls "~1.1.2" + +type-fest@^0.11.0: + version "0.11.0" + resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.11.0.tgz#97abf0872310fed88a5c466b25681576145e33f1" + integrity sha512-OdjXJxnCN1AvyLSzeKIgXTXxV+99ZuXl3Hpo9XpJAv9MBcHrrJOQ5kV7ypXOuQie+AmWG25hLbiKdwYTifzcfQ== + +type-fest@^0.8.1: + version "0.8.1" + resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.8.1.tgz#09e249ebde851d3b1e48d27c105444667f17b83d" + integrity sha512-4dbzIzqvjtgiM5rw1k5rEHtBANKmdudhGyBEajN01fEyhaAIhsoKNy6y7+IN93IfpFtwY9iqi7kD+xwKhQsNJA== + +typedarray@^0.0.6: + version "0.0.6" + resolved "https://registry.yarnpkg.com/typedarray/-/typedarray-0.0.6.tgz#867ac74e3864187b1d3d47d996a78ec5c8830777" + integrity sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c= + +unicode-canonical-property-names-ecmascript@^1.0.4: + version "1.0.4" + resolved "https://registry.yarnpkg.com/unicode-canonical-property-names-ecmascript/-/unicode-canonical-property-names-ecmascript-1.0.4.tgz#2619800c4c825800efdd8343af7dd9933cbe2818" + integrity sha512-jDrNnXWHd4oHiTZnx/ZG7gtUTVp+gCcTTKr8L0HjlwphROEW3+Him+IpvC+xcJEFegapiMZyZe02CyuOnRmbnQ== + +unicode-match-property-ecmascript@^1.0.4: + version "1.0.4" + resolved "https://registry.yarnpkg.com/unicode-match-property-ecmascript/-/unicode-match-property-ecmascript-1.0.4.tgz#8ed2a32569961bce9227d09cd3ffbb8fed5f020c" + integrity sha512-L4Qoh15vTfntsn4P1zqnHulG0LdXgjSO035fEpdtp6YxXhMT51Q6vgM5lYdG/5X3MjS+k/Y9Xw4SFCY9IkR0rg== + dependencies: + unicode-canonical-property-names-ecmascript "^1.0.4" + unicode-property-aliases-ecmascript "^1.0.4" + +unicode-match-property-value-ecmascript@^1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/unicode-match-property-value-ecmascript/-/unicode-match-property-value-ecmascript-1.2.0.tgz#0d91f600eeeb3096aa962b1d6fc88876e64ea531" + integrity sha512-wjuQHGQVofmSJv1uVISKLE5zO2rNGzM/KCYZch/QQvez7C1hUhBIuZ701fYXExuufJFMPhv2SyL8CyoIfMLbIQ== + +unicode-property-aliases-ecmascript@^1.0.4: + version "1.1.0" + resolved "https://registry.yarnpkg.com/unicode-property-aliases-ecmascript/-/unicode-property-aliases-ecmascript-1.1.0.tgz#dd57a99f6207bedff4628abefb94c50db941c8f4" + integrity sha512-PqSoPh/pWetQ2phoj5RLiaqIk4kCNwoV3CI+LfGmWLKI3rE3kl1h59XpX2BjgDrmbxD9ARtQobPGU1SguCYuQg== + +union-value@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/union-value/-/union-value-1.0.1.tgz#0b6fe7b835aecda61c6ea4d4f02c14221e109847" + integrity sha512-tJfXmxMeWYnczCVs7XAEvIV7ieppALdyepWMkHkwciRpZraG/xwT+s2JN8+pr1+8jCRf80FFzvr+MpQeeoF4Xg== + dependencies: + arr-union "^3.1.0" + get-value "^2.0.6" + is-extendable "^0.1.1" + set-value "^2.0.1" + +unset-value@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/unset-value/-/unset-value-1.0.0.tgz#8376873f7d2335179ffb1e6fc3a8ed0dfc8ab559" + integrity sha1-g3aHP30jNRef+x5vw6jtDfyKtVk= + dependencies: + has-value "^0.3.1" + isobject "^3.0.0" + +upath@^1.1.1: + version "1.2.0" + resolved "https://registry.yarnpkg.com/upath/-/upath-1.2.0.tgz#8f66dbcd55a883acdae4408af8b035a5044c1894" + integrity sha512-aZwGpamFO61g3OlfT7OQCHqhGnW43ieH9WZeP7QxN/G/jS4jfqUkZxoryvJgVPEcrl5NL/ggHsSmLMHuH64Lhg== + +uri-js@^4.2.2: + version "4.2.2" + resolved "https://registry.yarnpkg.com/uri-js/-/uri-js-4.2.2.tgz#94c540e1ff772956e2299507c010aea6c8838eb0" + integrity sha512-KY9Frmirql91X2Qgjry0Wd4Y+YTdrdZheS8TFwvkbLWf/G5KNJDCh6pKL5OZctEW4+0Baa5idK2ZQuELRwPznQ== + dependencies: + punycode "^2.1.0" + +urix@^0.1.0: + version "0.1.0" + resolved "https://registry.yarnpkg.com/urix/-/urix-0.1.0.tgz#da937f7a62e21fec1fd18d49b35c2935067a6c72" + integrity sha1-2pN/emLiH+wf0Y1Js1wpNQZ6bHI= + +use@^3.1.0: + version "3.1.1" + resolved "https://registry.yarnpkg.com/use/-/use-3.1.1.tgz#d50c8cac79a19fbc20f2911f56eb973f4e10070f" + integrity sha512-cwESVXlO3url9YWlFW/TA9cshCEhtu7IKJ/p5soJ/gGpj7vbvFrAY/eIioQ6Dw23KjZhYgiIo8HOs1nQ2vr/oQ== + +util-deprecate@^1.0.1, util-deprecate@~1.0.1: + version "1.0.2" + resolved "https://registry.yarnpkg.com/util-deprecate/-/util-deprecate-1.0.2.tgz#450d4dc9fa70de732762fbd2d4a28981419a0ccf" + integrity sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8= + +util.promisify@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/util.promisify/-/util.promisify-1.0.1.tgz#6baf7774b80eeb0f7520d8b81d07982a59abbaee" + integrity sha512-g9JpC/3He3bm38zsLupWryXHoEcS22YHthuPQSJdMy6KNrzIRzWqcsHzD/WUnqe45whVou4VIsPew37DoXWNrA== + dependencies: + define-properties "^1.1.3" + es-abstract "^1.17.2" + has-symbols "^1.0.1" + object.getownpropertydescriptors "^2.1.0" + +util@^0.10.3: + version "0.10.4" + resolved "https://registry.yarnpkg.com/util/-/util-0.10.4.tgz#3aa0125bfe668a4672de58857d3ace27ecb76901" + integrity sha512-0Pm9hTQ3se5ll1XihRic3FDIku70C+iHUdT/W926rSgHV5QgXsYbKZN8MSC3tJtSkhuROzvsQjAaFENRXr+19A== + dependencies: + inherits "2.0.3" + +uuid@^3.3.2: + version "3.4.0" + resolved "https://registry.yarnpkg.com/uuid/-/uuid-3.4.0.tgz#b23e4358afa8a202fe7a100af1f5f883f02007ee" + integrity sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A== + +v8-compile-cache@^2.0.3: + version "2.1.0" + resolved "https://registry.yarnpkg.com/v8-compile-cache/-/v8-compile-cache-2.1.0.tgz#e14de37b31a6d194f5690d67efc4e7f6fc6ab30e" + integrity sha512-usZBT3PW+LOjM25wbqIlZwPeJV+3OSz3M1k1Ws8snlW39dZyYL9lOGC5FgPVHfk0jKmjiDV8Z0mIbVQPiwFs7g== + +v8flags@^3.1.1: + version "3.1.3" + resolved "https://registry.yarnpkg.com/v8flags/-/v8flags-3.1.3.tgz#fc9dc23521ca20c5433f81cc4eb9b3033bb105d8" + integrity sha512-amh9CCg3ZxkzQ48Mhcb8iX7xpAfYJgePHxWMQCBWECpOSqJUXgY26ncA61UTV0BkPqfhcy6mzwCIoP4ygxpW8w== + dependencies: + homedir-polyfill "^1.0.1" + +validate-npm-package-license@^3.0.1: + version "3.0.4" + resolved "https://registry.yarnpkg.com/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz#fc91f6b9c7ba15c857f4cb2c5defeec39d4f410a" + integrity sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew== + dependencies: + spdx-correct "^3.0.0" + spdx-expression-parse "^3.0.0" + +verror@1.10.0: + version "1.10.0" + resolved "https://registry.yarnpkg.com/verror/-/verror-1.10.0.tgz#3a105ca17053af55d6e270c1f8288682e18da400" + integrity sha1-OhBcoXBTr1XW4nDB+CiGguGNpAA= + dependencies: + assert-plus "^1.0.0" + core-util-is "1.0.2" + extsprintf "^1.2.0" + +w3c-hr-time@^1.0.1: + version "1.0.2" + resolved "https://registry.yarnpkg.com/w3c-hr-time/-/w3c-hr-time-1.0.2.tgz#0a89cdf5cc15822df9c360543676963e0cc308cd" + integrity sha512-z8P5DvDNjKDoFIHK7q8r8lackT6l+jo/Ye3HOle7l9nICP9lf1Ci25fy9vHd0JOWewkIFzXIEig3TdKT7JQ5fQ== + dependencies: + browser-process-hrtime "^1.0.0" + +walker@^1.0.7, walker@~1.0.5: + version "1.0.7" + resolved "https://registry.yarnpkg.com/walker/-/walker-1.0.7.tgz#2f7f9b8fd10d677262b18a884e28d19618e028fb" + integrity sha1-L3+bj9ENZ3JisYqITijRlhjgKPs= + dependencies: + makeerror "1.0.x" + +webidl-conversions@^4.0.2: + version "4.0.2" + resolved "https://registry.yarnpkg.com/webidl-conversions/-/webidl-conversions-4.0.2.tgz#a855980b1f0b6b359ba1d5d9fb39ae941faa63ad" + integrity sha512-YQ+BmxuTgd6UXZW3+ICGfyqRyHXVlD5GtQr5+qjiNW7bF0cqrzX500HVXPBOvgXb5YnzDd+h0zqyv61KUD7+Sg== + +whatwg-encoding@^1.0.1, whatwg-encoding@^1.0.3: + version "1.0.5" + resolved "https://registry.yarnpkg.com/whatwg-encoding/-/whatwg-encoding-1.0.5.tgz#5abacf777c32166a51d085d6b4f3e7d27113ddb0" + integrity sha512-b5lim54JOPN9HtzvK9HFXvBma/rnfFeqsic0hSpjtDbVxR3dJKLc+KB4V6GgiGOvl7CY/KNh8rxSo9DKQrnUEw== + dependencies: + iconv-lite "0.4.24" + +whatwg-mimetype@^2.1.0, whatwg-mimetype@^2.2.0: + version "2.3.0" + resolved "https://registry.yarnpkg.com/whatwg-mimetype/-/whatwg-mimetype-2.3.0.tgz#3d4b1e0312d2079879f826aff18dbeeca5960fbf" + integrity sha512-M4yMwr6mAnQz76TbJm914+gPpB/nCwvZbJU28cUD6dR004SAxDLOOSUaB1JDRqLtaOV/vi0IC5lEAGFgrjGv/g== + +whatwg-url@^6.4.1: + version "6.5.0" + resolved "https://registry.yarnpkg.com/whatwg-url/-/whatwg-url-6.5.0.tgz#f2df02bff176fd65070df74ad5ccbb5a199965a8" + integrity sha512-rhRZRqx/TLJQWUpQ6bmrt2UV4f0HCQ463yQuONJqC6fO2VoEb1pTYddbe59SkYq87aoM5A3bdhMZiUiVws+fzQ== + dependencies: + lodash.sortby "^4.7.0" + tr46 "^1.0.1" + webidl-conversions "^4.0.2" + +whatwg-url@^7.0.0: + version "7.1.0" + resolved "https://registry.yarnpkg.com/whatwg-url/-/whatwg-url-7.1.0.tgz#c2c492f1eca612988efd3d2266be1b9fc6170d06" + integrity sha512-WUu7Rg1DroM7oQvGWfOiAK21n74Gg+T4elXEQYkOhtyLeWiJFoOGLXPKI/9gzIie9CtwVLm8wtw6YJdKyxSjeg== + dependencies: + lodash.sortby "^4.7.0" + tr46 "^1.0.1" + webidl-conversions "^4.0.2" + +which-module@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/which-module/-/which-module-2.0.0.tgz#d9ef07dce77b9902b8a3a8fa4b31c3e3f7e6e87a" + integrity sha1-2e8H3Od7mQK4o6j6SzHD4/fm6Ho= + +which@^1.2.9, which@^1.3.0: + version "1.3.1" + resolved "https://registry.yarnpkg.com/which/-/which-1.3.1.tgz#a45043d54f5805316da8d62f9f50918d3da70b0a" + integrity sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ== + dependencies: + isexe "^2.0.0" + +word-wrap@~1.2.3: + version "1.2.3" + resolved "https://registry.yarnpkg.com/word-wrap/-/word-wrap-1.2.3.tgz#610636f6b1f703891bd34771ccb17fb93b47079c" + integrity sha512-Hz/mrNwitNRh/HUAtM/VT/5VH+ygD6DV7mYKZAtHOrbs8U7lvPS6xf7EJKMF0uW1KJCl0H701g3ZGus+muE5vQ== + +wrap-ansi@^5.1.0: + version "5.1.0" + resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-5.1.0.tgz#1fd1f67235d5b6d0fee781056001bfb694c03b09" + integrity sha512-QC1/iN/2/RPVJ5jYK8BGttj5z83LmSKmvbvrXPNCLZSEb32KKVDJDl/MOt2N01qU2H/FkzEa9PKto1BqDjtd7Q== + dependencies: + ansi-styles "^3.2.0" + string-width "^3.0.0" + strip-ansi "^5.0.0" + +wrappy@1: + version "1.0.2" + resolved "https://registry.yarnpkg.com/wrappy/-/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f" + integrity sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8= + +write-file-atomic@2.4.1: + version "2.4.1" + resolved "https://registry.yarnpkg.com/write-file-atomic/-/write-file-atomic-2.4.1.tgz#d0b05463c188ae804396fd5ab2a370062af87529" + integrity sha512-TGHFeZEZMnv+gBFRfjAcxL5bPHrsGKtnb4qsFAws7/vlh+QfwAaySIw4AXP9ZskTTh5GWu3FLuJhsWVdiJPGvg== + dependencies: + graceful-fs "^4.1.11" + imurmurhash "^0.1.4" + signal-exit "^3.0.2" + +write@1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/write/-/write-1.0.3.tgz#0800e14523b923a387e415123c865616aae0f5c3" + integrity sha512-/lg70HAjtkUgWPVZhZcm+T4hkL8Zbtp1nFNOn3lRrxnlv50SRBv7cR7RqR+GMsd3hUXy9hWBo4CHTbFTcOYwig== + dependencies: + mkdirp "^0.5.1" + +ws@6: + version "6.2.1" + resolved "https://registry.yarnpkg.com/ws/-/ws-6.2.1.tgz#442fdf0a47ed64f59b6a5d8ff130f4748ed524fb" + integrity sha512-GIyAXC2cB7LjvpgMt9EKS2ldqr0MTrORaleiOno6TweZ6r3TKtoFQWay/2PceJ3RuBasOHzXNn5Lrw1X0bEjqA== + dependencies: + async-limiter "~1.0.0" + +ws@^5.2.0: + version "5.2.2" + resolved "https://registry.yarnpkg.com/ws/-/ws-5.2.2.tgz#dffef14866b8e8dc9133582514d1befaf96e980f" + integrity sha512-jaHFD6PFv6UgoIVda6qZllptQsMlDEJkTQcybzzXDYM1XO9Y8em691FGMPmM46WGyLU4z9KMgQN+qrux/nhlHA== + dependencies: + async-limiter "~1.0.0" + +xml-name-validator@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/xml-name-validator/-/xml-name-validator-3.0.0.tgz#6ae73e06de4d8c6e47f9fb181f78d648ad457c6a" + integrity sha512-A5CUptxDsvxKJEU3yO6DuWBSJz/qizqzJKOMIfUJHETbBw/sFaDxgd6fxm1ewUaM0jZ444Fc5vC5ROYurg/4Pw== + +y18n@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/y18n/-/y18n-4.0.0.tgz#95ef94f85ecc81d007c264e190a120f0a3c8566b" + integrity sha512-r9S/ZyXu/Xu9q1tYlpsLIsa3EeLXXk0VwlxqTcFRfg9EhMW+17kbt9G0NrgCmhGb5vT2hyhJZLfDGx+7+5Uj/w== + +yargs-parser@^13.1.2: + version "13.1.2" + resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-13.1.2.tgz#130f09702ebaeef2650d54ce6e3e5706f7a4fb38" + integrity sha512-3lbsNRf/j+A4QuSZfDRA7HRSfWrzO0YjqTJd5kjAq37Zep1CEgaYmrH9Q3GwPiB9cHyd1Y1UwggGhJGoxipbzg== + dependencies: + camelcase "^5.0.0" + decamelize "^1.2.0" + +yargs@^13.3.0: + version "13.3.2" + resolved "https://registry.yarnpkg.com/yargs/-/yargs-13.3.2.tgz#ad7ffefec1aa59565ac915f82dccb38a9c31a2dd" + integrity sha512-AX3Zw5iPruN5ie6xGRIDgqkT+ZhnRlZMLMHAs8tg7nRruy2Nb+i5o9bwghAogtM08q1dpr2LVoS8KSTMYpWXUw== + dependencies: + cliui "^5.0.0" + find-up "^3.0.0" + get-caller-file "^2.0.1" + require-directory "^2.1.1" + require-main-filename "^2.0.0" + set-blocking "^2.0.0" + string-width "^3.0.0" + which-module "^2.0.0" + y18n "^4.0.0" + yargs-parser "^13.1.2"